/**
* Centralized date / time formatting utilities .
*
* All formatters are timezone - aware , using Intl . DateTimeFormat .
* Consolidates duplicated formatUtcTimestamp / formatZonedTimestamp / resolveExplicitTimezone
* that previously lived in envelope . ts and session - updates . ts .
*/
/**
* Validate an IANA timezone string . Returns the string if valid , undefined otherwise .
*/
export function resolveTimezone(value: string): string | undefined {
try {
new Intl.DateTimeFormat("en-US" , { timeZone: value }).format(new Date());
return value;
} catch {
return undefined;
}
}
export type FormatTimestampOptions = {
/** Include seconds in the output. Default: false */
displaySeconds?: boolean ;
};
export type FormatZonedTimestampOptions = FormatTimestampOptions & {
/** IANA timezone string (e.g., 'America/New_York'). Default: system timezone */
timeZone?: string;
};
/**
* Format a Date as a UTC timestamp string .
*
* Without seconds : ` 2024 - 01 - 15 T14 : 30 Z `
* With seconds : ` 2024 - 01 - 15 T14 : 30 : 05 Z `
*/
export function formatUtcTimestamp(date: Date, options?: FormatTimestampOptions): string {
const yyyy = String(date.getUTCFullYear()).padStart(4 , "0" );
const mm = String(date.getUTCMonth() + 1 ).padStart(2 , "0" );
const dd = String(date.getUTCDate()).padStart(2 , "0" );
const hh = String(date.getUTCHours()).padStart(2 , "0" );
const min = String(date.getUTCMinutes()).padStart(2 , "0" );
if (!options?.displaySeconds) {
return `${yyyy}-${mm}-${dd}T${hh}:${min}Z`;
}
const sec = String(date.getUTCSeconds()).padStart(2 , "0" );
return `${yyyy}-${mm}-${dd}T${hh}:${min}:${sec}Z`;
}
/**
* Format a Date with timezone display using Intl . DateTimeFormat .
*
* Without seconds : ` 2024 - 01 - 15 14 : 30 EST `
* With seconds : ` 2024 - 01 - 15 14 : 30 : 05 EST `
*
* Returns undefined if Intl formatting fails .
*/
export function formatZonedTimestamp(
date: Date,
options?: FormatZonedTimestampOptions,
): string | undefined {
try {
const intlOptions: Intl.DateTimeFormatOptions = {
timeZone: options?.timeZone,
year: "numeric" ,
month: "2-digit" ,
day: "2-digit" ,
hour: "2-digit" ,
minute: "2-digit" ,
hourCycle: "h23" ,
timeZoneName: "short" ,
};
if (options?.displaySeconds) {
intlOptions.second = "2-digit" ;
}
const parts = new Intl.DateTimeFormat("en-US" , intlOptions).formatToParts(date);
const pick = (type: string) => parts.find((part) => part.type === type)?.value;
const yyyy = pick("year" );
const mm = pick("month" );
const dd = pick("day" );
const hh = pick("hour" );
const min = pick("minute" );
const sec = options?.displaySeconds ? pick("second" ) : undefined;
const tz = [...parts]
.toReversed()
.find((part) => part.type === "timeZoneName" )
?.value?.trim();
if (!yyyy || !mm || !dd || !hh || !min) {
return undefined;
}
if (options?.displaySeconds && sec) {
return `${yyyy}-${mm}-${dd} ${hh}:${min}:${sec}${tz ? ` ${tz}` : "" }`;
}
return `${yyyy}-${mm}-${dd} ${hh}:${min}${tz ? ` ${tz}` : "" }`;
} catch {
return undefined;
}
}
Messung V0.5 in Prozent C=97 H=96 G=96
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland