index.ts 1.5 KB

123456789101112131415161718192021222324252627282930313233
  1. /**
  2. * Time vocabulary shared by the wire boundaries that accept a caller's zone.
  3. * Validation and canonicalization only: this library formats nothing and owns
  4. * no failure vocabulary — each boundary declares and throws its own refusal.
  5. * @module @deepseek-ai/dsh-util-time
  6. */
  7. /** Strict browser-zone profile: UTC or an IANA Area/Location-style identifier. */
  8. const IANA_TIME_ZONE = /^[A-Za-z][A-Za-z0-9_+.-]*(?:\/[A-Za-z0-9_+.-]+)+$/
  9. /**
  10. * Validate and canonicalize one caller-supplied IANA zone at a wire boundary.
  11. *
  12. * The canonical name is what a later reader needs: a zone identity is stored on
  13. * durable records and resolved again by another process, so an alias accepted
  14. * here would not compare equal to the zone a reader derives.
  15. * @param value - the caller's reported zone name.
  16. * @returns the canonical zone, or `undefined` when the name is unusable.
  17. */
  18. export function canonicalClientTimeZone(value: string): string | undefined {
  19. if (value.length === 0 || value.trim() !== value
  20. || (value !== 'UTC' && !IANA_TIME_ZONE.test(value))) return undefined
  21. try {
  22. const canonical = new Intl.DateTimeFormat('en-US', { timeZone: value })
  23. .resolvedOptions().timeZone
  24. /* v8 ignore next -- Intl returns UTC or a canonical IANA Area/Location for accepted input. */
  25. if (canonical !== 'UTC' && !IANA_TIME_ZONE.test(canonical)) return undefined
  26. return canonical
  27. } catch {
  28. // Intl rejects unsupported zone names; the caller maps that parser rejection.
  29. return undefined
  30. }
  31. }