From 8e5f83c0a3dd850b3b284672bbdab8b45e8e7181 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Sun, 1 Feb 2026 21:06:44 +0200 Subject: [PATCH] refactor(quota): unify reset time formatting across providers --- .../desktop/src-tauri/src/quota_providers.rs | 37 ++++++---------- packages/vscode/src/quotaProviders.ts | 44 +++++++++---------- packages/web/server/lib/quota-providers.js | 44 +++++++++---------- 3 files changed, 55 insertions(+), 70 deletions(-) diff --git a/packages/desktop/src-tauri/src/quota_providers.rs b/packages/desktop/src-tauri/src/quota_providers.rs index 025844eb..7c01d398 100644 --- a/packages/desktop/src-tauri/src/quota_providers.rs +++ b/packages/desktop/src-tauri/src/quota_providers.rs @@ -154,28 +154,18 @@ fn normalize_auth_entry(value: Option<&Value>) -> Option { } } -fn format_reset_at(timestamp_ms: i64) -> Option { - let dt = Local.timestamp_millis_opt(timestamp_ms).single()?; - Some(dt.format("%-I:%M %p").to_string()) -} +fn format_reset_time(timestamp_ms: i64) -> Option { + let reset_dt = Local.timestamp_millis_opt(timestamp_ms).single()?; + let now = Local::now(); + let is_today = reset_dt.date_naive() == now.date_naive(); -fn format_duration(seconds: i64) -> Option { - if seconds < 0 { - return None; + if is_today { + // Same day: show time only (e.g., "9:56 PM") + Some(reset_dt.format("%-I:%M %p").to_string()) + } else { + // Different day: show date + weekday + time (e.g., "Feb 2, Sun 9:56 PM") + Some(reset_dt.format("%b %-d, %a %-I:%M %p").to_string()) } - let clamped = seconds.max(0) as i64; - let hours = clamped / 3600; - let minutes = (clamped % 3600) / 60; - if hours == 0 && minutes == 0 { - return Some("0m".to_string()); - } - if hours == 0 { - return Some(format!("{}m", minutes)); - } - if minutes == 0 { - return Some(format!("{}h", hours)); - } - Some(format!("{}h {}m", hours, minutes)) } fn calculate_reset_after_seconds(reset_at: Option) -> Option { @@ -188,8 +178,7 @@ fn calculate_reset_after_seconds(reset_at: Option) -> Option { fn to_usage_window(used_percent: Option, window_seconds: Option, reset_at: Option) -> UsageWindow { let remaining_percent = used_percent.map(|value| (100.0 - value).max(0.0)); let reset_after_seconds = calculate_reset_after_seconds(reset_at); - let reset_at_formatted = reset_at.and_then(format_reset_at); - let reset_after_formatted = reset_after_seconds.and_then(format_duration); + let reset_formatted = reset_at.and_then(format_reset_time); UsageWindow { used_percent, @@ -197,8 +186,8 @@ fn to_usage_window(used_percent: Option, window_seconds: Option, reset window_seconds, reset_after_seconds, reset_at, - reset_at_formatted, - reset_after_formatted, + reset_at_formatted: reset_formatted.clone(), + reset_after_formatted: reset_formatted, } } diff --git a/packages/vscode/src/quotaProviders.ts b/packages/vscode/src/quotaProviders.ts index 4cbd01b7..fe525917 100644 --- a/packages/vscode/src/quotaProviders.ts +++ b/packages/vscode/src/quotaProviders.ts @@ -152,9 +152,25 @@ const normalizeAuthEntry = (entry: AuthEntry | null) => { return null; }; -const formatResetAt = (timestamp: number) => { +const formatResetTime = (timestamp: number) => { try { - return new Date(timestamp).toLocaleTimeString(undefined, { + const resetDate = new Date(timestamp); + const now = new Date(); + const isToday = resetDate.toDateString() === now.toDateString(); + + if (isToday) { + // Same day: show time only (e.g., "9:56 PM") + return resetDate.toLocaleTimeString(undefined, { + hour: 'numeric', + minute: '2-digit', + }); + } + + // Different day: show date + weekday + time (e.g., "Feb 2, Sun 9:56 PM") + return resetDate.toLocaleString(undefined, { + month: 'short', + day: 'numeric', + weekday: 'short', hour: 'numeric', minute: '2-digit', }); @@ -163,25 +179,6 @@ const formatResetAt = (timestamp: number) => { } }; -const formatDuration = (seconds: number | null) => { - if (typeof seconds !== 'number' || Number.isNaN(seconds)) { - return null; - } - const clamped = Math.max(0, Math.round(seconds)); - const hours = Math.floor(clamped / 3600); - const minutes = Math.floor((clamped % 3600) / 60); - if (hours === 0 && minutes === 0) { - return '0m'; - } - if (hours === 0) { - return `${minutes}m`; - } - if (minutes === 0) { - return `${hours}h`; - } - return `${hours}h ${minutes}m`; -}; - const calculateResetAfterSeconds = (resetAt: number | null) => { if (!resetAt) return null; const delta = Math.floor((resetAt - Date.now()) / 1000); @@ -190,14 +187,15 @@ const calculateResetAfterSeconds = (resetAt: number | null) => { const toUsageWindow = (data: { usedPercent: number | null; windowSeconds: number | null; resetAt: number | null }) => { const resetAfterSeconds = calculateResetAfterSeconds(data.resetAt); + const resetFormatted = data.resetAt ? formatResetTime(data.resetAt) : null; return { usedPercent: data.usedPercent, remainingPercent: data.usedPercent !== null ? Math.max(0, 100 - data.usedPercent) : null, windowSeconds: data.windowSeconds ?? null, resetAfterSeconds, resetAt: data.resetAt, - resetAtFormatted: data.resetAt ? formatResetAt(data.resetAt) : null, - resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null, + resetAtFormatted: resetFormatted, + resetAfterFormatted: resetFormatted, } satisfies UsageWindow; }; diff --git a/packages/web/server/lib/quota-providers.js b/packages/web/server/lib/quota-providers.js index 09cb399d..ab3f50c5 100644 --- a/packages/web/server/lib/quota-providers.js +++ b/packages/web/server/lib/quota-providers.js @@ -46,9 +46,25 @@ const normalizeAuthEntry = (entry) => { return null; }; -const formatResetAt = (timestamp) => { +const formatResetTime = (timestamp) => { try { - return new Date(timestamp).toLocaleTimeString(undefined, { + const resetDate = new Date(timestamp); + const now = new Date(); + const isToday = resetDate.toDateString() === now.toDateString(); + + if (isToday) { + // Same day: show time only (e.g., "9:56 PM") + return resetDate.toLocaleTimeString(undefined, { + hour: 'numeric', + minute: '2-digit' + }); + } + + // Different day: show date + weekday + time (e.g., "Feb 2, Sun 9:56 PM") + return resetDate.toLocaleString(undefined, { + month: 'short', + day: 'numeric', + weekday: 'short', hour: 'numeric', minute: '2-digit' }); @@ -57,25 +73,6 @@ const formatResetAt = (timestamp) => { } }; -const formatDuration = (seconds) => { - if (typeof seconds !== 'number' || Number.isNaN(seconds)) { - return null; - } - const clamped = Math.max(0, Math.round(seconds)); - const hours = Math.floor(clamped / 3600); - const minutes = Math.floor((clamped % 3600) / 60); - if (hours === 0 && minutes === 0) { - return '0m'; - } - if (hours === 0) { - return `${minutes}m`; - } - if (minutes === 0) { - return `${hours}h`; - } - return `${hours}h ${minutes}m`; -}; - const calculateResetAfterSeconds = (resetAt) => { if (!resetAt) return null; const delta = Math.floor((resetAt - Date.now()) / 1000); @@ -84,14 +81,15 @@ const calculateResetAfterSeconds = (resetAt) => { const toUsageWindow = ({ usedPercent, windowSeconds, resetAt }) => { const resetAfterSeconds = calculateResetAfterSeconds(resetAt); + const resetFormatted = resetAt ? formatResetTime(resetAt) : null; return { usedPercent, remainingPercent: usedPercent !== null ? Math.max(0, 100 - usedPercent) : null, windowSeconds: windowSeconds ?? null, resetAfterSeconds, resetAt, - resetAtFormatted: resetAt ? formatResetAt(resetAt) : null, - resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null + resetAtFormatted: resetFormatted, + resetAfterFormatted: resetFormatted }; };