mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
@@ -19,6 +18,7 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/time/tz"
|
||||
"github.com/photoprism/photoprism/pkg/time/unix"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
@@ -184,8 +184,8 @@ func OIDCRedirect(router *gin.RouterGroup) {
|
||||
user.Settings().UILanguage = clean.Locale(userInfo.Locale.String(), user.Settings().UILanguage)
|
||||
|
||||
// Update UI timezone.
|
||||
if tz := userInfo.Zoneinfo; tz != "" && tz != time.UTC.String() {
|
||||
user.Settings().UITimeZone = tz
|
||||
if zone := userInfo.Zoneinfo; zone != "" && zone != tz.UTC {
|
||||
user.Settings().UITimeZone = zone
|
||||
}
|
||||
|
||||
// Update user location, if available.
|
||||
|
||||
@@ -6,13 +6,15 @@ import (
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/time/tz"
|
||||
)
|
||||
|
||||
func TestUTC(t *testing.T) {
|
||||
t.Run("Zone", func(t *testing.T) {
|
||||
utc := UTC()
|
||||
|
||||
if zone, offset := utc.Zone(); zone != time.UTC.String() {
|
||||
if zone, offset := utc.Zone(); zone != tz.UTC {
|
||||
t.Error("should be UTC")
|
||||
} else if offset != 0 {
|
||||
t.Error("offset should be 0")
|
||||
@@ -26,7 +28,7 @@ func TestUTC(t *testing.T) {
|
||||
|
||||
assert.True(t, utcGorm.After(utc))
|
||||
|
||||
if zone, offset := utcGorm.Zone(); zone != time.UTC.String() {
|
||||
if zone, offset := utcGorm.Zone(); zone != tz.UTC {
|
||||
t.Error("gorm time should be UTC")
|
||||
} else if offset != 0 {
|
||||
t.Error("gorm time offset should be 0")
|
||||
|
||||
@@ -126,6 +126,7 @@ func NewUserPhoto(stackable bool, userUid string) Photo {
|
||||
LensID: UnknownLens.ID,
|
||||
CellID: UnknownLocation.ID,
|
||||
PlaceID: UnknownPlace.ID,
|
||||
TimeZone: tz.Local,
|
||||
Camera: &UnknownCamera,
|
||||
Lens: &UnknownLens,
|
||||
Cell: &UnknownLocation,
|
||||
|
||||
@@ -77,22 +77,24 @@ func (m *Photo) SetTakenAt(utc, local time.Time, zone, source string) {
|
||||
m.TakenAt = utc
|
||||
m.TakenAtLocal = local
|
||||
m.TakenSrc = source
|
||||
m.TimeZone = tz.Name(m.TimeZone)
|
||||
|
||||
if zone == time.UTC.String() && m.TimeZone != tz.Local {
|
||||
// Location exists, set local time from UTC.
|
||||
if zone == tz.UTC && m.TimeZone != tz.Local {
|
||||
// Set local time from UTC and keep existing time zone.
|
||||
m.TakenAtLocal = m.GetTakenAtLocal()
|
||||
} else if zone != tz.Local {
|
||||
// Apply new time zone.
|
||||
m.TimeZone = zone
|
||||
m.TakenAt = m.GetTakenAt()
|
||||
} else if m.TimeZoneUTC() {
|
||||
m.TimeZone = zone
|
||||
// Keep UTC?
|
||||
|
||||
if m.TimeZoneUTC() {
|
||||
m.TakenAtLocal = utc
|
||||
} else {
|
||||
m.TakenAt = m.GetTakenAt()
|
||||
}
|
||||
} else if m.TimeZone != tz.Local {
|
||||
// Apply existing time zone.
|
||||
} else if m.TimeZoneUTC() {
|
||||
m.TakenAtLocal = utc
|
||||
} else if !m.TimeZoneLocal() {
|
||||
// Keep existing time zone.
|
||||
m.TakenAt = m.GetTakenAt()
|
||||
}
|
||||
|
||||
@@ -104,6 +106,11 @@ func (m *Photo) TimeZoneUTC() bool {
|
||||
return tz.IsUTC(m.TimeZone)
|
||||
}
|
||||
|
||||
// TimeZoneLocal tests if the current time zone is Local.
|
||||
func (m *Photo) TimeZoneLocal() bool {
|
||||
return tz.IsLocal(m.TimeZone)
|
||||
}
|
||||
|
||||
// UpdateTimeZone updates the time zone.
|
||||
func (m *Photo) UpdateTimeZone(zone string) {
|
||||
if zone == "" {
|
||||
|
||||
@@ -145,7 +145,7 @@ func TestPhoto_SetTakenAt(t *testing.T) {
|
||||
assert.Equal(t, time.Date(2013, 11, 11, 9, 7, 18, 0, time.UTC), m.TakenAt)
|
||||
assert.Equal(t, time.Date(2013, 11, 11, 9, 7, 18, 0, time.UTC), m.TakenAtLocal)
|
||||
})
|
||||
t.Run("SuccessLocalEmpty", func(t *testing.T) {
|
||||
t.Run("LocalIsZero", func(t *testing.T) {
|
||||
m := PhotoFixtures.Get("Photo15")
|
||||
assert.Equal(t, time.Date(2013, 11, 11, 9, 7, 18, 0, time.UTC), m.TakenAt)
|
||||
assert.Equal(t, time.Date(2013, 11, 11, 9, 7, 18, 0, time.UTC), m.TakenAtLocal)
|
||||
@@ -154,32 +154,53 @@ func TestPhoto_SetTakenAt(t *testing.T) {
|
||||
assert.Equal(t, time.Date(2019, 12, 11, 9, 7, 18, 0, time.UTC), m.TakenAt)
|
||||
assert.Equal(t, time.Date(2019, 12, 11, 9, 7, 18, 0, time.UTC), m.TakenAtLocal)
|
||||
})
|
||||
t.Run("SkipUpdate", func(t *testing.T) {
|
||||
t.Run("Ignore", func(t *testing.T) {
|
||||
photo := &Photo{TakenAt: time.Date(2013, 11, 11, 9, 7, 18, 0, time.UTC)}
|
||||
photo.SetTakenAt(time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC),
|
||||
time.Date(2014, 12, 11, 10, 7, 18, 0, time.UTC), "", SrcAuto)
|
||||
assert.Equal(t, time.Date(2013, 11, 11, 9, 7, 18, 0, time.UTC), photo.TakenAt)
|
||||
})
|
||||
t.Run("LocalFromUTC", func(t *testing.T) {
|
||||
t.Run("SetFromUTC", func(t *testing.T) {
|
||||
photo := &Photo{TakenAt: time.Date(2015, 11, 11, 9, 7, 18, 0, time.UTC), TimeZone: "Europe/Berlin"}
|
||||
photo.SetTakenAt(time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC),
|
||||
time.Date(2014, 12, 11, 10, 7, 18, 0, time.UTC), time.UTC.String(), SrcManual)
|
||||
time.Date(2014, 12, 11, 10, 7, 18, 0, time.UTC), tz.UTC, SrcManual)
|
||||
assert.Equal(t, "Europe/Berlin", photo.TimeZone)
|
||||
assert.Equal(t, time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC), photo.TakenAt)
|
||||
assert.Equal(t, time.Date(2014, 12, 11, 10, 07, 18, 0, time.UTC), photo.TakenAtLocal)
|
||||
})
|
||||
t.Run("KeepUTC", func(t *testing.T) {
|
||||
photo := &Photo{TakenAt: time.Date(2015, 11, 11, 9, 7, 18, 0, time.UTC), TimeZone: time.UTC.String()}
|
||||
t.Run("SetUtc", func(t *testing.T) {
|
||||
photo := &Photo{TakenAt: time.Date(2015, 11, 11, 9, 7, 18, 0, time.UTC), TimeZone: tz.UTC}
|
||||
photo.SetTakenAt(time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC),
|
||||
time.Date(2014, 12, 11, 10, 7, 18, 0, time.UTC), time.UTC.String(), SrcManual)
|
||||
time.Date(2014, 12, 11, 10, 7, 18, 0, time.UTC), tz.UTC, SrcManual)
|
||||
assert.Equal(t, tz.UTC, photo.TimeZone)
|
||||
assert.Equal(t, time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC), photo.TakenAt)
|
||||
assert.Equal(t, time.Date(2014, 12, 11, 9, 07, 18, 0, time.UTC), photo.TakenAtLocal)
|
||||
})
|
||||
t.Run("UTCToLocal", func(t *testing.T) {
|
||||
photo := &Photo{TakenAt: time.Date(2015, 11, 11, 9, 7, 18, 0, time.UTC), TimeZone: time.UTC.String()}
|
||||
t.Run("KeepUtc", func(t *testing.T) {
|
||||
photo := &Photo{TakenAt: time.Date(2015, 11, 11, 9, 7, 18, 0, time.UTC), TimeZone: tz.UTC}
|
||||
photo.SetTakenAt(time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC),
|
||||
time.Date(2014, 12, 11, 10, 7, 18, 0, time.UTC), "", SrcManual)
|
||||
assert.Equal(t, tz.UTC, photo.TimeZone)
|
||||
assert.Equal(t, time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC), photo.TakenAt)
|
||||
assert.Equal(t, time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC), photo.TakenAtLocal)
|
||||
})
|
||||
t.Run("NoTimeZone", func(t *testing.T) {
|
||||
photo := &Photo{TakenAt: time.Date(2015, 11, 11, 9, 7, 18, 0, time.UTC), TimeZone: ""}
|
||||
photo.SetTakenAt(time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC),
|
||||
time.Date(2014, 12, 11, 10, 7, 18, 0, time.UTC), "", SrcMeta)
|
||||
assert.Equal(t, tz.Local, photo.TimeZone)
|
||||
assert.Equal(t, time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC), photo.TakenAt)
|
||||
assert.Equal(t, time.Date(2014, 12, 11, 10, 7, 18, 0, time.UTC), photo.TakenAtLocal)
|
||||
photo.SetTakenAt(time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC),
|
||||
time.Date(2014, 12, 11, 10, 7, 18, 0, time.UTC), tz.Local, SrcMeta)
|
||||
assert.Equal(t, tz.Local, photo.TimeZone)
|
||||
assert.Equal(t, time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC), photo.TakenAt)
|
||||
assert.Equal(t, time.Date(2014, 12, 11, 10, 7, 18, 0, time.UTC), photo.TakenAtLocal)
|
||||
photo.SetTakenAt(time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC),
|
||||
time.Date(2014, 12, 11, 10, 7, 18, 0, time.UTC), tz.UTC, SrcMeta)
|
||||
assert.Equal(t, tz.UTC, photo.TimeZone)
|
||||
assert.Equal(t, time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC), photo.TakenAt)
|
||||
assert.Equal(t, time.Date(2014, 12, 11, 9, 7, 18, 0, time.UTC), photo.TakenAtLocal)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/ai/classify"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/pkg/time/tz"
|
||||
)
|
||||
|
||||
func TestSavePhotoForm(t *testing.T) {
|
||||
@@ -849,10 +850,12 @@ func TestNewPhoto(t *testing.T) {
|
||||
t.Run("Stackable", func(t *testing.T) {
|
||||
m := NewPhoto(true)
|
||||
assert.Equal(t, IsStackable, m.PhotoStack)
|
||||
assert.Equal(t, tz.Local, m.TimeZone)
|
||||
})
|
||||
t.Run("NotStackable", func(t *testing.T) {
|
||||
m := NewPhoto(false)
|
||||
assert.Equal(t, IsUnstacked, m.PhotoStack)
|
||||
assert.Equal(t, tz.Local, m.TimeZone)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ func (data *Data) Exiftool(jsonData []byte, originalName string) (err error) {
|
||||
|
||||
// Fallback to GPS UTC Time?
|
||||
if data.TakenAt.IsZero() && data.TakenAtLocal.IsZero() && !data.TakenGps.IsZero() {
|
||||
data.TimeZone = time.UTC.String()
|
||||
data.TimeZone = tz.UTC
|
||||
data.TakenAt = data.TakenGps.UTC()
|
||||
data.TakenAtLocal = time.Time{}
|
||||
}
|
||||
@@ -236,7 +236,7 @@ func (data *Data) Exiftool(jsonData []byte, originalName string) (err error) {
|
||||
// Assume default time zone for MP4 & Quicktime videos is UTC.
|
||||
// see https://exiftool.org/TagNames/QuickTime.html
|
||||
log.Debugf("metadata: default time zone for %s is UTC (%s)", logName, clean.Log(mt))
|
||||
data.TimeZone = time.UTC.String()
|
||||
data.TimeZone = tz.UTC
|
||||
data.TakenAt = data.TakenAt.UTC()
|
||||
data.TakenAtLocal = time.Time{}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ package meta
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media/projection"
|
||||
"github.com/photoprism/photoprism/pkg/media/video"
|
||||
"github.com/photoprism/photoprism/pkg/time/tz"
|
||||
)
|
||||
|
||||
func TestJSON(t *testing.T) {
|
||||
@@ -166,7 +166,7 @@ func TestJSON(t *testing.T) {
|
||||
assert.Equal(t, "2020-05-11 14:18:35 +0000 UTC", data.TakenAtLocal.String())
|
||||
assert.Equal(t, "2020-05-11 14:18:35 +0000 UTC", data.TakenAt.String())
|
||||
assert.Equal(t, 0, data.TakenNs)
|
||||
assert.Equal(t, time.UTC.String(), data.TimeZone)
|
||||
assert.Equal(t, tz.UTC, data.TimeZone)
|
||||
assert.Equal(t, "", data.TimeOffset)
|
||||
assert.Equal(t, 270, data.Width)
|
||||
assert.Equal(t, 480, data.Height)
|
||||
@@ -1072,7 +1072,7 @@ func TestJSON(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "2021-07-12T22:56:37Z", data.TakenAt.Format("2006-01-02T15:04:05Z"))
|
||||
assert.Equal(t, "2021-07-12T22:56:37Z", data.TakenAtLocal.Format("2006-01-02T15:04:05Z"))
|
||||
assert.Equal(t, time.UTC.String(), data.TimeZone)
|
||||
assert.Equal(t, tz.UTC, data.TimeZone)
|
||||
assert.Equal(t, 1080, data.Height)
|
||||
assert.Equal(t, 1920, data.Width)
|
||||
assert.Equal(t, 0.0, data.Lat)
|
||||
@@ -1090,7 +1090,7 @@ func TestJSON(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "2021-07-06T13:51:36Z", data.TakenAt.Format("2006-01-02T15:04:05Z"))
|
||||
assert.Equal(t, "2021-07-06T13:51:36Z", data.TakenAtLocal.Format("2006-01-02T15:04:05Z"))
|
||||
assert.Equal(t, time.UTC.String(), data.TimeZone)
|
||||
assert.Equal(t, tz.UTC, data.TimeZone)
|
||||
assert.Equal(t, 1080, data.Height)
|
||||
assert.Equal(t, 1920, data.Width)
|
||||
assert.Equal(t, 0.0, data.Lat)
|
||||
|
||||
@@ -4,11 +4,10 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/time/tz"
|
||||
)
|
||||
|
||||
// XMP parses an XMP file and returns a Data struct.
|
||||
@@ -68,7 +67,7 @@ func (data *Data) XMP(fileName string) (err error) {
|
||||
if takenAt := doc.TakenAt(data.TimeZone); !takenAt.IsZero() {
|
||||
data.TakenAt = takenAt.UTC()
|
||||
if data.TimeZone == "" {
|
||||
data.TimeZone = time.UTC.String()
|
||||
data.TimeZone = tz.UTC
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/video"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/time/tz"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
"github.com/photoprism/photoprism/pkg/txt/clip"
|
||||
)
|
||||
@@ -792,11 +793,11 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot
|
||||
if m.IsMedia() && entity.SrcPriority[photo.TakenSrc] <= entity.SrcPriority[entity.SrcName] {
|
||||
// Try to extract time from original file name first.
|
||||
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)
|
||||
} else if !taken.IsZero() {
|
||||
photo.SetTakenAt(taken, taken, time.UTC.String(), takenSrc)
|
||||
photo.SetTakenAt(taken, taken, tz.Local, entity.SrcName)
|
||||
} else if takenAt, takenSrc := m.TakenAt(); takenSrc == entity.SrcName {
|
||||
photo.SetTakenAt(takenAt, takenAt, tz.Local, entity.SrcName)
|
||||
} else if !takenAt.IsZero() {
|
||||
photo.SetTakenAt(takenAt, takenAt, tz.UTC, takenSrc)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ func (m *MediaFile) TakenAt() (time.Time, string) {
|
||||
return m.takenAt, m.takenAtSrc
|
||||
}
|
||||
|
||||
// Otherwiese, try to determine creation time from file name and path.
|
||||
// Otherwise, try to determine creation time from file name and path.
|
||||
if nameTime := txt.DateFromFilePath(m.fileName); !nameTime.IsZero() {
|
||||
m.takenAt = nameTime
|
||||
m.takenAtSrc = entity.SrcName
|
||||
|
||||
Reference in New Issue
Block a user