refactor(quota): unify reset time formatting across providers
This commit is contained in:
@@ -154,28 +154,18 @@ fn normalize_auth_entry(value: Option<&Value>) -> Option<AuthEntry> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_reset_at(timestamp_ms: i64) -> Option<String> {
|
fn format_reset_time(timestamp_ms: i64) -> Option<String> {
|
||||||
let dt = Local.timestamp_millis_opt(timestamp_ms).single()?;
|
let reset_dt = Local.timestamp_millis_opt(timestamp_ms).single()?;
|
||||||
Some(dt.format("%-I:%M %p").to_string())
|
let now = Local::now();
|
||||||
}
|
let is_today = reset_dt.date_naive() == now.date_naive();
|
||||||
|
|
||||||
fn format_duration(seconds: i64) -> Option<String> {
|
if is_today {
|
||||||
if seconds < 0 {
|
// Same day: show time only (e.g., "9:56 PM")
|
||||||
return None;
|
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<i64>) -> Option<i64> {
|
fn calculate_reset_after_seconds(reset_at: Option<i64>) -> Option<i64> {
|
||||||
@@ -188,8 +178,7 @@ fn calculate_reset_after_seconds(reset_at: Option<i64>) -> Option<i64> {
|
|||||||
fn to_usage_window(used_percent: Option<f64>, window_seconds: Option<i64>, reset_at: Option<i64>) -> UsageWindow {
|
fn to_usage_window(used_percent: Option<f64>, window_seconds: Option<i64>, reset_at: Option<i64>) -> UsageWindow {
|
||||||
let remaining_percent = used_percent.map(|value| (100.0 - value).max(0.0));
|
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_after_seconds = calculate_reset_after_seconds(reset_at);
|
||||||
let reset_at_formatted = reset_at.and_then(format_reset_at);
|
let reset_formatted = reset_at.and_then(format_reset_time);
|
||||||
let reset_after_formatted = reset_after_seconds.and_then(format_duration);
|
|
||||||
|
|
||||||
UsageWindow {
|
UsageWindow {
|
||||||
used_percent,
|
used_percent,
|
||||||
@@ -197,8 +186,8 @@ fn to_usage_window(used_percent: Option<f64>, window_seconds: Option<i64>, reset
|
|||||||
window_seconds,
|
window_seconds,
|
||||||
reset_after_seconds,
|
reset_after_seconds,
|
||||||
reset_at,
|
reset_at,
|
||||||
reset_at_formatted,
|
reset_at_formatted: reset_formatted.clone(),
|
||||||
reset_after_formatted,
|
reset_after_formatted: reset_formatted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -152,9 +152,25 @@ const normalizeAuthEntry = (entry: AuthEntry | null) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatResetAt = (timestamp: number) => {
|
const formatResetTime = (timestamp: number) => {
|
||||||
try {
|
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',
|
hour: 'numeric',
|
||||||
minute: '2-digit',
|
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) => {
|
const calculateResetAfterSeconds = (resetAt: number | null) => {
|
||||||
if (!resetAt) return null;
|
if (!resetAt) return null;
|
||||||
const delta = Math.floor((resetAt - Date.now()) / 1000);
|
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 toUsageWindow = (data: { usedPercent: number | null; windowSeconds: number | null; resetAt: number | null }) => {
|
||||||
const resetAfterSeconds = calculateResetAfterSeconds(data.resetAt);
|
const resetAfterSeconds = calculateResetAfterSeconds(data.resetAt);
|
||||||
|
const resetFormatted = data.resetAt ? formatResetTime(data.resetAt) : null;
|
||||||
return {
|
return {
|
||||||
usedPercent: data.usedPercent,
|
usedPercent: data.usedPercent,
|
||||||
remainingPercent: data.usedPercent !== null ? Math.max(0, 100 - data.usedPercent) : null,
|
remainingPercent: data.usedPercent !== null ? Math.max(0, 100 - data.usedPercent) : null,
|
||||||
windowSeconds: data.windowSeconds ?? null,
|
windowSeconds: data.windowSeconds ?? null,
|
||||||
resetAfterSeconds,
|
resetAfterSeconds,
|
||||||
resetAt: data.resetAt,
|
resetAt: data.resetAt,
|
||||||
resetAtFormatted: data.resetAt ? formatResetAt(data.resetAt) : null,
|
resetAtFormatted: resetFormatted,
|
||||||
resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null,
|
resetAfterFormatted: resetFormatted,
|
||||||
} satisfies UsageWindow;
|
} satisfies UsageWindow;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -46,9 +46,25 @@ const normalizeAuthEntry = (entry) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatResetAt = (timestamp) => {
|
const formatResetTime = (timestamp) => {
|
||||||
try {
|
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',
|
hour: 'numeric',
|
||||||
minute: '2-digit'
|
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) => {
|
const calculateResetAfterSeconds = (resetAt) => {
|
||||||
if (!resetAt) return null;
|
if (!resetAt) return null;
|
||||||
const delta = Math.floor((resetAt - Date.now()) / 1000);
|
const delta = Math.floor((resetAt - Date.now()) / 1000);
|
||||||
@@ -84,14 +81,15 @@ const calculateResetAfterSeconds = (resetAt) => {
|
|||||||
|
|
||||||
const toUsageWindow = ({ usedPercent, windowSeconds, resetAt }) => {
|
const toUsageWindow = ({ usedPercent, windowSeconds, resetAt }) => {
|
||||||
const resetAfterSeconds = calculateResetAfterSeconds(resetAt);
|
const resetAfterSeconds = calculateResetAfterSeconds(resetAt);
|
||||||
|
const resetFormatted = resetAt ? formatResetTime(resetAt) : null;
|
||||||
return {
|
return {
|
||||||
usedPercent,
|
usedPercent,
|
||||||
remainingPercent: usedPercent !== null ? Math.max(0, 100 - usedPercent) : null,
|
remainingPercent: usedPercent !== null ? Math.max(0, 100 - usedPercent) : null,
|
||||||
windowSeconds: windowSeconds ?? null,
|
windowSeconds: windowSeconds ?? null,
|
||||||
resetAfterSeconds,
|
resetAfterSeconds,
|
||||||
resetAt,
|
resetAt,
|
||||||
resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
|
resetAtFormatted: resetFormatted,
|
||||||
resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
|
resetAfterFormatted: resetFormatted
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user