feat(dates): filename date class — per-pattern conventions + filename-over-EXIF priority (#116)
The filename date class is now first-class (ADR-0001): - memories::extract_date_from_filename returns FilenameDate: Wall(NaiveDateTime) for the screenshot/dash/compact patterns, Utc(DateTime<Utc>) for the unix-timestamp patterns. No patterns added or removed; every existing pattern carries its convention. - Waterfall inversion in both resolve_date_taken and resolve_dates_batch: an in-class name is the original date of record and resolves BEFORE EXIF / container dates are consulted (shared filename_class_resolved step; in-class files skip exiftool entirely). Wall patterns run the offset cascade over the kamadak prior's evidence (empty evidence -> server-zone assumption, so a class file always resolves); timestamp patterns are true instants (tz_source utc_direct, no offset). - Legacy date_taken stays byte-identical: wall-as-UTC for wall patterns, epoch for timestamp patterns, via FilenameDate::legacy_datetime / legacy_timestamp at the seven consumer call sites. - Out-of-class files keep the global EXIF-wins waterfall unchanged. - CLAUDE.md waterfall section rewritten (step 0 = filename class; the EXIF-beats-filename note becomes out-of-class-only). Tests: five new waterfall-seam tests (wall no-evidence -> assumed_server; wall outranks EXIF with offset-tag evidence; wall GPS evidence; timestamp utc_direct; in-class video name resolves without exiftool) plus enum-shape assertions in every extractor test. Code-reviewed on both axes (standards: no violations, 3 of 4 P2 notes applied; spec: all acceptance criteria verified, no scope creep). 716 integration + 639 lib tests pass; fmt + clippy clean.
This commit is contained in:
1 parent
6f73a31e08
commit
a97bf53603
7 files changed
+358
-143
No files matched your search
@@ -526,8 +526,8 @@ pub async fn get_file_metadata(
|
||||
response.library_name = Some(resolved_library.name.clone());
|
||||
|
||||
// Extract date from filename if possible
|
||||
response.filename_date =
|
||||
memories::extract_date_from_filename(&path.path).map(|dt| dt.timestamp());
|
||||
response.filename_date = memories::extract_date_from_filename(&path.path)
|
||||
.map(memories::FilenameDate::legacy_timestamp);
|
||||
|
||||
// Query EXIF data if available
|
||||
if let Ok(mut dao) = exif_dao.lock()
|
||||
@@ -721,8 +721,8 @@ pub async fn set_image_gps(
|
||||
response.exif = Some(row.into());
|
||||
response.library_id = Some(resolved_library.id);
|
||||
response.library_name = Some(resolved_library.name.clone());
|
||||
response.filename_date =
|
||||
memories::extract_date_from_filename(&body.path).map(|dt| dt.timestamp());
|
||||
response.filename_date = memories::extract_date_from_filename(&body.path)
|
||||
.map(memories::FilenameDate::legacy_timestamp);
|
||||
span.set_status(Status::Ok);
|
||||
HttpResponse::Ok().json(response)
|
||||
}
|
||||
@@ -881,8 +881,8 @@ fn build_metadata_response_for_date_mutation(
|
||||
response.exif = Some(exif.into());
|
||||
response.library_id = Some(library.id);
|
||||
response.library_name = Some(library.name.clone());
|
||||
response.filename_date =
|
||||
memories::extract_date_from_filename(rel_path).map(|dt| dt.timestamp());
|
||||
response.filename_date = memories::extract_date_from_filename(rel_path)
|
||||
.map(memories::FilenameDate::legacy_timestamp);
|
||||
response
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user