{"version":3,"file":"format.mjs","names":[],"sources":["../../../../package/common/calendar/format.ts"],"sourcesContent":["import { CalendarView, type CalendarRange } from './calendar.types';\nimport { addDays, NARROW_WEEK, type CalendarPeriod } from './dates';\n\nconst formatters = new Map<string, Intl.DateTimeFormat>();\nlet zone: string | undefined;\n\n/**\n * Read once per task: reading the zone costs as much as building a formatter. Its name, not its\n * offsets, because Casablanca and Brazzaville share January's and July's but not March's.\n */\nfunction currentZone(): string {\n  if (zone === undefined) {\n    zone = new Intl.DateTimeFormat().resolvedOptions().timeZone;\n    queueMicrotask(() => {\n      zone = undefined;\n    });\n  }\n  return zone;\n}\n\n/**\n * Cached, since building one costs about fifty times more than using it. A formatter keeps the\n * time zone it was built in while `Date` follows the current one, so the key carries the zone: a\n * zone change (the device's, or `process.env.TZ` between tests) then builds a new formatter\n * instead of labelling days in the old zone.\n */\nexport function formatter(locale: string | undefined, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n  const key = `${currentZone()}|${locale ?? ''}|${JSON.stringify(options)}`;\n  let cached = formatters.get(key);\n  if (!cached) {\n    cached = new Intl.DateTimeFormat(locale, options);\n    formatters.set(key, cached);\n  }\n  return cached;\n}\n\n// ES2020's lib predates `formatRange`, which every supported browser has had since 2021.\nfunction formatRange(format: Intl.DateTimeFormat, start: Date, end: Date): string {\n  return (format as Intl.DateTimeFormat & { formatRange(a: Date, b: Date): string }).formatRange(start, end);\n}\n\nfunction uses12Hour(locale: string | undefined): boolean {\n  // `hourCycle` is missing from ES2020's lib typings, not from any supported browser.\n  const options = formatter(locale, { hour: 'numeric' }).resolvedOptions() as { hourCycle?: string };\n  const cycle = options.hourCycle;\n  return cycle === 'h11' || cycle === 'h12';\n}\n\nexport function formatTime(date: Date, locale: string | undefined): string {\n  return formatter(locale, { hour: 'numeric', minute: '2-digit' }).format(date);\n}\n\n/** A time span, with the dates added by Intl where the two ends fall on different days. */\nexport function formatTimeRange(start: Date, end: Date, locale: string | undefined): string {\n  return formatRange(formatter(locale, { hour: 'numeric', minute: '2-digit' }), start, end);\n}\n\n/**\n * The clock times alone, `10:00 PM to 2:00 AM` as the locale writes it, where `formatTimeRange` would add both dates for\n * an event that crosses midnight. Both ends are moved onto one day for it. The first spec of\n * `formatRange` threw a RangeError for an end earlier than the start; an engine still doing that\n * gets the start time alone.\n */\nexport function formatClockRange(start: Date, end: Date, locale: string | undefined): string {\n  const from = new Date(2000, 0, 1, start.getHours(), start.getMinutes());\n  const to = new Date(2000, 0, 1, end.getHours(), end.getMinutes());\n  try {\n    return formatRange(formatter(locale, { hour: 'numeric', minute: '2-digit' }), from, to);\n  } catch {\n    return formatTime(start, locale);\n  }\n}\n\n/** `9 AM` where the locale counts to twelve, `09:00` where it counts to 24. */\nexport function formatHour(hour: number, locale: string | undefined): string {\n  const date = new Date(2000, 0, 1, hour);\n  return uses12Hour(locale)\n    ? formatter(locale, { hour: 'numeric' }).format(date)\n    : formatter(locale, { hour: '2-digit', minute: '2-digit' }).format(date);\n}\n\nexport function formatWeekday(date: Date, locale: string | undefined, width: 'long' | 'short' | 'narrow'): string {\n  return formatter(locale, { weekday: width }).format(date);\n}\n\n/** The day of the month in the locale's own digits. */\nexport function formatDayNumber(date: Date, locale: string | undefined): string {\n  return formatter(locale, { day: 'numeric' }).format(date);\n}\n\nexport function formatLongDate(date: Date, locale: string | undefined): string {\n  return formatter(locale, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }).format(date);\n}\n\nexport function formatShortDate(date: Date, locale: string | undefined): string {\n  return formatter(locale, { weekday: 'short', day: 'numeric', month: 'short' }).format(date);\n}\n\nexport function formatMonthTitle(date: Date, locale: string | undefined): string {\n  return formatter(locale, { month: 'long', year: 'numeric' }).format(date);\n}\n\nexport function formatPeriodTitle(\n  period: CalendarPeriod,\n  date: Date,\n  range: CalendarRange,\n  locale: string | undefined,\n): string {\n  switch (period) {\n    case CalendarView.Week:\n    case NARROW_WEEK:\n      return formatRange(\n        formatter(locale, { day: 'numeric', month: 'short', year: 'numeric' }),\n        range.start,\n        addDays(range.end, -1),\n      );\n    case CalendarView.Day:\n      return formatLongDate(date, locale);\n    default:\n      return formatMonthTitle(date, locale);\n  }\n}\n"],"mappings":"wHAGA,IAAM,EAAa,IAAI,IACnB,EAMJ,SAAS,GAAsB,CAO7B,OANI,IAAS,IAAA,KACX,EAAO,IAAI,KAAK,eAAe,CAAC,CAAC,gBAAgB,CAAC,CAAC,SACnD,mBAAqB,CACnB,EAAO,IAAA,EACT,CAAC,GAEI,CACT,CAQA,SAAgB,EAAU,EAA4B,EAA0D,CAC9G,IAAM,EAAM,GAAG,EAAY,EAAE,GAAG,GAAU,GAAG,GAAG,KAAK,UAAU,CAAO,IAClE,EAAS,EAAW,IAAI,CAAG,EAK/B,OAJK,IACH,EAAS,IAAI,KAAK,eAAe,EAAQ,CAAO,EAChD,EAAW,IAAI,EAAK,CAAM,GAErB,CACT,CAGA,SAAS,EAAY,EAA6B,EAAa,EAAmB,CAChF,OAAQ,EAA2E,YAAY,EAAO,CAAG,CAC3G,CAEA,SAAS,EAAW,EAAqC,CAGvD,IAAM,EADU,EAAU,EAAQ,CAAE,KAAM,SAAU,CAAC,CAAC,CAAC,gBACzC,CAAA,CAAQ,UACtB,OAAO,IAAU,OAAS,IAAU,KACtC,CAEA,SAAgB,EAAW,EAAY,EAAoC,CACzE,OAAO,EAAU,EAAQ,CAAE,KAAM,UAAW,OAAQ,SAAU,CAAC,CAAC,CAAC,OAAO,CAAI,CAC9E,CAGA,SAAgB,EAAgB,EAAa,EAAW,EAAoC,CAC1F,OAAO,EAAY,EAAU,EAAQ,CAAE,KAAM,UAAW,OAAQ,SAAU,CAAC,EAAG,EAAO,CAAG,CAC1F,CAQA,SAAgB,EAAiB,EAAa,EAAW,EAAoC,CAC3F,IAAM,EAAO,IAAI,KAAK,IAAM,EAAG,EAAG,EAAM,SAAS,EAAG,EAAM,WAAW,CAAC,EAChE,EAAK,IAAI,KAAK,IAAM,EAAG,EAAG,EAAI,SAAS,EAAG,EAAI,WAAW,CAAC,EAChE,GAAI,CACF,OAAO,EAAY,EAAU,EAAQ,CAAE,KAAM,UAAW,OAAQ,SAAU,CAAC,EAAG,EAAM,CAAE,CACxF,MAAQ,CACN,OAAO,EAAW,EAAO,CAAM,CACjC,CACF,CAGA,SAAgB,EAAW,EAAc,EAAoC,CAC3E,IAAM,EAAO,IAAI,KAAK,IAAM,EAAG,EAAG,CAAI,EACtC,OAAO,EAAW,CAAM,EACpB,EAAU,EAAQ,CAAE,KAAM,SAAU,CAAC,CAAC,CAAC,OAAO,CAAI,EAClD,EAAU,EAAQ,CAAE,KAAM,UAAW,OAAQ,SAAU,CAAC,CAAC,CAAC,OAAO,CAAI,CAC3E,CAEA,SAAgB,EAAc,EAAY,EAA4B,EAA4C,CAChH,OAAO,EAAU,EAAQ,CAAE,QAAS,CAAM,CAAC,CAAC,CAAC,OAAO,CAAI,CAC1D,CAGA,SAAgB,EAAgB,EAAY,EAAoC,CAC9E,OAAO,EAAU,EAAQ,CAAE,IAAK,SAAU,CAAC,CAAC,CAAC,OAAO,CAAI,CAC1D,CAEA,SAAgB,EAAe,EAAY,EAAoC,CAC7E,OAAO,EAAU,EAAQ,CAAE,QAAS,OAAQ,IAAK,UAAW,MAAO,OAAQ,KAAM,SAAU,CAAC,CAAC,CAAC,OAAO,CAAI,CAC3G,CAMA,SAAgB,EAAiB,EAAY,EAAoC,CAC/E,OAAO,EAAU,EAAQ,CAAE,MAAO,OAAQ,KAAM,SAAU,CAAC,CAAC,CAAC,OAAO,CAAI,CAC1E,CAEA,SAAgB,EACd,EACA,EACA,EACA,EACQ,CACR,OAAQ,EAAR,CACE,KAAK,EAAa,KAClB,KAAK,EACH,OAAO,EACL,EAAU,EAAQ,CAAE,IAAK,UAAW,MAAO,QAAS,KAAM,SAAU,CAAC,EACrE,EAAM,MACN,EAAQ,EAAM,IAAK,EAAE,CACvB,EACF,KAAK,EAAa,IAChB,OAAO,EAAe,EAAM,CAAM,EACpC,QACE,OAAO,EAAiB,EAAM,CAAM,CACxC,CACF"}