Refactors date and file formatting utilities

refactor(utils): consolidate date and file size formatting

Extracts formatting logic into dedicated utility functions to improve
code maintainability and consistency across components.

Changes:
- Consolidate date formatting functions (formatRelativeTime, etc.)
- Consolidate file size formatting into single formatFileSize utility
- Fix file size calculation errors
- Update UI components to use new utilities
- Change "ASAP" to "Waiting" for unscheduled queue items

Tests:
- Add comprehensive unit tests for date utilities
- Add unit tests for file size formatting
This commit is contained in:
AneoPsy
2026-02-17 01:15:33 -10:00
parent a4fad2d2f5
commit b477fbf46d
19 changed files with 652 additions and 122 deletions
+61
View File
@@ -424,3 +424,64 @@ export function formatDuration(seconds: number | null | undefined): string {
return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
}
type TranslateFunction = (key: string, options?: Record<string, unknown>) => string;
/**
* Format a date string as a human-readable relative time expression.
*
* @param dateStr - UTC date string, or null
* @param timeFormat - Time format preference ('12h', '24h', or 'system')
* @param t - Optional translation function for i18n support
* @returns Relative string (e.g., "5m ago", "in 2h", "3d ago") or formatted date if older than 7 days
*/
export function formatRelativeTime(
dateStr: string | null,
timeFormat: TimeFormat = 'system',
t?: TranslateFunction
): string {
if (!dateStr) return t?.('time.unknown') ?? '-';
const date = parseUTCDate(dateStr);
if (!date) return t?.('time.unknown') ?? '-';
const now = new Date();
const diffMs = date.getTime() - now.getTime();
const isPast = diffMs < 0;
const absDiffMs = Math.abs(diffMs);
const minutes = Math.floor(absDiffMs / 60000);
const hours = Math.floor(absDiffMs / 3600000);
const days = Math.floor(absDiffMs / 86400000);
// Less than 1 minute
if (minutes < 1) {
return isPast
? t?.('time.justNow') ?? 'Just now'
: t?.('time.now') ?? 'Now';
}
// Less than 1 hour
if (hours < 1) {
return isPast
? t?.('time.minsAgo', { count: minutes }) ?? `${minutes}m ago`
: t?.('time.inMins', { count: minutes }) ?? `in ${minutes}m`;
}
// Less than 1 day
if (days < 1) {
return isPast
? t?.('time.hoursAgo', { count: hours }) ?? `${hours}h ago`
: t?.('time.inHours', { count: hours }) ?? `in ${hours}h`;
}
// Less than 7 days
if (days < 7) {
return isPast
? t?.('time.daysAgo', { count: days }) ?? `${days}d ago`
: t?.('time.inDays', { count: days }) ?? `in ${days}d`;
}
// Older than 7 days
return formatDateTime(dateStr, timeFormat);
}
+21
View File
@@ -0,0 +1,21 @@
/**
* Formats a byte count into a human-readable string (e.g. `1.5 MB`).
*
* @param bytes - The number of bytes to format.
* @returns A formatted string with the appropriate unit (B, KB, MB, GB, or TB).
*/
export function formatFileSize(bytes: number): string {
if (bytes === 0) return '0 B';
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
const k = 1024;
const i = Math.floor(Math.log(bytes) / Math.log(k));
const size = bytes / Math.pow(k, i);
// No decimals for bytes, 1 decimal for larger units
return i === 0
? `${size} ${units[i]}`
: `${size.toFixed(1)} ${units[i]}`;
}