- Shipped
- August 30, 2026 at 6:57 PM UTC
- Author
- Kamo
- Commit
- 9e2e78d
Reported by a member: past chats cannot be identified or verified by the date they were sent or received — only a time is present. Their own history runs back to April, so all but the nine messages they sent today read as a bare "3:33 AM" with nothing on screen to resolve it against; the message list draws no day separator either, so the stamp is the only thing that could carry the date and it never did. The instant was never the problem — `ts` is epoch millis off `dateCreated` and has been correct since the LocalDateTime parsing fix. Every stamp was simply rendered with `fmt.time()`, which formats a time of day and nothing else, whether the message is five minutes or four months old. `formatDayAwareTimeInZone` is the rule the member asked for: the time alone while the instant is still today's, `formatDateTimeInZone` in front of it once it is not. Today is a CALENDAR day in the member's zone rather than 24 elapsed hours — at 00:10 a message from 23:50 is twenty minutes old and belongs to yesterday, and that is precisely the message whose day nobody can supply from memory. It reads the clock per call rather than capturing it, so a conversation left open across midnight dates last night's messages on its next render. No new dictionary keys: the date comes from Intl in the member's own locale, so nothing here needs 21 translations. Applied to the four stamps that had it wrong: the message header, the "edited" tooltip, the per-member read receipts, and the social chat bubble. The SMS tab had a hand-rolled version of the same idea that asked `getFullYear()` — the browser's calendar, not the member's — and answered with a bare weekday, which stops identifying anything after seven days; it now goes through the same helper. serverTime's Intl formatters are memoised on the way past. Constructing one measures ~180us against ~3us to use, and a day-aware stamp asks for two day keys per row, so opening a 200-message history would have spent ~70ms building formatters it immediately discarded. They are immutable, so one per (locale, zone, options) is byte-identical to one per call — the same argument, and the same measurement, as the cache useFormatters already keeps.