UNPKG

@formatjs/ecma402-abstract

Version:

A collection of implementation for ECMAScript abstract operations

72 lines (71 loc) 2.77 kB
// Cached regex patterns for performance const OFFSET_TIMEZONE_PREFIX_REGEX = /^[+-]/; const OFFSET_TIMEZONE_FORMAT_REGEX = /^([+-])(\d{2})(?::?(\d{2}))?(?::?(\d{2}))?(?:\.(\d{1,9}))?$/; /** * IsTimeZoneOffsetString ( offsetString ) * https://tc39.es/ecma262/#sec-istimezoneoffsetstring * * Validates whether a string represents a valid UTC offset timezone. * Supports formats: ±HH, ±HHMM, ±HH:MM, ±HH:MM:SS, ±HH:MM:SS.sss * * @param offsetString - The string to validate as a timezone offset * @returns true if offsetString is a valid UTC offset format */ function IsTimeZoneOffsetString(offsetString) { // 1. If offsetString does not start with '+' or '-', return false if (!OFFSET_TIMEZONE_PREFIX_REGEX.test(offsetString)) { return false; } // 2. Let parseResult be ParseText(offsetString, UTCOffset) const match = OFFSET_TIMEZONE_FORMAT_REGEX.exec(offsetString); // 3. If parseResult is a List of errors, return false if (!match) { return false; } // 4. Validate component ranges per ECMA-262 grammar // Hour must be 0-23, Minute must be 0-59, Second must be 0-59 const hours = parseInt(match[2], 10); const minutes = match[3] ? parseInt(match[3], 10) : 0; const seconds = match[4] ? parseInt(match[4], 10) : 0; if (hours > 23 || minutes > 59 || seconds > 59) { return false; } // 5. Return true return true; } /** * IsValidTimeZoneName ( timeZone ) * https://tc39.es/ecma402/#sec-isvalidtimezonename * * Extended to support UTC offset time zones per ECMA-402 PR #788 (ES2026). * The abstract operation validates both: * 1. UTC offset identifiers (e.g., "+01:00", "-05:30") * 2. Available named time zone identifiers from IANA Time Zone Database * * @param tz - The timezone identifier to validate * @param implDetails - Implementation details containing timezone data * @returns true if timeZone is a valid identifier */ export function IsValidTimeZoneName(tz, { zoneNamesFromData, uppercaseLinks }) { // 1. If IsTimeZoneOffsetString(timeZone) is true, return true // Per ECMA-402 PR #788, UTC offset identifiers are valid if (IsTimeZoneOffsetString(tz)) { return true; } // 2. Let timeZones be AvailableNamedTimeZoneIdentifiers() // 3. If timeZones contains an element equal to timeZone, return true // NOTE: Implementation uses case-insensitive comparison per spec note const uppercasedTz = tz.toUpperCase(); const zoneNames = new Set(); const linkNames = new Set(); zoneNamesFromData.map((z) => z.toUpperCase()).forEach((z) => zoneNames.add(z)); Object.keys(uppercaseLinks).forEach((linkName) => { linkNames.add(linkName.toUpperCase()); zoneNames.add(uppercaseLinks[linkName].toUpperCase()); }); if (zoneNames.has(uppercasedTz) || linkNames.has(uppercasedTz)) { return true; } // 4. Return false return false; }