Skip to content
This repository has been archived by the owner on May 16, 2022. It is now read-only.

Commit

Permalink
Handle timezones
Browse files Browse the repository at this point in the history
Signed-off-by: Igor Shishkin <[email protected]>
  • Loading branch information
teran committed Oct 10, 2019
1 parent abd7259 commit 943473c
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 32 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ Usage: tagger [OPTIONS] file.csv

Options:
-filename-pattern string
filename pattern for generate exiftool command. %d means frame number on the film. (default "FILM_%05d.dng")
filename pattern for generate exiftool command. %d means frame number on the film (default "FILM_%05d.dng")
-help
display help message
display help message
-timezone string
location or timezone name used while setting time on EOS 1V, will be used for proper scans timestamping (example: 'Europe/Moscow') (default "UTC")
```
NOTICES
Expand All @@ -27,9 +29,9 @@ NOTICES
* It's in **deep alpha** state
* It's **NOT** going to make any changes to real data: just prints exiftool commands to STDOUT
* It relies on the data provided by ES-E1 software in CSV format(in EOS 1V Memory just export via `File` -> `Export` -> `CSV`)
* It does **NOT** perform timezone detection since ES-E1 exports timestamps without timezone mark.
* **Always** carefully review exiftool commands *before* applying them.
* It sets ISO for each frame from the film settings. So if you have set ISO for particular frame to the another value it will still use the one from the film properties.
* It allows you to specify timezone set on EOS 1V to properly timestamp scans so please pay attention to `-timezone` flag **which defaults to UTC timezone**
Licence
-------
Expand Down
6 changes: 4 additions & 2 deletions cmd/tagger/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ const (
)

var (
filenamePattern string = "FILM_%05d.dng"
displayHelp bool = false
filenamePattern string = "FILM_%05d.dng"
timezone string = "UTC"
)

func parseFlags() {
Expand All @@ -22,8 +23,9 @@ func parseFlags() {
flag.PrintDefaults()
}

flag.StringVar(&filenamePattern, "filename-pattern", filenamePattern, "filename pattern for generate exiftool command. %d means frame number on the film.")
flag.BoolVar(&displayHelp, "help", displayHelp, "display help message")
flag.StringVar(&filenamePattern, "filename-pattern", filenamePattern, "filename pattern for generate exiftool command. %d means frame number on the film")
flag.StringVar(&timezone, "timezone", timezone, "location or timezone name used while setting time on EOS 1V, will be used for proper scans timestamping (example: 'Europe/Moscow')")

flag.Parse()

Expand Down
7 changes: 6 additions & 1 deletion cmd/tagger/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import (
func main() {
parseFlags()

t, err := tagger.NewCSVParser(flag.Arg(0))
tz, err := tagger.LocationByTimeZone(timezone)
if err != nil {
log.Fatalf("error looking up timezone: %s", err)
}

t, err := tagger.NewCSVParser(flag.Arg(0), tz)
if err != nil {
log.Fatalf("error initializing CSV parser: %s", err)
}
Expand Down
29 changes: 12 additions & 17 deletions csv_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
// CSVParser type
type CSVParser struct {
rc io.ReadCloser
tz *time.Location
}

var (
Expand All @@ -23,24 +24,18 @@ var (
)

// NewCSVParser creates new CSVParser object
func NewCSVParser(fn string) (*CSVParser, error) {
func NewCSVParser(fn string, tz *time.Location) (*CSVParser, error) {
fp, err := os.Open(fn)
if err != nil {
return nil, err
}

return &CSVParser{
rc: fp,
tz: tz,
}, nil
}

// NewCSVParserFromReadCloser creats new CSVParser object from ReadCloser
func NewCSVParserFromReadCloser(fp io.ReadCloser) *CSVParser {
return &CSVParser{
rc: fp,
}
}

// Close ...
func (p *CSVParser) Close() error {
return p.rc.Close()
Expand All @@ -61,7 +56,7 @@ func (p *CSVParser) Parse() (Film, error) {
}
remarks = strings.TrimSpace(strings.Split(remarks, ",")[2])

film, err := parseFilmData(filmDataStr)
film, err := parseFilmData(filmDataStr, p.tz)
if err != nil {
return film, err
}
Expand All @@ -80,7 +75,7 @@ func (p *CSVParser) Parse() (Film, error) {
continue
}

frame, err := parseFrameData(frameStr)
frame, err := parseFrameData(frameStr, p.tz)
if err != nil {
if err == ErrEmptyFrame {
continue
Expand All @@ -95,11 +90,11 @@ func (p *CSVParser) Parse() (Film, error) {
return film, err
}

func parseFilmData(s string) (Film, error) {
func parseFilmData(s string, tz *time.Location) (Film, error) {
ss := strings.Split(s, ",")

ts := fmt.Sprintf("%sT%s", ss[6], ss[7])
tt, err := time.Parse(TimestampFormat, ts)
tt, err := time.ParseInLocation(TimestampFormat, ts, tz)
if err != nil {
return Film{}, err
}
Expand All @@ -125,7 +120,7 @@ func parseFilmData(s string) (Film, error) {
return f, nil
}

func parseFrameData(s string) (Frame, error) {
func parseFrameData(s string, tz *time.Location) (Frame, error) {
ss := strings.Split(s, ",")
if len(ss) != 21 {
return Frame{}, fmt.Errorf("wrong amount of columns for frame: %d: `%s`", len(ss), s)
Expand Down Expand Up @@ -209,9 +204,9 @@ func parseFrameData(s string) (Frame, error) {
FilmAdvanceMode: ss[12],
AFMode: ss[13],
BulbExposureTime: ss[14],
Timestamp: maybeParseTimestamp(ss[15], ss[16]),
Timestamp: maybeParseTimestamp(ss[15], ss[16], tz),
MultipleExposure: ss[17],
BatteryLoadedDate: maybeParseTimestamp(ss[18], ss[19]),
BatteryLoadedDate: maybeParseTimestamp(ss[18], ss[19], tz),
Remarks: ss[20],
}
return f, nil
Expand All @@ -226,12 +221,12 @@ func isEmptySliceOfStrings(ss []string) bool {
return true
}

func maybeParseTimestamp(d, t string) time.Time {
func maybeParseTimestamp(d, t string, tz *time.Location) time.Time {
if d == "" || t == "" {
return time.Time{}
}

ts, err := time.Parse(TimestampFormat, fmt.Sprintf("%vT%v", d, t))
ts, err := time.ParseInLocation(TimestampFormat, fmt.Sprintf("%vT%v", d, t), tz)
if err != nil {
log.Printf("error parsing timestamp: `%sT%s`: %s", d, t, err)
return time.Time{}
Expand Down
26 changes: 17 additions & 9 deletions csv_parser_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tagger

import (
"strings"
"testing"
"time"

Expand All @@ -10,7 +11,10 @@ import (
func TestCSVParser(t *testing.T) {
r := require.New(t)

p, err := NewCSVParser("testdata/sample.CSV")
tz, err := LocationByTimeZone("CET")
r.NoError(err)

p, err := NewCSVParser("testdata/sample.CSV", tz)
r.NoError(err)
r.NotNil(p)

Expand All @@ -24,7 +28,7 @@ func TestCSVParser(t *testing.T) {
r.Equal(Film{
ID: "03-758",
Title: "Sample",
FilmLoadedTimestamp: mustParseTimestamp("09/01/2010T14:00:00"),
FilmLoadedTimestamp: mustParseTimestamp(t, "09/01/2010T14:00:00", tz),
FrameCount: 36,
ISO: 200,
Frames: []Frame{
Expand All @@ -41,7 +45,7 @@ func TestCSVParser(t *testing.T) {
ShootingMode: "Program AE",
FilmAdvanceMode: "Single-frame",
AFMode: "One-Shot AF",
Timestamp: time.Date(2010, 11, 9, 18, 31, 26, 0, time.UTC),
Timestamp: mustParseTimestamp(t, "11/09/2010T18:31:26", tz),
MultipleExposure: "OFF",
BatteryLoadedDate: time.Time{},
},
Expand All @@ -58,7 +62,7 @@ func TestCSVParser(t *testing.T) {
ShootingMode: "Program AE",
FilmAdvanceMode: "Single-frame",
AFMode: "One-Shot AF",
Timestamp: time.Date(2010, 12, 9, 18, 32, 55, 0, time.UTC),
Timestamp: mustParseTimestamp(t, "12/09/2010T18:32:55", tz),
MultipleExposure: "OFF",
BatteryLoadedDate: time.Time{},
ExposureCompensation: -5,
Expand All @@ -68,11 +72,15 @@ func TestCSVParser(t *testing.T) {
}, film)
}

func mustParseTimestamp(ts string) time.Time {
tt, err := time.Parse(TimestampFormat, ts)
if err != nil {
panic(err)
}
func mustParseTimestamp(t *testing.T, ts string, tz *time.Location) time.Time {
r := require.New(t)

tts := strings.Split(ts, "T")
r.Len(tts, 2)

tt := maybeParseTimestamp(tts[0], tts[1], tz)
r.NotNil(tt)
r.False(tt.IsZero())

return tt
}
8 changes: 8 additions & 0 deletions timezone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package tagger

import "time"

// LocationByTimeZone returns time.Location object by timezone name
func LocationByTimeZone(z string) (*time.Location, error) {
return time.LoadLocation(z)
}

0 comments on commit 943473c

Please sign in to comment.