Root cause of m's 11:16 reminder emails: the alpine runtime image installs
only ca-certificates and ships no /usr/share/zoneinfo, so
time.LoadLocation("Europe/Berlin") errored in production. inSlot's silent
UTC fallback then matched reminder_morning_time=09:00 against now.UTC().Hour(),
firing at 09:00 UTC = 11:00 Berlin (CEST UTC+2) plus the ticker phase.
Fix:
- import _ "time/tzdata" in cmd/server/main.go (and the services package
for test parity) — embeds Go's IANA database in the binary, ~450KB,
works without OS tzdata.
- inSlot now logs and returns false on bad tz instead of pretending the
user lives in UTC. matchesLocalDueDate mirrors the same change.
- Tests updated: previous "Mars/Olympus falls back to UTC" expectation
flipped to "skips user", new TestTZDataEmbedded asserts
LoadLocation("Europe/Berlin") works in the test binary, new
TestInSlot_BerlinAt0900_NotAt1100 locks the headline regression
(must fire at 07:05 UTC = 09:05 Berlin, must NOT fire at 09:16 UTC =
11:16 Berlin).
Validation at the user-save boundary (UserService line 296-300, 619)
already rejected unparseable IANA names — that remains; this PR only
hardens the read path so any pre-existing corrupt rows skip rather than
silently reroute.
Build/vet/test/bun-build all green. Self-merging to main.