Skip to content

Commit 4e9694f

Browse files
aksOpsclaude
andcommitted
fix(ui): quota reset timer shows actual remaining time, not 0 sec
relativeTime measures how long *ago* a timestamp was and clamps negatives to 0, so rendering `resets in ${relativeTime(resetAt)}` against a future reset timestamp always displayed "0 sec". Add relativeFuture for forward-counting durations ("in 3 hr") and use it in QuotaStrip. Shares the coarse-duration formatter with relativeTime so both render identically. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 79eaf8e commit 4e9694f

2 files changed

Lines changed: 18 additions & 2 deletions

File tree

ui/src/components/QuotaStrip.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useQuota } from "@/hooks/useQuota";
2-
import { relativeTime } from "@/lib/format";
2+
import { relativeFuture } from "@/lib/format";
33
import { cn } from "@/lib/utils";
44

55
interface BarProps {
@@ -50,7 +50,7 @@ function QuotaBar({ pct, resetAt, label }: BarProps) {
5050
className="hidden shrink-0 text-xs text-fg-dim md:inline"
5151
title={`Resets at ${resetAt}`}
5252
>
53-
resets in {relativeTime(resetAt)}
53+
resets in {relativeFuture(resetAt)}
5454
</span>
5555
)}
5656
</div>

ui/src/lib/format.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ export function stripAnsi(s: string): string {
1414
export function relativeTime(iso: string | Date, now: Date = new Date()): string {
1515
const then = typeof iso === "string" ? new Date(iso) : iso;
1616
const seconds = Math.max(0, Math.round((now.getTime() - then.getTime()) / 1000));
17+
return formatCoarseDuration(seconds);
18+
}
19+
20+
/**
21+
* Same coarse-duration formatter as `relativeTime`, but counts forward
22+
* to a future timestamp: "in 3 hr". Used for things like quota reset
23+
* timers where `iso` is in the future; relativeTime would clamp those
24+
* to "0 sec" because it measures how long *ago* the timestamp was.
25+
*/
26+
export function relativeFuture(iso: string | Date, now: Date = new Date()): string {
27+
const then = typeof iso === "string" ? new Date(iso) : iso;
28+
const seconds = Math.max(0, Math.round((then.getTime() - now.getTime()) / 1000));
29+
return formatCoarseDuration(seconds);
30+
}
31+
32+
function formatCoarseDuration(seconds: number): string {
1733
if (seconds < 60) return `${seconds} sec`;
1834
const minutes = Math.round(seconds / 60);
1935
if (minutes < 60) return `${minutes} min`;

0 commit comments

Comments
 (0)