From 28507224dccde5bc9c646a7f2fcc994359cff551 Mon Sep 17 00:00:00 2001 From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 00:54:41 +0200 Subject: [PATCH] Show the date for a message from today's weekday last week (#5341) A reply sent last Friday at 12:02 PM, read on Friday at 11:47 AM, was labelled "Friday 12:02 PM" because the weekday window counted 24-hour periods, and that reply was just under seven of them old. Count calendar days instead, so the weekday form covers only the previous six days and today's weekday a week back shows its date. Closes #5225 Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com> --- packages/app/src/utils/time.test.ts | 11 +++++++++++ packages/app/src/utils/time.ts | 25 +++++++++++++------------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/packages/app/src/utils/time.test.ts b/packages/app/src/utils/time.test.ts index f979797b55..292fffbf6d 100644 --- a/packages/app/src/utils/time.test.ts +++ b/packages/app/src/utils/time.test.ts @@ -108,6 +108,17 @@ describe("formatMessageTimestamp", () => { expect(formatted).toMatch(/10:12 PM|22:12/); }); + it("shows the full date for last week's same weekday, even under seven days ago", () => { + // 2026-09-18 is a Friday. The reply is 6 days 23h45m old and lands on today's weekday, + // so a weekday label would read as today. + const now = new Date(2026, 8, 25, 11, 47); + const date = new Date(2026, 8, 18, 12, 2); + const formatted = formatMessageTimestamp(date, now); + expect(formatted).toMatch(/Sep|September/); + expect(formatted).toMatch(/18/); + expect(formatted).not.toMatch(/Friday/); + }); + it("includes full date for older timestamps", () => { const now = new Date(2026, 4, 14, 17, 30); const date = new Date(2026, 3, 1, 9, 5); diff --git a/packages/app/src/utils/time.ts b/packages/app/src/utils/time.ts index f91487dfb5..8fa94e8926 100644 --- a/packages/app/src/utils/time.ts +++ b/packages/app/src/utils/time.ts @@ -91,12 +91,14 @@ export function formatCompactTimeAgo(date: Date, now: Date = new Date()): string return describeCompactTimeAgo(date, now).label; } -function isSameLocalDay(a: Date, b: Date): boolean { - return ( - a.getFullYear() === b.getFullYear() && - a.getMonth() === b.getMonth() && - a.getDate() === b.getDate() - ); +/** + * How many local midnights lie between two instants: 0 for the same day, 1 for yesterday. + * Counted on the calendar rather than in elapsed time, so six days and 23 hours ago on + * today's weekday is 7, and rounded so a DST day of 23 or 25 hours still counts as one. + */ +function localCalendarDaysBetween(earlier: Date, later: Date): number { + const startOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate()); + return Math.round((startOfDay(later).getTime() - startOfDay(earlier).getTime()) / DAY_MS); } // Cached Intl formatter. Explicitly carrying `hourCycle` from the resolved @@ -120,19 +122,18 @@ function getTimeFormatter(): Intl.DateTimeFormat { /** * Format a chat-message timestamp for hover-revealed UI. * - Same day: "10:11 PM" or "22:11" depending on user preference - * - Within ~6 days: "Wednesday 10:11 PM" - * - Older: "14 May 2026, 10:11 PM" + * - The previous 6 calendar days: "Wednesday 10:11 PM" + * - Older, including today's weekday last week: "14 May 2026, 10:11 PM" */ export function formatMessageTimestamp(date: Date, now: Date = new Date()): string { const time = getTimeFormatter().format(date); + const daysAgo = localCalendarDaysBetween(date, now); - if (isSameLocalDay(date, now)) { + if (daysAgo === 0) { return time; } - const diffMs = now.getTime() - date.getTime(); - const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); - if (diffDays >= 0 && diffDays < 7) { + if (daysAgo > 0 && daysAgo < 7) { const weekday = date.toLocaleDateString(undefined, { weekday: "long" }); return `${weekday} ${time}`; }