Skip to content

feat(settings): display timezone (account default + per-device override) - #25

Merged
SiteRelEnby merged 2 commits into
mainfrom
feat/timezone-setting
Jul 13, 2026
Merged

SiteRelEnby merged 2 commits into
mainfrom
feat/timezone-setting

Conversation

@SiteRelEnby

Copy link
Copy Markdown
Collaborator

Adds the display-timezone preference to Android, mirroring web's two tiers.

Tiers

  • Account default (System.timezone): an IANA zone or null = "Automatic" (each device's own clock). Synced across devices and exported. Set via PATCH /v1/systems/me.
    • "Automatic" is an explicit null the server must receive. Since Moshi codegen drops null fields and the backend uses model_dump(exclude_unset=True) (omit = unchanged, null = auto), the account tier sends a dedicated single-field body serialized with .serializeNulls().
  • Device override (local PreferencesRepository): shadows the account on this device only. Absent = follow the account, "auto" = pin to this device's clock, or a pinned IANA zone.

Resolution (device override > account > device-local) is exposed app-wide via a LocalDisplayTimeZone composition local, sourced from a cached account timezone (refreshed on login, persisted like file_cdn_base) combined with the local override.

UI

Settings → Appearance gains an Account timezone picker and an On this device override. Both use a searchable picker that leads with a "Common" group of friendly, DST-compensated names (Eastern Time (US & Canada), UK / Ireland (London), ...) then the full "All time zones" list, matching web's timezone-select. Selected common zones display their friendly label.

Applied

Front history, journal entry, and member-revision timestamps render in the resolved zone. This also fixes a latent bug where those journal/member times rendered in the server's offset (OffsetDateTime.toLocalDateTime()) rather than any chosen zone.

Scope / follow-up

Deliberately partial sweep. Remaining timestamp surfaces still render in the device clock and are tracked as follow-up: analytics grouping, poll close-dates, files, imports, retention, custom-field date editor, the history chart buckets + date-range pickers, the RecentFronts widget, and the Wear module. (Widget/wear are separate processes; the ViewModel surfaces need the effective zone via prefs rather than the composition local.)

Testing

  • Unit tests for the resolver (tier precedence, auto, invalid-zone fallback) and the common-zone label mapping.
  • :app:assemblePlayRelease green (through R8); unit suite green.
  • Verified on device: account zone, device override, and history/journal/member times shifting.

Backend dependency

The account tier reads/writes System.timezone on /v1/systems/me, so it needs the timezone-enabled backend deployed to the target instance.

Mirrors web's two-tier timezone preference:

- Account default (System.timezone): an IANA zone or null = "automatic"
  (each device's own clock), synced across devices via PATCH
  /v1/systems/me. "Automatic" is an explicit null the server must receive,
  so it goes through a dedicated serialize-nulls body (the shared codegen
  adapter drops null fields; the backend uses exclude_unset).
- Device override (local pref): shadows the account on this device only.
  Absent = follow the account, "auto" = pin to this device's clock, or a
  pinned zone.

Resolution (device override > account > device-local) is exposed app-wide
through a LocalDisplayTimeZone composition local, sourced from a cached
account timezone (refreshed on login) combined with the local override.

UI lives under Settings > Appearance: an account picker and an "On this
device" override, each using a searchable picker that leads with a
"Common" group of friendly, DST-compensated names (Eastern Time, UK /
Ireland, ...) then the full zone list, matching web's timezone-select.

Front history, journal, and member-revision timestamps now render in the
resolved zone. This also fixes those journal/member times previously
rendering in the server's offset rather than any chosen zone. Remaining
timestamp surfaces (analytics, polls, files, imports, retention, the
widget, and wear) still use the device clock and are tracked as follow-up.

Adds unit tests for the resolver and the common-zone mapping.
@SiteRelEnby
SiteRelEnby enabled auto-merge July 12, 2026 21:52
@SiteRelEnby
SiteRelEnby merged commit 0f51f4f into main Jul 13, 2026
1 check passed
@SiteRelEnby
SiteRelEnby deleted the feat/timezone-setting branch July 13, 2026 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant