Skip to content

[Bug]: Usage "Past 24 hours" labels ignore the Time format setting and always use the 12-hour clock #15570

Description

@mInrOz

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/web

Steps to reproduce

  1. Open Settings and set Time format to 24-hour. Alternatively, keep System default on an OS or browser that uses a 24-hour clock (for example en-GB or nb-NO).
  2. Open Usage and select Past 24 hours.
  3. Look at the window label in the top bar, the chart's x-axis labels, the chart tooltip, and the first column of the breakdown table.

Expected behavior

The hourly labels follow the Time format setting, like the chat timestamps and the limit reset times on the same page. With 24-hour or a 24-hour system default, the labels show 14 or 14:00, not 2 PM.

Actual behavior

All hourly labels on the Past 24 hours view use the US 12-hour clock for all three Time format values: 2 PM, 2 PM today, Oct 3, 2 PM to Oct 4, 2 PM.

The limit reset times on the same page follow the setting. They use formatUpcomingTimestamp(window.resetsAt, timestampFormat, now) (UsageLimits.tsx#L90-L97).

Root cause

The hourly helpers in packages/shared/src/usageFormat.ts take only a time zone. They create Intl.DateTimeFormat("en-US", { hour: "numeric", ... }) with no hour12 or hourCycle. The en-US locale forces a 12-hour clock, so neither the setting nor the system locale has an effect:

The rest of the web app formats times through apps/web/src/timestampFormat.ts (L3-L22). Those helpers take timestampFormat from usePrimarySettings((settings) => settings.timestampFormat). They also use the host locale from #6190. UsagePage and UsageProviderChart do not read timestampFormat, and no prop or context passes it to the hourly helpers.

Affected call sites (Usage > Past 24 hours)

Location Surface
UsagePage.tsx#L322-L325 Window label in the top bar (formatDateTimeShort)
UsagePage.tsx#L814-L816 First column of the breakdown table (formatHourShort)
UsageProviderChart.tsx#L311-L316, used at L410 and L441-L453 Chart tooltip (formatRelativeHourShort) and x-axis labels (formatHourShort)

The helpers were added in #6170. The mobile Usage screen also calls formatHourShort (UsageRouteScreen.tsx#L526, L544). Mobile has no Time format setting, so that path keeps the current behavior unless the fix changes the default.

Other web surfaces that ignore the setting

These surfaces use the runtime locale default instead of timestampFormat:

The closed PR #12867 covered most of this second list but did not touch the Usage page. A maintainer closed it on 2026-10-01 for missing current UI evidence. I report the Usage page as the primary defect here, and I list the other surfaces for completeness.

Secondary observation

The usage helpers also hard-code English month names. formatDateTimeShort uses en-US, and formatDayShort (usageFormat.ts#L64) has a fixed "Jan" to "Dec" table. An nb-NO user sees Oct 3, while the runtime locale gives 3. okt.. This can be intentional, as with the English tooltip dates in #10256. I do not ask for a change to it here.

Suggested fix (sketch)

Resolve the setting to a boolean in the web app and pass it to the shared helpers. The parameter is optional, so mobile and existing tests keep their current output.

// apps/web/src/timestampFormat.ts
+export function resolveHour12(timestampFormat: TimestampFormat): boolean {
+  if (timestampFormat !== "locale") return timestampFormat === "12-hour";
+  return (
+    new Intl.DateTimeFormat(timestampLocale, { hour: "numeric" }).resolvedOptions().hour12 ?? false
+  );
+}

// packages/shared/src/usageFormat.ts
-export function formatHourShort(hourStart: string, timeZone?: string): string {
+export function formatHourShort(hourStart: string, timeZone?: string, hour12?: boolean): string {
   ...
-  const hourFormat = dateTimeFormatter("en-US", { ...options, hour: "numeric" });
+  const hourFormat = dateTimeFormatter("en-US", {
+    ...options,
+    hour: "numeric",
+    ...(hour12 === undefined ? {} : { hour12 }),
+  });
   // same for the timeZoneName formatter, formatDateTimeShort, and formatRelativeHourShort

// UsagePage.tsx and UsageProviderChart.tsx
+const timestampFormat = usePrimarySettings((settings) => settings.timestampFormat);
+const hour12 = resolveHour12(timestampFormat);
-formatHourShort(period.hourStart, window.timeZone)
+formatHourShort(period.hourStart, window.timeZone, hour12)

With en-US and hour12: false, an hour-only label renders as 14. If 14:00 reads better on the axis, add minute: "2-digit" in 24-hour mode only.

Impact

Cosmetic issue

Version or commit

main @ eac52f0

Environment

Code inspection on main @ eac52f0, plus a Node.js 24.19.0 run (V8 with full ICU) of the unmodified usageFormat.ts and timestampFormat.ts modules. I did not check this in the packaged desktop app or a browser.

Logs or stack traces

# Instant 2026-10-04T12:37:00Z (14:37 in Europe/Oslo), timeZone "Europe/Oslo".
# The window is 2026-10-03T12:37Z to 2026-10-04T12:37Z.

== TZ=Europe/Oslo LANG=LC_ALL=nb_NO.UTF-8
default locale: nb-NO hour12 default: h23
usageFormat.formatHourShort          : "2 PM"
usageFormat.formatHourShort (DST)    : "2 AM GMT+2"
usageFormat.formatDateTimeShort      : "Oct 3, 2 PM to Oct 4, 2 PM"
usageFormat.formatRelativeHourShort  : "2 PM today" "2 PM yesterday"
timestampFormat.formatShortTimestamp(locale): "14:37"
timestampFormat.formatShortTimestamp(24-hour): "14:37"
timestampFormat.formatShortTimestamp(12-hour): "2:37 p.m."
== TZ=Europe/Oslo LANG=LC_ALL=en_GB.UTF-8
default locale: en-GB hour12 default: h23
usageFormat.formatHourShort          : "2 PM"
usageFormat.formatHourShort (DST)    : "2 AM GMT+2"
usageFormat.formatDateTimeShort      : "Oct 3, 2 PM to Oct 4, 2 PM"
usageFormat.formatRelativeHourShort  : "2 PM today" "2 PM yesterday"
timestampFormat.formatShortTimestamp(locale): "14:37"
timestampFormat.formatShortTimestamp(24-hour): "14:37"
timestampFormat.formatShortTimestamp(12-hour): "2:37 pm"

Workaround

None in the app. The hourly labels use the 12-hour clock for all three Time format values.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions