ADR-0017: Time Zone and Day Key Strategy for DailyReflection
DateFebruary 8, 2026
CategoryData Architecture
Tagsdata-modelpersistence-local
Context
- Roadmap open question: Time-zone and date handling strategy.
- DailyReflection needs a “day identifier” for:
- The one-per-day uniqueness rule (ADR-0016)
- Week window aggregation for weekly synthesis
- Correct UI labeling (“Reflection for Feb 8”)
- Users travel, change time zones, and can reflect after midnight.
- We will later package reflections to an LLM; “what day is this?” must be unambiguous in exported/packaged data.
Constraints:
- Local-first; all decisions must be computable on-device.
- Room/SQLite; prefer simple uniqueness constraints.
Decision
We will define a day_key for DailyReflection as a LocalDate in a specific IANA time zone.
For each DailyReflection we store:
day_key: LocalDate (e.g., 2026-02-08)
zone_id: IANA zone ID (e.g., America/Los_Angeles) used to interpret that day
created_at_utc: Instant (UTC timestamp)
updated_at_utc: Instant
Uniqueness:
- Enforce unique(day_key) for DailyReflection records.
Operational rules:
- When creating “today’s” reflection, compute
day_key using the device’s current zone at the time and store that zone in zone_id.
- Past reflections keep their stored
day_key and zone_id permanently.
- If the device time zone changes later, it does not rewrite past day keys.
Week aggregation rules (for synthesis):
- By default, weekly windows are computed using the current device zone when generating the report.
- When rendering each day, display the reflection’s stored
day_key and zone_id for clarity.
Scope boundaries:
- Day-boundary offsets (e.g., “my day starts at 4am”) are out of scope for now.
Rationale
- Clarity for the user: A daily reflection belongs to a named local calendar date.
- Simple constraint: Unique(day_key) is easy to enforce and understand.
- LLM packaging unambiguity: The exported packet can include both
day_key and zone_id, plus UTC timestamps, allowing reconstruction and preventing confusion during weekly advice generation.
- Travel-safe: Storing the zone with each reflection preserves context (“this was written while I was in Tokyo”).
- Avoids rewriting history: Time zone changes don’t mutate prior entries.
Consequences
Positive effects:
- Daily reflection identity is stable and easy to query.
- Weekly synthesis can reliably select a 7-day range by day keys.
- LLM packaging can include:
day_key
zone_id
- UTC timestamps
- ordered question/answer pairs (ADR-0014)
- free-text notes (ADR-0014)
Known downsides / edge cases:
- Near-midnight reflections: A reflection written at 00:30 may belong to the new day even if the user intended it for “yesterday”. This is common and expected; a future feature may allow choosing the day.
- Time zone rollback: If the user changes time zone backward, “today” may compute to a
day_key that already exists. The app should open the existing reflection.
- Week boundary ambiguity: “This week” depends on zone; we choose current zone for synthesis by default.
Follow-up work:
- Add an optional UI affordance to “Reflect for…” allowing the user to pick a day_key explicitly.
- Consider a future setting:
day_start_offset_minutes to shift day boundaries (requires a new ADR if adopted).
Alternatives Considered
- Use UTC date as the day key — rejected: does not match user’s lived calendar day.
- Key by (day_key, zone_id) — rejected for now: allows multiple reflections for the same calendar date when traveling, which complicates ADR-0016 and weekly packaging.
- Derive day_key only from created_at and current zone (no stored zone) — rejected: zone changes would reinterpret the day, rewriting history implicitly.
Notes
- This strategy is intentionally conservative and favors predictable user experience and stable week packaging for LLM guidance.