Skip to content

Commit fadcc88

Browse files
committed
Clean up time formatting utils
1 parent 631838c commit fadcc88

File tree

3 files changed

+74
-42
lines changed

3 files changed

+74
-42
lines changed

cli/src/utils/time-format.ts

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,21 @@
1+
import { formatTimeUntil } from '@codebuff/common/util/dates'
2+
13
/**
2-
* Format time until reset in human-readable form
4+
* Format time until reset in human-readable form.
35
* @param resetDate - The date when the quota/resource resets
46
* @returns Human-readable string like "2h 30m" or "45m"
57
*/
68
export const formatResetTime = (resetDate: Date | null): string => {
79
if (!resetDate) return ''
8-
const now = new Date()
9-
const diffMs = resetDate.getTime() - now.getTime()
10-
if (diffMs <= 0) return 'now'
11-
12-
const diffMins = Math.floor(diffMs / (1000 * 60))
13-
const diffHours = Math.floor(diffMins / 60)
14-
const remainingMins = diffMins % 60
15-
16-
if (diffHours > 0) {
17-
return `${diffHours}h ${remainingMins}m`
18-
}
19-
return `${diffMins}m`
10+
return formatTimeUntil(resetDate, { fallback: 'now' })
2011
}
2112

2213
/**
23-
* Format time until reset in human-readable form, including days
14+
* Format time until reset in human-readable form, including days.
2415
* @param resetDate - The date when the quota/resource resets
2516
* @returns Human-readable string like "4d 7h" or "2h 30m"
2617
*/
2718
export const formatResetTimeLong = (resetDate: Date | string | null): string => {
2819
if (!resetDate) return ''
29-
const date = typeof resetDate === 'string' ? new Date(resetDate) : resetDate
30-
const now = new Date()
31-
const diffMs = date.getTime() - now.getTime()
32-
if (diffMs <= 0) return 'now'
33-
34-
const diffMins = Math.floor(diffMs / (1000 * 60))
35-
const diffHours = Math.floor(diffMins / 60)
36-
const diffDays = Math.floor(diffHours / 24)
37-
const remainingHours = diffHours % 24
38-
const remainingMins = diffMins % 60
39-
40-
if (diffDays > 0) {
41-
return `${diffDays}d ${remainingHours}h`
42-
}
43-
if (diffHours > 0) {
44-
return `${diffHours}h ${remainingMins}m`
45-
}
46-
return `${diffMins}m`
20+
return formatTimeUntil(resetDate, { fallback: 'now' })
4721
}

common/src/util/dates.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,67 @@ export const getNextQuotaReset = (referenceDate: Date | null): Date => {
1515
}
1616
return nextMonth
1717
}
18+
19+
export interface FormatTimeUntilOptions {
20+
/**
21+
* What to return when the date is in the past or invalid.
22+
* @default 'now'
23+
*/
24+
fallback?: string
25+
/**
26+
* Whether to include the smaller unit (hours in "Xd Yh", minutes in "Xh Ym").
27+
* @default true
28+
*/
29+
includeSubUnit?: boolean
30+
}
31+
32+
/**
33+
* Format the time until a future date in a human-readable string.
34+
*
35+
* @param date - The target date (Date object or ISO string)
36+
* @param options - Formatting options
37+
* @returns Human-readable string like "4d 7h", "2h 30m", or "45m"
38+
*
39+
* @example
40+
* // Date 2 days and 5 hours in the future
41+
* formatTimeUntil(futureDate) // "2d 5h"
42+
* formatTimeUntil(futureDate, { includeSubUnit: false }) // "2d"
43+
*
44+
* // Date 3 hours and 20 minutes in the future
45+
* formatTimeUntil(futureDate) // "3h 20m"
46+
*
47+
* // Date in the past
48+
* formatTimeUntil(pastDate) // "now"
49+
* formatTimeUntil(pastDate, { fallback: '0h' }) // "0h"
50+
*/
51+
export const formatTimeUntil = (
52+
date: Date | string | null,
53+
options: FormatTimeUntilOptions = {},
54+
): string => {
55+
const { fallback = 'now', includeSubUnit = true } = options
56+
57+
if (!date) return fallback
58+
59+
const target = typeof date === 'string' ? new Date(date) : date
60+
const diffMs = target.getTime() - Date.now()
61+
62+
if (isNaN(diffMs) || diffMs <= 0) return fallback
63+
64+
const diffMins = Math.floor(diffMs / (1000 * 60))
65+
const diffHours = Math.floor(diffMins / 60)
66+
const diffDays = Math.floor(diffHours / 24)
67+
const remainingHours = diffHours % 24
68+
const remainingMins = diffMins % 60
69+
70+
if (diffDays > 0) {
71+
return includeSubUnit && remainingHours > 0
72+
? `${diffDays}d ${remainingHours}h`
73+
: `${diffDays}d`
74+
}
75+
if (diffHours > 0) {
76+
return includeSubUnit && remainingMins > 0
77+
? `${diffHours}h ${remainingMins}m`
78+
: `${diffHours}h`
79+
}
80+
return `${diffMins}m`
81+
}

web/src/app/profile/components/subscription-section.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,15 @@ import { Button } from '@/components/ui/button'
1515
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
1616
import { cn } from '@/lib/utils'
1717

18+
import { formatTimeUntil } from '@codebuff/common/util/dates'
19+
1820
import type {
1921
SubscriptionResponse,
2022
ActiveSubscriptionResponse,
2123
} from '@codebuff/common/types/subscription'
2224

23-
function formatDaysHours(dateStr: string): string {
24-
const target = new Date(dateStr)
25-
const diffMs = target.getTime() - Date.now()
26-
if (isNaN(diffMs) || diffMs <= 0) return '0h'
27-
const totalHours = Math.ceil(diffMs / (1000 * 60 * 60))
28-
const days = Math.floor(totalHours / 24)
29-
const hours = totalHours % 24
30-
if (days > 0) return hours > 0 ? `${days}d ${hours}h` : `${days}d`
31-
return `${hours}h`
32-
}
25+
const formatDaysHours = (dateStr: string): string =>
26+
formatTimeUntil(dateStr, { fallback: '0h' })
3327

3428
function ProgressBar({ percentAvailable, label }: { percentAvailable: number; label: string }) {
3529
const percent = Math.min(100, Math.max(0, Math.round(percentAvailable)))

0 commit comments

Comments
 (0)