UNPKG

@typespec/http-server-js

Version:

TypeSpec HTTP server code generator for JavaScript

236 lines (210 loc) 7.12 kB
// Copyright (c) Microsoft Corporation // Licensed under the MIT license. // #region Duration /** * Regular expression for matching ISO8601 duration strings. * * Yields: * - 0: the full match * - 1: the sign (optional) * - 2: years (optional) * - 3: months (optional) * - 4: weeks (optional) * - 5: days (optional) * - 6: hours (optional) * - 7: minutes (optional) * - 8: seconds (optional) */ const ISO8601_DURATION_REGEX = /^(-)?P(?:((?:\d*[.,])?\d+)Y)?(?:((?:\d*[.,])?\d+)M)?(?:((?:\d*[.,])?\d+)W)?(?:((?:\d*[.,])?\d+)D)?(?:T(?:((?:\d*[.,])?\d+)H)?(?:((?:\d*[.,])?\d+)M)?(?:((?:\d*[.,])?\d+)S)?)?$/; /** * A duration of time, measured in years, months, weeks, days, hours, minutes, and seconds. * * The values may be fractional and are not normalized (e.g. 36 hours is not the same duration as 1 day and 12 hours * when accounting for Daylight Saving Time changes or leap seconds). * * @see https://en.wikipedia.org/wiki/ISO_8601#Durations */ export interface Duration { /** * "+" if the duration is positive, "-" if the duration is negative. */ sign: "+" | "-"; /** * The number of years in the duration. */ years: number; /** * The number of months in the duration. */ months: number; /** * The number of weeks in the duration. */ weeks: number; /** * The number of days in the duration. */ days: number; /** * The number of hours in the duration. */ hours: number; /** * The number of minutes in the duration. */ minutes: number; /** * The number of seconds in the duration. */ seconds: number; } export const Duration = Object.freeze({ /** * Parses an ISO8601 duration string into an object. * * @see https://en.wikipedia.org/wiki/ISO_8601#Durations * * @param duration - the duration string to parse * @returns an object containing the parsed duration */ parseISO8601(duration: string, maxLength: number = 100): Duration { duration = duration.trim(); if (duration.length > maxLength) throw new Error(`ISO8601 duration string is too long: ${duration}`); const match = duration.match(ISO8601_DURATION_REGEX); if (!match) throw new Error(`Invalid ISO8601 duration: ${duration}`); return { sign: match[1] === undefined ? "+" : (match[1] as Duration["sign"]), years: parseFloatNormal(match[2]), months: parseFloatNormal(match[3]), weeks: parseFloatNormal(match[4]), days: parseFloatNormal(match[5]), hours: parseFloatNormal(match[6]), minutes: parseFloatNormal(match[7]), seconds: parseFloatNormal(match[8]), }; function parseFloatNormal(match: string | undefined): number { if (match === undefined) return 0; const normalized = match.replace(",", "."); const parsed = parseFloat(normalized); if (isNaN(parsed)) throw new Error(`Unreachable: Invalid number in ISO8601 duration string: ${match}`); return parsed; } }, /** * Writes a Duration to an ISO8601 duration string. * * @see https://en.wikipedia.org/wiki/ISO_8601#Durations * * @param duration - the duration to write to a string * @returns a string in ISO8601 duration format */ toISO8601(duration: Duration): string { const sign = duration.sign === "+" ? "" : "-"; const years = duration.years !== 0 && !isNaN(Number(duration.years)) ? `${duration.years}Y` : ""; const months = duration.months !== 0 && !isNaN(Number(duration.months)) ? `${duration.months}M` : ""; const weeks = duration.weeks !== 0 && !isNaN(Number(duration.weeks)) ? `${duration.weeks}W` : ""; const days = duration.days !== 0 && !isNaN(Number(duration.days)) ? `${duration.days}D` : ""; let time = ""; const _hours = duration.hours !== 0 && !isNaN(Number(duration.hours)); const _minutes = duration.minutes !== 0 && !isNaN(Number(duration.minutes)); const _seconds = duration.seconds !== 0 && !isNaN(Number(duration.seconds)); if (_hours || _minutes || _seconds) { const hours = _hours ? `${duration.hours}H` : ""; const minutes = _minutes ? `${duration.minutes}M` : ""; const seconds = _seconds ? `${duration.seconds}S` : ""; time = `T${hours}${minutes}${seconds}`; } return `${sign}P${years}${months}${weeks}${days}${time}`; }, /** * Gets the total number of seconds in a duration. * * This method will throw an Error if the duration contains any years, months, weeks, or days, as those require a reference * point to calculate the total number of seconds. * * WARNING: If the total number of seconds is larger than the maximum safe integer in JavaScript, this method will * lose precision. @see Duration.totalSecondsBigInt for a BigInt alternative. * * @param duration - the duration to calculate the total number of seconds for * @returns the total number of seconds in the duration */ totalSeconds(duration: Duration): number { if ( duration.years !== 0 || duration.months !== 0 || duration.weeks !== 0 || duration.days !== 0 ) { throw new Error( "Cannot calculate total seconds for a duration with years, months, weeks, or days.", ); } return ( duration.seconds + duration.minutes * 60 + duration.hours * 60 * 60 + duration.weeks * 7 * 24 * 60 * 60 ); }, /** * Gets the total number of seconds in a duration. * * This method will throw an Error if the duration contains any years, months, weeks, or days, as those require a reference * point to calculate the total number of seconds. It will also throw an error if any of the components are not integers. * * @param duration - the duration to calculate the total number of seconds for * @returns the total number of seconds in the duration */ totalSecondsBigInt(duration: Duration): bigint { if ( duration.years !== 0 || duration.months !== 0 || duration.weeks !== 0 || duration.days !== 0 ) { throw new Error( "Cannot calculate total seconds for a duration with years, months, weeks, or days.", ); } if ( !Number.isInteger(duration.seconds) || !Number.isInteger(duration.minutes) || !Number.isInteger(duration.hours) || !Number.isInteger(duration.weeks) ) { throw new Error( "Cannot calculate total seconds as a BigInt for a duration with non-integer components.", ); } return ( BigInt(duration.seconds) + BigInt(duration.minutes) * 60n + BigInt(duration.hours) * 60n * 60n + BigInt(duration.weeks) * 7n * 24n * 60n * 60n ); }, /** * Creates a duration from a total number of seconds. * * The result is not normalized, so it will only contain a seconds field. */ fromTotalSeconds(seconds: number): Duration { return { sign: seconds < 0 ? "-" : "+", years: 0, months: 0, weeks: 0, days: 0, hours: 0, minutes: 0, seconds: Math.abs(seconds), }; }, }); // #endregion