mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-11 16:24:11 +01:00
Signed-off-by: Michael Mayer <michael@photoprism.app> Co-authored-by: Michael Mayer <michael@photoprism.app> Co-authored-by: graciousgrey <theresagresch@gmail.com>
166 lines
4.3 KiB
Go
166 lines
4.3 KiB
Go
package entity
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/photoprism/photoprism/pkg/time/tz"
|
|
"github.com/photoprism/photoprism/pkg/txt"
|
|
)
|
|
|
|
// TrustedTime tests if the photo has a known date and time from a trusted source.
|
|
func (m *Photo) TrustedTime() bool {
|
|
if SrcPriority[m.TakenSrc] <= SrcPriority[SrcEstimate] {
|
|
return false
|
|
} else if m.TakenAt.IsZero() || m.TakenAtLocal.IsZero() {
|
|
return false
|
|
} else if tz.Name(m.TimeZone) == tz.Local {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// SetTakenAt changes the photo date if not empty and from the same source.
|
|
func (m *Photo) SetTakenAt(utc, local time.Time, zone, source string) {
|
|
if utc.IsZero() || utc.Year() < 1000 || utc.Year() > txt.YearMax {
|
|
return
|
|
}
|
|
|
|
// Prevent the existing time from being overwritten by lower priority sources.
|
|
if SrcPriority[source] < SrcPriority[m.TakenSrc] && !m.TakenAt.IsZero() {
|
|
return
|
|
}
|
|
|
|
// Normalize time zone string.
|
|
zone = tz.Name(zone)
|
|
|
|
// Ignore sub-seconds to avoid jitter.
|
|
utc = tz.TruncateUTC(utc)
|
|
|
|
// Default local time to taken if zero or invalid.
|
|
if local.IsZero() || local.Year() < 1000 {
|
|
local = utc
|
|
} else {
|
|
local = tz.TruncateLocal(local)
|
|
}
|
|
|
|
// If no zone is specified, assume the current zone or try to determine
|
|
// the time zone based on the time offset. Otherwise, default to Local.
|
|
if source == SrcName && tz.Name(zone) == tz.Local && tz.Name(m.TimeZone) == tz.Local {
|
|
// Assume Local timezone if the time was extracted from a filename.
|
|
zone = tz.Local
|
|
} else if zone == tz.Unknown {
|
|
if m.TimeZone != tz.Unknown {
|
|
zone = m.TimeZone
|
|
} else if !utc.Equal(local) {
|
|
zone = tz.UtcOffset(utc, local, "")
|
|
}
|
|
|
|
if zone == tz.Unknown {
|
|
zone = tz.Local
|
|
}
|
|
}
|
|
|
|
// Don't update older date.
|
|
if SrcPriority[source] <= SrcPriority[SrcAuto] && !m.TakenAt.IsZero() && utc.After(m.TakenAt) {
|
|
return
|
|
}
|
|
|
|
// Use location time zone if it has a higher priority.
|
|
if SrcPriority[source] < SrcPriority[m.PlaceSrc] && m.HasLatLng() {
|
|
if locZone := m.LocationTimeZone(); locZone != "" {
|
|
if zone == tz.UTC {
|
|
local = tz.LocationUTC(utc, tz.Find(locZone))
|
|
}
|
|
zone = locZone
|
|
}
|
|
}
|
|
|
|
// Set UTC time and date source.
|
|
m.TakenAt = utc
|
|
m.TakenAtLocal = local
|
|
m.TakenSrc = source
|
|
m.TimeZone = tz.Name(m.TimeZone)
|
|
|
|
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
|
|
|
|
if m.TimeZoneUTC() {
|
|
m.TakenAtLocal = utc
|
|
} else {
|
|
m.TakenAt = m.GetTakenAt()
|
|
}
|
|
} else if m.TimeZoneUTC() {
|
|
m.TakenAtLocal = utc
|
|
} else if !m.TimeZoneLocal() {
|
|
// Keep existing time zone.
|
|
m.TakenAt = m.GetTakenAt()
|
|
}
|
|
|
|
m.UpdateDateFields()
|
|
}
|
|
|
|
// TimeZoneUTC tests if the current time zone is UTC.
|
|
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 applies a new time zone when the source priority allows it and recalculates derived times.
|
|
func (m *Photo) UpdateTimeZone(zone string) {
|
|
if zone == "" {
|
|
return
|
|
} else if zone = tz.Name(zone); zone == tz.Local || zone == tz.UTC || zone == tz.Name(m.TimeZone) {
|
|
return
|
|
}
|
|
|
|
if SrcPriority[m.TakenSrc] >= SrcPriority[SrcManual] && !tz.IsLocal(m.TimeZone) {
|
|
return
|
|
}
|
|
|
|
if m.TimeZoneUTC() {
|
|
m.TimeZone = zone
|
|
m.TakenAtLocal = m.GetTakenAtLocal()
|
|
} else {
|
|
m.TimeZone = zone
|
|
m.TakenAt = m.GetTakenAt()
|
|
}
|
|
}
|
|
|
|
// UpdateDateFields updates internal date fields.
|
|
func (m *Photo) UpdateDateFields() {
|
|
if m.TakenAt.IsZero() || m.TakenAt.Year() < 1000 {
|
|
return
|
|
}
|
|
|
|
if m.TakenAtLocal.IsZero() || m.TakenAtLocal.Year() < 1000 {
|
|
m.TakenAtLocal = m.TakenAt
|
|
}
|
|
|
|
// Set date to unknown if file system date is about the same as indexing time.
|
|
if m.TakenSrc == SrcAuto && m.TakenAt.After(m.CreatedAt.Add(-24*time.Hour)) {
|
|
m.PhotoYear = UnknownYear
|
|
m.PhotoMonth = UnknownMonth
|
|
m.PhotoDay = UnknownDay
|
|
} else if m.TakenSrc != SrcManual && m.TakenSrc != SrcBatch {
|
|
m.PhotoYear = m.TakenAtLocal.Year()
|
|
m.PhotoMonth = int(m.TakenAtLocal.Month())
|
|
m.PhotoDay = m.TakenAtLocal.Day()
|
|
}
|
|
|
|
// Update photo_taken_at column in related files.
|
|
Log("photo", "update date fields",
|
|
UnscopedDb().Model(File{}).
|
|
Where("photo_id = ? AND photo_taken_at <> ?", m.ID, m.TakenAtLocal).
|
|
Updates(File{PhotoTakenAt: m.TakenAtLocal}).Error,
|
|
)
|
|
}
|