fix(dates): read exiftool's word-form GPS hemisphere refs (#163) #165

Merged
cameron merged 1 commits from fix/exiftool-gps-hemisphere into master 2026-10-13 14:32:37 +00:00
Owner

Fixes #163.

Problem

The exiftool date-resolver path compared GPSLatitudeRef / GPSLongitudeRef against the one-letter forms "S" / "W". With ImageApi's flags, exiftool prints the word forms ("South", "West"). So every western- or southern-hemisphere photo resolved by exiftool was mirrored into the opposite hemisphere before the GPS→zone lookup.

On prod this hit 280 of the 281 date_taken_tz_source = 'gps' rows. Ohio photos resolved to roughly UTC+8 (camera_offset_minutes = +480), which made their date_utc 12–13 h off. Apollo #37 matches on true_instant, so this has to land before #37 deploys.

Fix

src/date_resolver.rs adds a gps_ref_is(value, letter) helper. It matches the first character case-insensitively, so "W", "w", "West" and " West" all count. This is the same rule exif.rs already uses for the kamadak path.

Tests

  • parse_payload_reads_exiftool_word_hemisphere_refs: real exiftool 12.76 output for an Ohio photo (West/North) and a Sydney photo (South/East). It fails if the old comparison is put back.
  • gps_ref_is_matches_letter_and_word_forms: unit test for the helper.

The full suite passes (1523 tests), and clippy and fmt are clean.

After deploy

Existing rows keep their wrong offsets until they're re-resolved. Back up the DB first, then:

UPDATE image_exif
SET date_utc = NULL, camera_offset_minutes = NULL, date_taken_tz_source = NULL
WHERE date_taken_tz_source = 'gps';

The drain picks them up again within a tick or two. The test server needs the same reset.

🤖 Generated with Claude Code

Fixes #163. ## Problem The exiftool date-resolver path compared `GPSLatitudeRef` / `GPSLongitudeRef` against the one-letter forms `"S"` / `"W"`. With ImageApi's flags, exiftool prints the word forms (`"South"`, `"West"`). So every western- or southern-hemisphere photo resolved by exiftool was mirrored into the opposite hemisphere before the GPS→zone lookup. On prod this hit 280 of the 281 `date_taken_tz_source = 'gps'` rows. Ohio photos resolved to roughly UTC+8 (`camera_offset_minutes = +480`), which made their `date_utc` 12–13 h off. Apollo #37 matches on `true_instant`, so this has to land before #37 deploys. ## Fix `src/date_resolver.rs` adds a `gps_ref_is(value, letter)` helper. It matches the first character case-insensitively, so `"W"`, `"w"`, `"West"` and `" West"` all count. This is the same rule `exif.rs` already uses for the kamadak path. ## Tests - `parse_payload_reads_exiftool_word_hemisphere_refs`: real exiftool 12.76 output for an Ohio photo (`West`/`North`) and a Sydney photo (`South`/`East`). It fails if the old comparison is put back. - `gps_ref_is_matches_letter_and_word_forms`: unit test for the helper. The full suite passes (1523 tests), and clippy and fmt are clean. ## After deploy Existing rows keep their wrong offsets until they're re-resolved. Back up the DB first, then: ```sql UPDATE image_exif SET date_utc = NULL, camera_offset_minutes = NULL, date_taken_tz_source = NULL WHERE date_taken_tz_source = 'gps'; ``` The drain picks them up again within a tick or two. The test server needs the same reset. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
cameron added 1 commit 2026-10-13 14:32:27 +00:00
The exiftool branch matched GPSLatitudeRef / GPSLongitudeRef with an
exact one-letter compare ("S" / "W"), but exiftool's print conversion
(which parse_gps_dms relies on) renders them as "South" / "West". The
flags never set, so every western longitude and southern latitude
reached the camera-offset cascade positive and the GPS step looked up
the mirrored point: northeast Ohio became China, offset UTC+8. On prod,
all 280 western-hemisphere `gps`-sourced rows carry +480, putting their
date_utc 12-13 h off.

gps_ref_is matches the first letter case-insensitively, the same rule
as exif::extract_gps_coordinate (whose kamadak path was already
correct; the stored gps_latitude / gps_longitude columns are unaffected).

Tests use exiftool's real output strings ("West" / "South", verified
against exiftool 12.76 with the resolver's -j -q -G1 flags), including
a southern-hemisphere case; the new payload test fails on the old
compare. The existing test fed "W", which exiftool doesn't emit by
default, so it couldn't catch this.

Existing rows need a re-resolve after deploy; see #163 for the reset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cameron force-pushed fix/exiftool-gps-hemisphere from ac4f0e458c to 5b6ffd7040 2026-10-13 14:32:27 +00:00 Compare
cameron merged commit ff1ba1fcd6 into master 2026-10-13 14:32:37 +00:00
cameron deleted branch fix/exiftool-gps-hemisphere 2026-10-13 14:32:37 +00:00
Sign in to join this conversation.