parent
eaecedf4bb
commit
680686185f
9 changed files with 355 additions and 97 deletions
|
@ -114,7 +114,7 @@ func (m *Folder) SetValuesFromPath() {
|
|||
}
|
||||
|
||||
if len(m.Path) >= 6 {
|
||||
if date := txt.Time(m.Path); !date.IsZero() {
|
||||
if date := txt.DateFromFilePath(m.Path); !date.IsZero() {
|
||||
if txt.IsUInt(s) || txt.IsTime(s) {
|
||||
if date.Day() > 1 {
|
||||
m.FolderTitle = date.Format("January 2, 2006")
|
||||
|
|
|
@ -11,18 +11,20 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/dsoprea/go-exif/v3"
|
||||
"gopkg.in/photoprism/go-tz.v2/tz"
|
||||
|
||||
exifcommon "github.com/dsoprea/go-exif/v3/common"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"gopkg.in/photoprism/go-tz.v2/tz"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
var exifIfdMapping *exifcommon.IfdMapping
|
||||
var exifTagIndex = exif.NewTagIndex()
|
||||
var exifMutex = sync.Mutex{}
|
||||
|
||||
const DateTimeZero = "0000:00:00 00:00:00"
|
||||
var exifDateFields = []string{"DateTimeOriginal", "DateTimeDigitized", "CreateDate", "DateTime"}
|
||||
|
||||
func init() {
|
||||
exifIfdMapping = exifcommon.NewIfdMapping()
|
||||
|
@ -32,11 +34,6 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
// ValidDateTime returns true if a date string looks valid and is not zero.
|
||||
func ValidDateTime(s string) bool {
|
||||
return len(s) == len(DateTimeZero) && s != DateTimeZero
|
||||
}
|
||||
|
||||
// Exif parses an image file for Exif meta data and returns as Data struct.
|
||||
func Exif(fileName string, fileType fs.FileFormat) (data Data, err error) {
|
||||
err = data.Exif(fileName, fileType)
|
||||
|
@ -236,36 +233,24 @@ func (data *Data) Exif(fileName string, fileType fs.FileFormat) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
var takenAt string
|
||||
takenAt := time.Time{}
|
||||
|
||||
if value, ok := tags["DateTimeOriginal"]; ok && ValidDateTime(value) {
|
||||
takenAt = value
|
||||
} else if value, ok := tags["DateTimeDigitized"]; ok && ValidDateTime(value) {
|
||||
takenAt = value
|
||||
} else if value, ok := tags["CreateDate"]; ok && ValidDateTime(value) {
|
||||
takenAt = value
|
||||
} else if value, ok := tags["DateTime"]; ok && ValidDateTime(value) {
|
||||
takenAt = value
|
||||
for _, name := range exifDateFields {
|
||||
if dateTime := txt.DateTime(tags[name], data.TimeZone); !dateTime.IsZero() {
|
||||
takenAt = dateTime
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ValidDateTime(takenAt) {
|
||||
takenAt = strings.ReplaceAll(takenAt, "/", ":")
|
||||
takenAt = strings.ReplaceAll(takenAt, "-", ":")
|
||||
|
||||
if taken, err := time.Parse("2006:01:02 15:04:05", takenAt); err == nil {
|
||||
data.TakenAtLocal = taken.Round(time.Second)
|
||||
data.TakenAt = data.TakenAtLocal
|
||||
|
||||
if loc, err := time.LoadLocation(data.TimeZone); err != nil {
|
||||
log.Warnf("metadata: unknown time zone %s in %s (exif)", data.TimeZone, logName)
|
||||
} else if tl, err := time.ParseInLocation("2006:01:02 15:04:05", takenAt, loc); err == nil {
|
||||
data.TakenAt = tl.Round(time.Second).UTC()
|
||||
} else {
|
||||
log.Errorf("metadata: %s in %s (exif time)", err.Error(), logName) // this should never happen
|
||||
}
|
||||
// Valid time found in Exif metadata?
|
||||
if !takenAt.IsZero() {
|
||||
if takenAtLocal, err := time.ParseInLocation("2006-01-02T15:04:05", takenAt.Format("2006-01-02T15:04:05"), time.UTC); err == nil {
|
||||
data.TakenAtLocal = takenAtLocal
|
||||
} else {
|
||||
log.Warnf("metadata: invalid time %s in %s (exif)", takenAt, logName)
|
||||
data.TakenAtLocal = takenAt
|
||||
}
|
||||
|
||||
data.TakenAt = takenAt.UTC()
|
||||
}
|
||||
|
||||
if value, ok := tags["Flash"]; ok {
|
||||
|
|
|
@ -79,13 +79,8 @@ func (data *Data) Exiftool(jsonData []byte, originalName string) (err error) {
|
|||
continue
|
||||
}
|
||||
|
||||
s := strings.TrimSpace(jsonValue.String())
|
||||
s = strings.ReplaceAll(s, "/", ":")
|
||||
|
||||
if tv, err := time.Parse("2006:01:02 15:04:05", strings.ReplaceAll(s, "-", ":")); err == nil {
|
||||
fieldValue.Set(reflect.ValueOf(tv.Round(time.Second).UTC()))
|
||||
} else if tv, err := time.Parse("2006:01:02 15:04:05Z07:00", s); err == nil {
|
||||
fieldValue.Set(reflect.ValueOf(tv.Round(time.Second)))
|
||||
if dateTime := txt.DateTime(jsonValue.String(), ""); !dateTime.IsZero() {
|
||||
fieldValue.Set(reflect.ValueOf(dateTime))
|
||||
}
|
||||
case time.Duration:
|
||||
if !fieldValue.IsZero() {
|
||||
|
|
|
@ -562,7 +562,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName, photoUID
|
|||
// Set taken date based on file mod time or name if other metadata is missing.
|
||||
if m.IsMedia() && entity.SrcPriority[photo.TakenSrc] <= entity.SrcPriority[entity.SrcName] {
|
||||
// Try to extract time from original file name first.
|
||||
if taken := txt.Time(photo.OriginalName); !taken.IsZero() {
|
||||
if taken := txt.DateFromFilePath(photo.OriginalName); !taken.IsZero() {
|
||||
photo.SetTakenAt(taken, taken, "", entity.SrcName)
|
||||
} else if taken, takenSrc := m.TakenAt(); takenSrc == entity.SrcName {
|
||||
photo.SetTakenAt(taken, taken, "", entity.SrcName)
|
||||
|
|
|
@ -128,7 +128,7 @@ func (m *MediaFile) TakenAt() (time.Time, string) {
|
|||
return m.takenAt, m.takenAtSrc
|
||||
}
|
||||
|
||||
if nameTime := txt.Time(m.fileName); !nameTime.IsZero() {
|
||||
if nameTime := txt.DateFromFilePath(m.fileName); !nameTime.IsZero() {
|
||||
m.takenAt = nameTime
|
||||
m.takenAtSrc = entity.SrcName
|
||||
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package txt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Go regular expression tester: https://regoio.herokuapp.com/
|
||||
|
||||
var DateRegexp = regexp.MustCompile("\\D\\d{4}[\\-_]\\d{2}[\\-_]\\d{2,}")
|
||||
var DatePathRegexp = regexp.MustCompile("\\D\\d{4}/\\d{1,2}/?\\d*")
|
||||
var DateTimeRegexp = regexp.MustCompile("\\D\\d{4}[\\-_]\\d{2}[\\-_]\\d{2}.{1,4}\\d{2}\\D\\d{2}\\D\\d{2,}")
|
||||
|
@ -13,6 +16,17 @@ var DateIntRegexp = regexp.MustCompile("\\d{1,4}")
|
|||
var YearRegexp = regexp.MustCompile("\\d{4,5}")
|
||||
var IsDateRegexp = regexp.MustCompile("\\d{4}[\\-_]?\\d{2}[\\-_]?\\d{2}")
|
||||
var IsDateTimeRegexp = regexp.MustCompile("\\d{4}[\\-_]?\\d{2}[\\-_]?\\d{2}.{1,4}\\d{2}\\D?\\d{2}\\D?\\d{2}")
|
||||
var ExifDateTimeRegexp = regexp.MustCompile("((?P<year>\\d{4})|\\D{4})\\D((?P<month>\\d{2})|\\D{2})\\D((?P<day>\\d{2})|\\D{2})\\D((?P<h>\\d{2})|\\D{2})\\D((?P<m>\\d{2})|\\D{2})\\D((?P<s>\\d{2})|\\D{2})(?P<z>\\D)?(?P<zh>\\d{2})?\\D?(?P<zm>\\d{2})?")
|
||||
var ExifDateTimeMatch = make(map[string]int)
|
||||
|
||||
func init() {
|
||||
names := ExifDateTimeRegexp.SubexpNames()
|
||||
for i := 0; i < len(names); i++ {
|
||||
if name := names[i]; name != "" {
|
||||
ExifDateTimeMatch[name] = i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
YearMin = 1990
|
||||
|
@ -32,8 +46,99 @@ const (
|
|||
SecMax = 59
|
||||
)
|
||||
|
||||
// Time returns a string as time or the zero time instant in case it can not be converted.
|
||||
func Time(s string) (result time.Time) {
|
||||
// DateTime parses a string and returns a valid Exif timestamp if possible.
|
||||
func DateTime(s, timeZone string) (t time.Time) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// Panic? Return unknown time.
|
||||
t = time.Time{}
|
||||
}
|
||||
}()
|
||||
|
||||
// Empty timestamp? Return unknown time.
|
||||
if s == "" {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
s = strings.TrimLeft(s, " ")
|
||||
|
||||
// Timestamp too short or much too long? Return unknown time.
|
||||
if len(s) < 4 || len(s) > 50 {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// Pad short timestamp with whitespace at the end.
|
||||
s = fmt.Sprintf("%-19s", s)
|
||||
|
||||
// Shorten timestamp to max length.
|
||||
if len(s) > 25 {
|
||||
s = s[:25]
|
||||
}
|
||||
|
||||
v := ExifDateTimeMatch
|
||||
m := ExifDateTimeRegexp.FindStringSubmatch(s)
|
||||
|
||||
// Pattern doesn't match? Return unknown time.
|
||||
if len(m) == 0 {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// Default to UTC.
|
||||
tz := time.UTC
|
||||
|
||||
// Local time zone currently not supported (undefined).
|
||||
if timeZone == time.Local.String() {
|
||||
timeZone = ""
|
||||
}
|
||||
|
||||
// Set time zone.
|
||||
loc, err := time.LoadLocation(timeZone)
|
||||
|
||||
// Location found?
|
||||
if err == nil && timeZone != "" && tz != time.Local {
|
||||
tz = loc
|
||||
timeZone = tz.String()
|
||||
} else {
|
||||
timeZone = ""
|
||||
}
|
||||
|
||||
// Does the timestamp contain a time zone offset?
|
||||
z := m[v["z"]] // Supported values, if not empty: Z, +, -
|
||||
zh := IntVal(m[v["zh"]], 0, 23, 0) // Hours.
|
||||
zm := IntVal(m[v["zm"]], 0, 59, 0) // Minutes.
|
||||
|
||||
// Valid time zone offset found?
|
||||
if offset := (zh*60 + zm) * 60; offset > 0 && offset <= 86400 {
|
||||
// Offset timezone name example: UTC+03:30
|
||||
if z == "+" {
|
||||
// Positive offset relative to UTC.
|
||||
tz = time.FixedZone(fmt.Sprintf("UTC+%02d:%02d", zh, zm), offset)
|
||||
} else if z == "-" {
|
||||
// Negative offset relative to UTC.
|
||||
tz = time.FixedZone(fmt.Sprintf("UTC-%02d:%02d", zh, zm), -1*offset)
|
||||
}
|
||||
}
|
||||
|
||||
// Create rounded timestamp from parsed input values.
|
||||
t = time.Date(
|
||||
IntVal(m[v["year"]], 1, YearMax, time.Now().Year()),
|
||||
time.Month(IntVal(m[v["month"]], 1, 12, 1)),
|
||||
IntVal(m[v["day"]], 1, 31, 1),
|
||||
IntVal(m[v["h"]], 0, 23, 0),
|
||||
IntVal(m[v["m"]], 0, 59, 0),
|
||||
IntVal(m[v["s"]], 0, 59, 0),
|
||||
0,
|
||||
tz).Round(time.Second)
|
||||
|
||||
if timeZone != "" && loc != nil && loc != tz {
|
||||
return t.In(loc)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// DateFromFilePath returns a string as time or the zero time instant in case it can not be converted.
|
||||
func DateFromFilePath(s string) (result time.Time) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
result = time.Time{}
|
|
@ -6,239 +6,334 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTime(t *testing.T) {
|
||||
func TestDateTime(t *testing.T) {
|
||||
t.Run("2016: : : : ", func(t *testing.T) {
|
||||
result := DateTime("2016: : : : ", "")
|
||||
assert.Equal(t, "2016-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2016: :__ : : ", func(t *testing.T) {
|
||||
result := DateTime("2016: :__ : : ", "")
|
||||
assert.Equal(t, "2016-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2016:06:28 : :??", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28 : :??", "")
|
||||
assert.Equal(t, "2016-06-28 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2016:06:28 09:45:49", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28 09:45:49", "")
|
||||
assert.Equal(t, "2016-06-28 09:45:49 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2016:06:28 09:45:49+10:00", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28 09:45:49+10:00", "")
|
||||
assert.Equal(t, "2016-06-28 09:45:49 +1000 UTC+10:00", result.String())
|
||||
})
|
||||
t.Run("2016:06:28 : :", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28 : :", "")
|
||||
assert.Equal(t, "2016-06-28 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2016/06/28T09-45:49", func(t *testing.T) {
|
||||
result := DateTime("2016/06/28T09-45:49", "")
|
||||
assert.Equal(t, "2016-06-28 09:45:49 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2016:06:28T09:45:49Z", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28T09:45:49Z", "")
|
||||
assert.Equal(t, "2016-06-28 09:45:49 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2016:06:28T09:45: Z", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28T09:45: Z", "")
|
||||
assert.Equal(t, "2016-06-28 09:45:00 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2016:06:28T09:45: ", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28T09:45: ", "")
|
||||
assert.Equal(t, "2016-06-28 09:45:00 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2016:06:28T09:45: ZABC", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28T09:45: ZABC", "")
|
||||
assert.Equal(t, "2016-06-28 09:45:00 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2016:06:28T09:45: ABC", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28T09:45: ABC", "")
|
||||
assert.Equal(t, "2016-06-28 09:45:00 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2016:06:28 09:45:49+10:00ABC", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28 09:45:49+10:00ABC", "")
|
||||
assert.Equal(t, "2016-06-28 09:45:49 +1000 UTC+10:00", result.String())
|
||||
})
|
||||
t.Run(" 2016:06:28 09:45:49-01:30ABC", func(t *testing.T) {
|
||||
result := DateTime(" 2016:06:28 09:45:49-01:30ABC", "")
|
||||
assert.Equal(t, "2016-06-28 09:45:49 -0130 UTC-01:30", result.String())
|
||||
})
|
||||
t.Run("2016:06:28 09:45:49-0130", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28 09:45:49-0130", "")
|
||||
assert.Equal(t, "2016-06-28 09:45:49 -0130 UTC-01:30", result.String())
|
||||
})
|
||||
t.Run("UTC/016:06:28 09:45:49-0130", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28 09:45:49-0130", "UTC")
|
||||
assert.Equal(t, "2016-06-28 11:15:49 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("UTC/016:06:28 09:45:49-0130", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28 09:45:49.0130", "UTC")
|
||||
assert.Equal(t, "2016-06-28 09:45:49 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2012:08:08 22:07:18", func(t *testing.T) {
|
||||
result := DateTime("2012:08:08 22:07:18", "")
|
||||
assert.Equal(t, "2012-08-08 22:07:18 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("2020-01-30_09-57-18", func(t *testing.T) {
|
||||
result := DateTime("2020-01-30_09-57-18", "")
|
||||
assert.Equal(t, "2020-01-30 09:57:18 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("EuropeBerlin/2016:06:28 09:45:49+10:00ABC", func(t *testing.T) {
|
||||
result := DateTime("2016:06:28 09:45:49+10:00ABC", "Europe/Berlin")
|
||||
assert.Equal(t, "2016-06-28 01:45:49 +0200 CEST", result.String())
|
||||
})
|
||||
t.Run("EuropeBerlin/ 2016:06:28 09:45:49-01:30ABC", func(t *testing.T) {
|
||||
result := DateTime(" 2016:06:28 09:45:49-01:30ABC", "Europe/Berlin")
|
||||
assert.Equal(t, "2016-06-28 13:15:49 +0200 CEST", result.String())
|
||||
})
|
||||
t.Run("EuropeBerlin/2012:08:08 22:07:18", func(t *testing.T) {
|
||||
result := DateTime("2012:08:08 22:07:18", "Europe/Berlin")
|
||||
assert.Equal(t, "2012-08-08 22:07:18 +0200 CEST", result.String())
|
||||
})
|
||||
t.Run("EuropeBerlin/2020-01-30_09-57-18", func(t *testing.T) {
|
||||
result := DateTime("2020-01-30_09-57-18", "Europe/Berlin")
|
||||
assert.Equal(t, "2020-01-30 09:57:18 +0100 CET", result.String())
|
||||
})
|
||||
}
|
||||
|
||||
func TestDateFromFilePath(t *testing.T) {
|
||||
t.Run("2016/08/18 iPhone/WRNI2074.jpg", func(t *testing.T) {
|
||||
result := Time("2016/08/18 iPhone/WRNI2074.jpg")
|
||||
result := DateFromFilePath("2016/08/18 iPhone/WRNI2074.jpg")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2016-08-18 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("2016/08/18 iPhone/OZBJ8443.jpg", func(t *testing.T) {
|
||||
result := Time("2016/08/18 iPhone/OZBJ8443.jpg")
|
||||
result := DateFromFilePath("2016/08/18 iPhone/OZBJ8443.jpg")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2016-08-18 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("2018/04 - April/2018-04-12 19:24:49.gif", func(t *testing.T) {
|
||||
result := Time("2018/04 - April/2018-04-12 19:24:49.gif")
|
||||
result := DateFromFilePath("2018/04 - April/2018-04-12 19:24:49.gif")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2018-04-12 19:24:49 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("2018", func(t *testing.T) {
|
||||
result := Time("2018")
|
||||
result := DateFromFilePath("2018")
|
||||
assert.True(t, result.IsZero())
|
||||
})
|
||||
|
||||
t.Run("2018-04-12 19/24/49.gif", func(t *testing.T) {
|
||||
result := Time("2018-04-12 19/24/49.gif")
|
||||
result := DateFromFilePath("2018-04-12 19/24/49.gif")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2018-04-12 19:24:49 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/2020/1212/20130518_142022_3D657EBD.jpg", func(t *testing.T) {
|
||||
result := Time("/2020/1212/20130518_142022_3D657EBD.jpg")
|
||||
result := DateFromFilePath("/2020/1212/20130518_142022_3D657EBD.jpg")
|
||||
//assert.False(t, result.IsZero())
|
||||
assert.True(t, result.IsZero())
|
||||
})
|
||||
|
||||
t.Run("20130518_142022_3D657EBD.jpg", func(t *testing.T) {
|
||||
result := Time("20130518_142022_3D657EBD.jpg")
|
||||
result := DateFromFilePath("20130518_142022_3D657EBD.jpg")
|
||||
//assert.False(t, result.IsZero())
|
||||
assert.True(t, result.IsZero())
|
||||
})
|
||||
|
||||
t.Run("telegram_2020_01_30_09_57_18.jpg", func(t *testing.T) {
|
||||
result := Time("telegram_2020_01_30_09_57_18.jpg")
|
||||
result := DateFromFilePath("telegram_2020_01_30_09_57_18.jpg")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2020-01-30 09:57:18 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("Screenshot 2019_05_21 at 10.45.52.png", func(t *testing.T) {
|
||||
result := Time("Screenshot 2019_05_21 at 10.45.52.png")
|
||||
result := DateFromFilePath("Screenshot 2019_05_21 at 10.45.52.png")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2019-05-21 10:45:52 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("telegram_2020-01-30_09-57-18.jpg", func(t *testing.T) {
|
||||
result := Time("telegram_2020-01-30_09-57-18.jpg")
|
||||
result := DateFromFilePath("telegram_2020-01-30_09-57-18.jpg")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2020-01-30 09:57:18 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("Screenshot 2019-05-21 at 10.45.52.png", func(t *testing.T) {
|
||||
result := Time("Screenshot 2019-05-21 at 10.45.52.png")
|
||||
result := DateFromFilePath("Screenshot 2019-05-21 at 10.45.52.png")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2019-05-21 10:45:52 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("telegram_2020-01-30_09-18.jpg", func(t *testing.T) {
|
||||
result := Time("telegram_2020-01-30_09-18.jpg")
|
||||
result := DateFromFilePath("telegram_2020-01-30_09-18.jpg")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2020-01-30 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("Screenshot 2019-05-21 at 10545.52.png", func(t *testing.T) {
|
||||
result := Time("Screenshot 2019-05-21 at 10545.52.png")
|
||||
result := DateFromFilePath("Screenshot 2019-05-21 at 10545.52.png")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2019-05-21 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/2019-05-21/file2314.JPG", func(t *testing.T) {
|
||||
result := Time("/2019-05-21/file2314.JPG")
|
||||
result := DateFromFilePath("/2019-05-21/file2314.JPG")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2019-05-21 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/2019.05.21", func(t *testing.T) {
|
||||
result := Time("/2019.05.21")
|
||||
result := DateFromFilePath("/2019.05.21")
|
||||
assert.True(t, result.IsZero())
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/05.21.2019", func(t *testing.T) {
|
||||
result := Time("/05.21.2019")
|
||||
result := DateFromFilePath("/05.21.2019")
|
||||
assert.True(t, result.IsZero())
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/21.05.2019", func(t *testing.T) {
|
||||
result := Time("/21.05.2019")
|
||||
result := DateFromFilePath("/21.05.2019")
|
||||
assert.True(t, result.IsZero())
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("05/21/2019", func(t *testing.T) {
|
||||
result := Time("05/21/2019")
|
||||
result := DateFromFilePath("05/21/2019")
|
||||
assert.True(t, result.IsZero())
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("2019-07-23", func(t *testing.T) {
|
||||
result := Time("2019-07-23")
|
||||
result := DateFromFilePath("2019-07-23")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2019-07-23 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("Photos/2015-01-14", func(t *testing.T) {
|
||||
result := Time("Photos/2015-01-14")
|
||||
result := DateFromFilePath("Photos/2015-01-14")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2015-01-14 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("21/05/2019", func(t *testing.T) {
|
||||
result := Time("21/05/2019")
|
||||
result := DateFromFilePath("21/05/2019")
|
||||
assert.True(t, result.IsZero())
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("2019/05/21", func(t *testing.T) {
|
||||
result := Time("2019/05/21")
|
||||
result := DateFromFilePath("2019/05/21")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2019-05-21 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("2019/05/2145", func(t *testing.T) {
|
||||
result := Time("2019/05/2145")
|
||||
result := DateFromFilePath("2019/05/2145")
|
||||
assert.True(t, result.IsZero())
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/05/21/2019", func(t *testing.T) {
|
||||
result := Time("/05/21/2019")
|
||||
result := DateFromFilePath("/05/21/2019")
|
||||
assert.True(t, result.IsZero())
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/21/05/2019", func(t *testing.T) {
|
||||
result := Time("/21/05/2019")
|
||||
result := DateFromFilePath("/21/05/2019")
|
||||
assert.True(t, result.IsZero())
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/2019/05/21.jpeg", func(t *testing.T) {
|
||||
result := Time("/2019/05/21.jpeg")
|
||||
result := DateFromFilePath("/2019/05/21.jpeg")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2019-05-21 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/2019/05/21/foo.txt", func(t *testing.T) {
|
||||
result := Time("/2019/05/21/foo.txt")
|
||||
result := DateFromFilePath("/2019/05/21/foo.txt")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2019-05-21 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("2019/21/05", func(t *testing.T) {
|
||||
result := Time("2019/21/05")
|
||||
result := DateFromFilePath("2019/21/05")
|
||||
assert.True(t, result.IsZero())
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/2019/05/21/foo.jpg", func(t *testing.T) {
|
||||
result := Time("/2019/05/21/foo.jpg")
|
||||
result := DateFromFilePath("/2019/05/21/foo.jpg")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2019-05-21 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/2019/21/05/foo.jpg", func(t *testing.T) {
|
||||
result := Time("/2019/21/05/foo.jpg")
|
||||
result := DateFromFilePath("/2019/21/05/foo.jpg")
|
||||
assert.True(t, result.IsZero())
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/2019/5/foo.jpg", func(t *testing.T) {
|
||||
result := Time("/2019/5/foo.jpg")
|
||||
result := DateFromFilePath("/2019/5/foo.jpg")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2019-05-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/2019/1/3/foo.jpg", func(t *testing.T) {
|
||||
result := Time("/2019/1/3/foo.jpg")
|
||||
result := DateFromFilePath("/2019/1/3/foo.jpg")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2019-01-03 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("/1989/1/3/foo.jpg", func(t *testing.T) {
|
||||
result := Time("/1989/1/3/foo.jpg")
|
||||
result := DateFromFilePath("/1989/1/3/foo.jpg")
|
||||
assert.True(t, result.IsZero())
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("545452019/1/3/foo.jpg", func(t *testing.T) {
|
||||
result := Time("/2019/1/3/foo.jpg")
|
||||
result := DateFromFilePath("/2019/1/3/foo.jpg")
|
||||
assert.False(t, result.IsZero())
|
||||
assert.Equal(t, "2019-01-03 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("fo.jpg", func(t *testing.T) {
|
||||
result := Time("fo.jpg")
|
||||
result := DateFromFilePath("fo.jpg")
|
||||
assert.True(t, result.IsZero())
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("n >6", func(t *testing.T) {
|
||||
result := Time("2020-01-30_09-87-18-23.jpg")
|
||||
result := DateFromFilePath("2020-01-30_09-87-18-23.jpg")
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
|
||||
t.Run("year < yearmin", func(t *testing.T) {
|
||||
result := Time("1020-01-30_09-57-18.jpg")
|
||||
result := DateFromFilePath("1020-01-30_09-57-18.jpg")
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("hour > hourmax", func(t *testing.T) {
|
||||
result := Time("2020-01-30_25-57-18.jpg")
|
||||
result := DateFromFilePath("2020-01-30_25-57-18.jpg")
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("invalid days", func(t *testing.T) {
|
||||
result := Time("2020-01-00.jpg")
|
||||
result := DateFromFilePath("2020-01-00.jpg")
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("IMG-20191120-WA0001.jpg", func(t *testing.T) {
|
||||
result := Time("IMG-20191120-WA0001.jpg")
|
||||
result := DateFromFilePath("IMG-20191120-WA0001.jpg")
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
t.Run("VID-20191120-WA0001.jpg", func(t *testing.T) {
|
||||
result := Time("VID-20191120-WA0001.jpg")
|
||||
result := DateFromFilePath("VID-20191120-WA0001.jpg")
|
||||
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", result.String())
|
||||
})
|
||||
}
|
|
@ -20,6 +20,31 @@ func Int(s string) int {
|
|||
return int(result)
|
||||
}
|
||||
|
||||
// IntVal converts a string to a validated integer or a default if invalid.
|
||||
func IntVal(s string, min, max, d int) (i int) {
|
||||
s = strings.TrimSpace(s)
|
||||
|
||||
if s == "" {
|
||||
return d
|
||||
}
|
||||
|
||||
result, err := strconv.ParseInt(s, 10, 32)
|
||||
|
||||
if err != nil {
|
||||
return d
|
||||
}
|
||||
|
||||
i = int(result)
|
||||
|
||||
if i < min {
|
||||
return d
|
||||
} else if max != 0 && i > max {
|
||||
return d
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
// UInt converts a string to an unsigned integer or 0 if invalid.
|
||||
func UInt(s string) uint {
|
||||
if s == "" {
|
||||
|
|
|
@ -7,31 +7,79 @@ import (
|
|||
)
|
||||
|
||||
func TestInt(t *testing.T) {
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
result := Int("")
|
||||
assert.Equal(t, 0, result)
|
||||
})
|
||||
|
||||
t.Run("non-numeric", func(t *testing.T) {
|
||||
t.Run("NonNumeric", func(t *testing.T) {
|
||||
result := Int("Screenshot")
|
||||
assert.Equal(t, 0, result)
|
||||
})
|
||||
|
||||
t.Run("zero", func(t *testing.T) {
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
result := Int("0")
|
||||
assert.Equal(t, 0, result)
|
||||
})
|
||||
|
||||
t.Run("int", func(t *testing.T) {
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
result := Int("000123")
|
||||
assert.Equal(t, 123, result)
|
||||
})
|
||||
|
||||
t.Run("WhitespacePadding", func(t *testing.T) {
|
||||
result := Int(" 123\t ")
|
||||
assert.Equal(t, 123, result)
|
||||
})
|
||||
|
||||
t.Run("PositiveInt", func(t *testing.T) {
|
||||
result := Int("123")
|
||||
assert.Equal(t, 123, result)
|
||||
})
|
||||
|
||||
t.Run("negative int", func(t *testing.T) {
|
||||
t.Run("NegativeInt", func(t *testing.T) {
|
||||
result := Int("-123")
|
||||
assert.Equal(t, -123, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntVal(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
result := IntVal("", 1, 31, 1)
|
||||
assert.Equal(t, 1, result)
|
||||
})
|
||||
|
||||
t.Run("NonNumeric", func(t *testing.T) {
|
||||
result := IntVal("Screenshot", 1, 31, -1)
|
||||
assert.Equal(t, -1, result)
|
||||
})
|
||||
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
result := IntVal("0", -10, 10, -1)
|
||||
assert.Equal(t, 0, result)
|
||||
})
|
||||
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
result := IntVal("000123", 1, 1000, 1)
|
||||
assert.Equal(t, 123, result)
|
||||
})
|
||||
|
||||
t.Run("WhitespacePadding", func(t *testing.T) {
|
||||
result := IntVal(" 123\t ", 1, 1000, 1)
|
||||
assert.Equal(t, 123, result)
|
||||
})
|
||||
|
||||
t.Run("PositiveInt", func(t *testing.T) {
|
||||
result := IntVal("123", 1, 1000, 1)
|
||||
assert.Equal(t, 123, result)
|
||||
})
|
||||
|
||||
t.Run("NegativeInt", func(t *testing.T) {
|
||||
result := IntVal("-123", -1000, 1000, 1)
|
||||
assert.Equal(t, -123, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsUInt(t *testing.T) {
|
||||
assert.False(t, IsUInt(""))
|
||||
assert.False(t, IsUInt("12 3"))
|
||||
|
@ -52,27 +100,32 @@ func TestIsPosInt(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestUInt(t *testing.T) {
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
result := UInt("")
|
||||
assert.Equal(t, uint(0), result)
|
||||
})
|
||||
|
||||
t.Run("non-numeric", func(t *testing.T) {
|
||||
t.Run("NonNumeric", func(t *testing.T) {
|
||||
result := UInt("Screenshot")
|
||||
assert.Equal(t, uint(0), result)
|
||||
})
|
||||
|
||||
t.Run("zero", func(t *testing.T) {
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
result := UInt("0")
|
||||
assert.Equal(t, uint(0), result)
|
||||
})
|
||||
|
||||
t.Run("int", func(t *testing.T) {
|
||||
t.Run("LeadingZeros", func(t *testing.T) {
|
||||
result := UInt("000123")
|
||||
assert.Equal(t, uint(0x7b), result)
|
||||
})
|
||||
|
||||
t.Run("PositiveInt", func(t *testing.T) {
|
||||
result := UInt("123")
|
||||
assert.Equal(t, uint(0x7b), result)
|
||||
})
|
||||
|
||||
t.Run("negative int", func(t *testing.T) {
|
||||
t.Run("NegativeInt", func(t *testing.T) {
|
||||
result := UInt("-123")
|
||||
assert.Equal(t, uint(0), result)
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue