mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
132 lines
4.4 KiB
TypeScript
132 lines
4.4 KiB
TypeScript
/**
|
|
* Checks if a value is a valid number (not NaN, null, undefined, or Infinity)
|
|
*/
|
|
function isValidNumber(value: unknown): value is number {
|
|
return typeof value === 'number' && Number.isFinite(value);
|
|
}
|
|
|
|
const UnitLabels = [
|
|
{
|
|
singular: 'hour',
|
|
plural: 'hours',
|
|
},
|
|
{
|
|
singular: 'minute',
|
|
plural: 'minutes',
|
|
},
|
|
{
|
|
singular: 'second',
|
|
plural: 'seconds',
|
|
},
|
|
] as const;
|
|
|
|
function toTimeUnitPhrase(timeUnitValue: number, unitIndex: number): string {
|
|
const unitLabel = timeUnitValue === 1 ? UnitLabels[unitIndex]?.singular : UnitLabels[unitIndex]?.plural;
|
|
|
|
return `${timeUnitValue} ${unitLabel}`;
|
|
}
|
|
|
|
/**
|
|
* Converts numeric seconds into a human-readable phrase for accessibility.
|
|
*
|
|
* @param seconds - A (positive or negative) time, represented as seconds
|
|
* @returns The time, represented as a phrase of hours, minutes, and seconds
|
|
*
|
|
* @example
|
|
* formatAsTimePhrase(3661) // "1 hour, 1 minute, 1 second"
|
|
* formatAsTimePhrase(90) // "1 minute, 30 seconds"
|
|
* formatAsTimePhrase(-30) // "30 seconds remaining"
|
|
*/
|
|
export function formatAsTimePhrase(seconds: number): string {
|
|
if (!isValidNumber(seconds)) return '';
|
|
|
|
const positiveSeconds = Math.abs(seconds);
|
|
const negative = positiveSeconds !== seconds;
|
|
const secondsDateTime = new Date(0, 0, 0, 0, 0, positiveSeconds, 0);
|
|
const timeParts = [secondsDateTime.getHours(), secondsDateTime.getMinutes(), secondsDateTime.getSeconds()];
|
|
|
|
const timeString = timeParts
|
|
// Convert non-0 values to a string of the value plus its unit
|
|
.map((timeUnitValue, index) => timeUnitValue && toTimeUnitPhrase(timeUnitValue, index))
|
|
// Ignore/exclude any 0 values
|
|
.filter(x => x)
|
|
// join into a single comma-separated string phrase
|
|
.join(', ');
|
|
|
|
// If the time was negative, assume it represents some remaining amount of time/"count down".
|
|
const negativeSuffix = negative ? ' remaining' : '';
|
|
|
|
return `${timeString}${negativeSuffix}`;
|
|
}
|
|
|
|
/**
|
|
* Converts a time, in numeric seconds, to a formatted string representation
|
|
* of the form [HH:[MM:]]SS, where hours and minutes are optional, either
|
|
* based on the value of `seconds` or (optionally) based on the value of `guide`.
|
|
*
|
|
* @param seconds - The total time you'd like formatted, in seconds
|
|
* @param guide - A number in seconds that represents how many units you'd want
|
|
* to show. This ensures consistent formatting between e.g. 35s and 4834s.
|
|
* @returns A string representation of the time, with expected units
|
|
*
|
|
* @example
|
|
* formatTime(90) // "1:30"
|
|
* formatTime(3661) // "1:01:01"
|
|
* formatTime(35, 3600) // "0:35" (guided by 1-hour duration)
|
|
* formatTime(NaN) // "0:00"
|
|
* formatTime(Infinity) // "0:00"
|
|
*/
|
|
export function formatTime(seconds: number, guide?: number): string {
|
|
// Handle negative values
|
|
let negative = false;
|
|
|
|
if (seconds < 0) {
|
|
negative = true;
|
|
seconds = 0 - seconds;
|
|
}
|
|
|
|
seconds = seconds < 0 ? 0 : seconds;
|
|
|
|
let s: number | string = Math.floor(seconds % 60);
|
|
let m: number | string = Math.floor((seconds / 60) % 60);
|
|
let h: number | string = Math.floor(seconds / 3600);
|
|
|
|
const gm = guide ? Math.floor((guide / 60) % 60) : 0;
|
|
const gh = guide ? Math.floor(guide / 3600) : 0;
|
|
|
|
// Handle invalid times
|
|
if (Number.isNaN(seconds) || seconds === Infinity) {
|
|
// '-' is false for all relational operators (e.g. <, >=) so this setting
|
|
// will add the minimum number of fields specified by the guide
|
|
h = m = s = '0';
|
|
}
|
|
|
|
// Check if we need to show hours
|
|
const showHours = (h as number) > 0 || gh > 0;
|
|
const hoursString = showHours ? `${h}:` : '';
|
|
|
|
// If hours are showing, we may need to add a leading zero.
|
|
// Always show at least one digit of minutes.
|
|
const minutesString = `${(showHours || gm >= 10) && (m as number) < 10 ? `0${m}` : m}:`;
|
|
|
|
// Check if leading zero is needed for seconds
|
|
const secondsString = (s as number) < 10 ? `0${s}` : s;
|
|
|
|
return (negative ? '-' : '') + hoursString + minutesString + secondsString;
|
|
}
|
|
|
|
/**
|
|
* Formats a time value with fallback handling for invalid values.
|
|
*
|
|
* @param time - The time value to format in seconds (duration, currentTime, etc.)
|
|
* @param guide - Optional guide time for consistent formatting
|
|
* @param fallback - Fallback text when time is invalid (default: "--:--")
|
|
* @returns Formatted time string or fallback
|
|
*/
|
|
export function formatDisplayTime(time: unknown, guide?: number, fallback: string = '--:--'): string {
|
|
if (!isValidNumber(time)) {
|
|
return fallback;
|
|
}
|
|
return formatTime(time, guide);
|
|
}
|