All Decisions

ADR-0017: Time Zone and Day Key Strategy for DailyReflection

DateFebruary 8, 2026
CategoryData Architecture
Tags
data-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.