Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions gui/src/components/codex-account-pool-helpers.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { TFn } from "../i18n/shared";
import { IconTicket } from "../icons";
import type { CodexAccountEntry } from "./codex-account-pool-types";
import { daysUntil, formatCreditDate } from "./codex-account-pool-utils";
import { daysUntil, formatCreditDate, formatCreditDateTime } from "./codex-account-pool-utils";

export function CodexCreditItem({ index, grantedAt, expiresAt, isNext, t }: {
index: number; grantedAt: string; expiresAt: string; isNext: boolean; t: TFn;
Expand All @@ -19,7 +19,7 @@ export function CodexCreditItem({ index, grantedAt, expiresAt, isNext, t }: {
</div>
<div className="credit-item-dates">
<span>{t("codexAuth.creditGranted", { date: formatCreditDate(grantedAt) })}</span>
<span className={urgent ? "credit-urgent" : ""}>{t("codexAuth.creditExpires", { date: formatCreditDate(expiresAt), days: String(days) })}</span>
<span className={urgent ? "credit-urgent" : ""}>{t("codexAuth.creditExpires", { date: formatCreditDateTime(expiresAt), days: String(days) })}</span>
</div>
</div>
);
Expand Down
9 changes: 8 additions & 1 deletion gui/src/components/codex-account-pool-utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { formatCreditDate as formatCreditDateIntl } from "../intl-formatters";
import {
formatCreditDate as formatCreditDateIntl,
formatCreditDateTime as formatCreditDateTimeIntl,
} from "../intl-formatters";

export function formatCreditDate(iso: string): string {
return formatCreditDateIntl(iso);
}

export function formatCreditDateTime(iso: string): string {
return formatCreditDateTimeIntl(iso);
}

export function daysUntil(iso: string): number {
return Math.max(0, Math.ceil((new Date(iso).getTime() - Date.now()) / 86_400_000));
}
14 changes: 14 additions & 0 deletions gui/src/intl-formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,26 @@ const CREDIT_DATE_FORMAT = new Intl.DateTimeFormat(undefined, {
year: "numeric",
});

const CREDIT_DATE_TIME_FORMAT = new Intl.DateTimeFormat(undefined, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Format the expiration time with the selected UI locale

When the user selects a dashboard language different from the browser language, this formatter still uses the browser default because it is constructed with undefined; for example, a German dashboard on an English browser embeds an English Jul 31, 2026, 02:34 PM timestamp in the German expiration text instead of the expected German date and 24-hour time. Pass the active I18nContext.locale through CodexCreditItem and cache or construct the formatter for that locale, as the quota reset formatters already do.

Useful? React with 👍 / 👎.

month: "short",
day: "numeric",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
});

export function formatCreditDate(iso: string): string {
const date = new Date(iso);
if (Number.isNaN(date.getTime())) return "\u2014";
return CREDIT_DATE_FORMAT.format(date);
}

export function formatCreditDateTime(iso: string): string {
const date = new Date(iso);
if (Number.isNaN(date.getTime())) return "\u2014";
return CREDIT_DATE_TIME_FORMAT.format(date);
}

/** Format a USD cost estimate for display. Returns "—" when unavailable. */
export function formatEstimatedUsdValue(value: number, locale?: string): string {
if (!Number.isFinite(value) || value < 0) return "\u2014";
Expand Down
19 changes: 19 additions & 0 deletions gui/tests/intl-formatters.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { describe, expect, test } from "bun:test";
import { formatCreditDate, formatCreditDateTime } from "../src/intl-formatters";

describe("credit date formatting", () => {
test("keeps the compact date format for grant dates", () => {
expect(formatCreditDate("2026-07-31T12:34:56Z")).not.toContain("12:34");
});

test("includes the local time for expiration dates", () => {
const iso = "2026-07-31T12:34:56Z";

expect(formatCreditDateTime(iso)).not.toBe(formatCreditDate(iso));
expect(formatCreditDateTime(iso)).not.toBe("—");
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

test("handles invalid dates consistently", () => {
expect(formatCreditDateTime("invalid")).toBe("—");
});
});
Loading