fix(dates): assumed_server fallback uses the offset at the photo's wall, not now's
A container whose /etc/localtime is a copy rather than a symlink makes server_zone() return None; the assumed_server rung then fell back to Local::now()'s offset, which is only correct for captures from the current season. Historical winter walls on a DST host skewed date_utc by an hour. Resolve the process-local zone at the wall instead (earliest reading for ambiguous walls). Surfaced by test-server validation of #119.
This commit is contained in:
1 parent
af87765bbb
commit
615941a511
1 file changed
+44
-1
+44
-1
@@ -170,7 +170,16 @@ pub fn resolve_camera_offset(
|
||||
}
|
||||
let minutes = match server_zone {
|
||||
Some(zone) => offset_minutes_for_zone(zone, wall),
|
||||
None => Local::now().offset().fix().local_minus_utc() / 60,
|
||||
// Zone name unresolvable on this host: assume the process-local
|
||||
// zone, but at the PHOTO's wall (a DST host's current-season
|
||||
// offset would skew historical captures by an hour). Ambiguous
|
||||
// walls take the earliest (pre-transition) reading; truly
|
||||
// nonexistent local times fall back to the current offset.
|
||||
None => match Local.from_local_datetime(&wall) {
|
||||
LocalResult::Single(dt) => dt.offset().fix().local_minus_utc() / 60,
|
||||
LocalResult::Ambiguous(dt, _) => dt.offset().fix().local_minus_utc() / 60,
|
||||
LocalResult::None => Local::now().offset().fix().local_minus_utc() / 60,
|
||||
},
|
||||
};
|
||||
OffsetResolution {
|
||||
minutes,
|
||||
@@ -338,6 +347,40 @@ mod tests {
|
||||
assert_eq!(res.source, TzSource::AssumedServer);
|
||||
}
|
||||
|
||||
/// The offset the process-local zone assigns to a wall clock —
|
||||
/// independent of the current season.
|
||||
fn local_offset_at(wall: NaiveDateTime) -> i32 {
|
||||
Local
|
||||
.from_local_datetime(&wall)
|
||||
.earliest()
|
||||
.map(|dt| dt.offset().fix().local_minus_utc() / 60)
|
||||
.unwrap_or_else(|| Local::now().offset().fix().local_minus_utc() / 60)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assumed_server_fallback_is_dst_aware_at_the_wall_when_zone_unresolvable() {
|
||||
// `server_zone() = None` (e.g. a container whose /etc/localtime is a
|
||||
// copy, not a symlink): the fallback assumes the process-local zone
|
||||
// AT THE PHOTO'S WALL, not the current season's offset. On a DST
|
||||
// host this test catches the old behavior whenever the host is in
|
||||
// either season (one of the two walls disagrees with `now`);
|
||||
// on a fixed-offset host both assertions hold trivially.
|
||||
let evidence = OffsetEvidence::default();
|
||||
let winter = resolve_camera_offset(wall_january(), &evidence, None, &StubLookup(None));
|
||||
let summer = resolve_camera_offset(wall_july(), &evidence, None, &StubLookup(None));
|
||||
assert_eq!(
|
||||
winter.minutes,
|
||||
local_offset_at(wall_january()),
|
||||
"winter wall must carry the winter offset"
|
||||
);
|
||||
assert_eq!(
|
||||
summer.minutes,
|
||||
local_offset_at(wall_july()),
|
||||
"summer wall must carry the summer offset"
|
||||
);
|
||||
assert_eq!(winter.source, TzSource::AssumedServer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gps_miss_falls_through_to_server_zone() {
|
||||
let evidence = OffsetEvidence {
|
||||
|
||||
Reference in new issue
Block a user