memories: parse filename dates as UTC, not server local
`extract_date_from_filename` was calling `Local::from_local_datetime` on the parsed YYYY-MM-DD-HH-MM-SS components, then `.timestamp()` was shifting the result by the SERVER's TZ offset to produce real UTC seconds. That made filename-sourced timestamps disagree with EXIF- sourced timestamps by hours: kamadak-exif's `DateTimeOriginal` is a naive string parsed AS-IF-UTC (the project's load-bearing "naive local reinterpreted as UTC" convention), and Apollo's photo matcher re-anchors that naive value through the BROWSER's TZ when matching to the track. Anything stamped in server-local instead got double-shifted on its way through the matcher and through any `formatNaive*` display path on the client. Visible symptom in the Apollo DETAILS modal: a photo's CURRENT date read correctly (1:25 AM via exif) while FROM FILENAME read 4 hours ahead (5:25 AM in EDT) for the same `IMG_20160710_012515.jpg`. Switch to `Utc::from_utc_datetime` so `.timestamp()` returns the wall-clock-as-UTC unix seconds — same convention as the EXIF path. The /memories endpoint, the canonical-date waterfall (which feeds `image_exif.date_taken` for filename-only files), and Apollo's DETAILS modal `filename_date` field all now line up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{HttpRequest, HttpResponse, Responder, get, web};
|
||||
use chrono::LocalResult::{Ambiguous, Single};
|
||||
use chrono::{DateTime, FixedOffset, Local, LocalResult, NaiveDate, TimeZone};
|
||||
use chrono::{DateTime, FixedOffset, Local, NaiveDate, TimeZone, Utc};
|
||||
use log::{debug, trace, warn};
|
||||
use opentelemetry::KeyValue;
|
||||
use opentelemetry::trace::{Span, Status, TraceContextExt, Tracer};
|
||||
@@ -134,6 +133,15 @@ pub struct MemoriesResponse {
|
||||
}
|
||||
|
||||
pub fn extract_date_from_filename(filename: &str) -> Option<DateTime<FixedOffset>> {
|
||||
// Filenames carry only digits — no timezone. We deliberately interpret
|
||||
// them as UTC so `.timestamp()` returns the wall-clock-as-UTC unix
|
||||
// seconds, matching the "naive local reinterpreted as UTC" convention
|
||||
// image_exif.date_taken uses for kamadak-exif DateTimeOriginal (which
|
||||
// is also naive). Anything else (Local::from_local_datetime, the
|
||||
// previous behavior) shifted filename-sourced dates by the SERVER's
|
||||
// TZ offset relative to UTC, making them disagree with EXIF-sourced
|
||||
// dates by hours and double-shifting through Apollo's photo matcher
|
||||
// (which re-anchors naive-as-UTC via the browser TZ).
|
||||
let build_date_from_ymd_capture =
|
||||
|captures: ®ex::Captures| -> Option<DateTime<FixedOffset>> {
|
||||
let year = captures.get(1)?.as_str().parse::<i32>().ok()?;
|
||||
@@ -143,16 +151,8 @@ pub fn extract_date_from_filename(filename: &str) -> Option<DateTime<FixedOffset
|
||||
let min = captures.get(5)?.as_str().parse::<u32>().ok()?;
|
||||
let sec = captures.get(6)?.as_str().parse::<u32>().ok()?;
|
||||
|
||||
match Local.from_local_datetime(
|
||||
&NaiveDate::from_ymd_opt(year, month, day)?.and_hms_opt(hour, min, sec)?,
|
||||
) {
|
||||
Single(dt) => Some(dt.fixed_offset()),
|
||||
Ambiguous(early_dt, _) => Some(early_dt.fixed_offset()),
|
||||
LocalResult::None => {
|
||||
warn!("Weird local date: {:?}", filename);
|
||||
None
|
||||
}
|
||||
}
|
||||
let naive = NaiveDate::from_ymd_opt(year, month, day)?.and_hms_opt(hour, min, sec)?;
|
||||
Some(Utc.from_utc_datetime(&naive).fixed_offset())
|
||||
};
|
||||
|
||||
// 1. Screenshot format: Screenshot_2014-06-01-20-44-50.png
|
||||
|
||||
Reference in New Issue
Block a user