Repository navigation
Description
Sleep: December 31, 1969 appears on startup before the ring connects
The Sleep screen briefly showed December 31, 1969 when opening PulseLoop, before the ring connected. Its date header should show the correct reference night from the first frame, including while disconnected or waiting for stored data to load.
Environment
- Phone: Samsung Galaxy A71, Android 13.
- Time zone: America/New_York.
- Installed build before the fix:
2.11.0-crp-retry-test2-debug, version code 75. - Affected area: Sleep → Day → date header.
Steps to reproduce
- Start PulseLoop from a stopped process with the ring disconnected, and PulseLoop on the Sleep screen.
- Open Sleep with the Day range selected.
- Watch the date header while the initial database read is pending.
The appearance is timing-dependent: the invalid date is visible while the screen still holds its initial state. A fast database read can hide it. Ring connection is the context in which it was noticed; the date calculation itself does not depend on BLE.
Actual: the initial header formats timestamp zero as a real date. In America/New_York, that is December 31, 1969.
Expected: show the current reference night immediately. Before 4 AM local, that is yesterday; from 4 AM onward, it is today. The header and date picker should agree, even with no stored sleep or ring connection.
Cause
SleepViewModel.SleepState.shownDayMillis defaults to 0L. SleepDayNavHeader passes that value directly to dayLabel, daySubLabel, and the date picker. The label formatter converts it with Instant.ofEpochMilli, so the missing day becomes a Unix-epoch date.
The asynchronous SleepViewModel.rebuild eventually supplies the correct reference night. Until it finishes, the initial zero remains visible; a failed rebuild can leave it in place.
iOS parity
The README asks that feature requests and UI changes go through iOS first. This is an Android startup-state bug: the proposed fix restores the behavior iOS already has.
On iOS main at 8d8d8b6, SleepView derives the header date from Date() and passes it through SleepService.dayReferenceNight. There is no initial zero timestamp in that path. Source inspection found no corresponding iOS startup bug; the iOS app was not run during this investigation.
Related issues and PRs reviewed
Searched open and closed issues and PRs in both repositories on November 4, 2026, for 1969/1970, epoch dates, sleep dates, startup, and the affected state names. The 1969/1970 searches also covered comments. No duplicate report or existing fix was found.
- Android PR #28, merged: includes
0d7212c, the Android port of Sleep day navigation. Git blame traces bothshownDayMillis = 0Land the header's unguarded formatting to this commit. This is the origin of the failing startup path. - iOS PR #84, merged: introduced the Sleep date stepper and picker that Android ported. It establishes the intended reference-night behavior, including the 4 AM boundary. The iOS implementation calculates the date from the current time.
- Android issue #107, closed, and PR #108, merged: concern CRP history being stored one day early after 8 PM. They address decoded history dates; this report concerns an initial display value before the database read completes. The startup fix leaves that history handling intact.
- iOS PR #106, open: fixes calendar-dependent date keys and locale/24-hour display formatting, among other changes. Its date-formatting changes do not address a zero-valued startup date or replace this Android fix.
Local fix and validation
A local fix is implemented and tested, ready for a focused Android PR:
- Initialize the displayed day using the same reference-night calculation used after loading (
TimeUtil.referenceNightLocal()), so startup and loaded state agree. - Resolve zero or negative day values to the reference night at the display boundary, and use the same resolved value for both header labels and the date picker.
- Preserve valid historical selections and the existing 4 AM reference-night rule.
Five new regression tests cover the empty initial state, zero and negative values, the 4 AM boundary, daylight-saving transitions, and historical selections. Coverage includes New York, UTC, and Tokyo. Those tests and the 13 existing TimeUtilTest cases passed: 18 tests, zero failures. The targeted run excluded an unrelated, ignored local test helper that references a removed class. Debug compilation and APK assembly also passed.
Debug build 76 (2.11.0-sleep-date-test-debug) was tested on the Samsung Galaxy A71. A cold launch succeeded, and the Sleep screen displayed Today — WED, NOV 4, with no 1969 or 1970 date in the inspected screen. The device check did not record the first frame before the database returned.
The fix changes presentation state only. It requires no database migration, stored-history cleanup, or BLE protocol changes.
Activity