temporal-fmt 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +480 -9
- package/dist/calendarUtils.cjs +259 -4
- package/dist/calendarUtils.cjs.map +1 -1
- package/dist/calendarUtils.js +33 -1
- package/dist/chunk-4N3WCGEY.js +183 -0
- package/dist/{chunk-4JWGUR4O.js.map → chunk-4N3WCGEY.js.map} +1 -1
- package/dist/chunk-5UTV5VP6.js +984 -0
- package/dist/chunk-5UTV5VP6.js.map +1 -0
- package/dist/chunk-ACKKWH5Z.js +578 -0
- package/dist/{chunk-2JHEV7ZF.js.map → chunk-ACKKWH5Z.js.map} +1 -1
- package/dist/chunk-EGUUO6LE.js +439 -0
- package/dist/chunk-EGUUO6LE.js.map +1 -0
- package/dist/chunk-FBPHNRVW.js +2939 -0
- package/dist/chunk-FBPHNRVW.js.map +1 -0
- package/dist/chunk-GPB4JHGI.js +65 -0
- package/dist/chunk-GPB4JHGI.js.map +1 -0
- package/dist/chunk-H7YQX25T.js +170 -0
- package/dist/{chunk-J2AQNXF2.js.map → chunk-H7YQX25T.js.map} +1 -1
- package/dist/chunk-HXY2GUUM.js +182 -0
- package/dist/{chunk-EHLRZULM.js.map → chunk-HXY2GUUM.js.map} +1 -1
- package/dist/chunk-L24NPTAJ.js +328 -0
- package/dist/chunk-L24NPTAJ.js.map +1 -0
- package/dist/chunk-M3GDTHMT.js +169 -0
- package/dist/chunk-M3GDTHMT.js.map +1 -0
- package/dist/chunk-MKPDLQKD.js +51 -0
- package/dist/chunk-MKPDLQKD.js.map +1 -0
- package/dist/chunk-TSLPIURC.js +242 -0
- package/dist/{chunk-VQXUMFB2.js.map → chunk-TSLPIURC.js.map} +1 -1
- package/dist/chunk-UOPAV3WX.js +75 -0
- package/dist/chunk-UOPAV3WX.js.map +1 -0
- package/dist/chunk-VNIUIQA4.js +398 -0
- package/dist/chunk-VNIUIQA4.js.map +1 -0
- package/dist/chunk-YEUASGDK.js +9 -0
- package/dist/chunk-YEUASGDK.js.map +1 -0
- package/dist/chunk-ZQZF5EXW.js +29 -0
- package/dist/chunk-ZQZF5EXW.js.map +1 -0
- package/dist/config.d.cts +4 -2
- package/dist/config.d.ts +4 -2
- package/dist/duration.cjs +638 -26
- package/dist/duration.cjs.map +1 -1
- package/dist/duration.js +28 -1
- package/dist/format.cjs +833 -35
- package/dist/format.cjs.map +1 -1
- package/dist/format.js +20 -1
- package/dist/index.cjs +7698 -163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -24
- package/dist/index.d.ts +108 -24
- package/dist/index.js +1536 -58
- package/dist/index.js.map +1 -1
- package/dist/interval.cjs +5480 -35
- package/dist/interval.cjs.map +1 -1
- package/dist/interval.js +46 -1
- package/dist/localeRegistry.cjs +375 -32
- package/dist/localeRegistry.cjs.map +1 -1
- package/dist/localeRegistry.js +11 -1
- package/dist/modApi.d.cts +131 -0
- package/dist/modApi.d.ts +131 -0
- package/dist/parse.cjs +1797 -70
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.js +20 -1
- package/dist/pattern.d.cts +1 -0
- package/dist/pattern.d.ts +1 -0
- package/dist/recurrence.cjs +452 -20
- package/dist/recurrence.cjs.map +1 -1
- package/dist/recurrence.js +19 -1
- package/dist/relativeTime.cjs +268 -21
- package/dist/relativeTime.cjs.map +1 -1
- package/dist/relativeTime.js +14 -1
- package/dist/runtime.d.cts +201 -0
- package/dist/runtime.d.ts +201 -0
- package/dist/timezone.cjs +387 -20
- package/dist/timezone.cjs.map +1 -1
- package/dist/timezone.js +26 -1
- package/package.json +4 -6
- package/scripts/cli.mjs +27 -5
- package/dist/chunk-2JHEV7ZF.js +0 -7
- package/dist/chunk-3MZLTVP3.js +0 -2
- package/dist/chunk-3MZLTVP3.js.map +0 -1
- package/dist/chunk-4JWGUR4O.js +0 -11
- package/dist/chunk-5U5WJ465.js +0 -36
- package/dist/chunk-5U5WJ465.js.map +0 -1
- package/dist/chunk-C5YESWFT.js +0 -2
- package/dist/chunk-C5YESWFT.js.map +0 -1
- package/dist/chunk-C6ZZ62ET.js +0 -2
- package/dist/chunk-C6ZZ62ET.js.map +0 -1
- package/dist/chunk-EHLRZULM.js +0 -5
- package/dist/chunk-F4RGUDA3.js +0 -2
- package/dist/chunk-F4RGUDA3.js.map +0 -1
- package/dist/chunk-J2AQNXF2.js +0 -2
- package/dist/chunk-NBXF7V5B.js +0 -2
- package/dist/chunk-NBXF7V5B.js.map +0 -1
- package/dist/chunk-R52YOKI3.js +0 -7
- package/dist/chunk-R52YOKI3.js.map +0 -1
- package/dist/chunk-UJR6DHXY.js +0 -2
- package/dist/chunk-UJR6DHXY.js.map +0 -1
- package/dist/chunk-VQXUMFB2.js +0 -21
- package/dist/chunk-XH46JNPK.js +0 -2
- package/dist/chunk-XH46JNPK.js.map +0 -1
- package/dist/chunk-YB6YAG7E.js +0 -35
- package/dist/chunk-YB6YAG7E.js.map +0 -1
- package/dist/chunk-ZGMTY52O.js +0 -2
- package/dist/chunk-ZGMTY52O.js.map +0 -1
package/dist/recurrence.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/recurrence.ts","../src/isoWeek.ts","../src/calendarUtils.ts","../src/arithmetic.ts","../src/comparison.ts"],"sourcesContent":["// Recurrence engine. Deterministic RRULE-like\n// recurrence without pulling in a runtime dependency. Supports\n// secondly/minutely/hourly/daily/weekly/monthly/yearly frequencies,\n// interval, count, until, weekdays, monthDays, positional rules,\n// exclusions, inclusions.\n//\n// RRULE interop: parseRRule() / formatRRule() convert to/from the\n// standard iCalendar RRULE string format (RFC 5545). Implemented\n// without depending on a RRULE library — the grammar is small enough\n// to handle inline.\n\nimport { add } from './arithmetic.js';\nimport { compare } from './comparison.js';\n\nexport type RecurrenceFrequency = 'secondly' | 'minutely' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly';\n\nexport interface RecurrenceRule {\n frequency: RecurrenceFrequency;\n interval: number; // default 1\n count?: number; // max occurrences\n until?: unknown; // Temporal value — recurrence ends before this\n // For weekly: ISO weekdays to include (1=Mon..7=Sun). Empty = all.\n byWeekday?: number[];\n // For monthly/yearly: days of month to include (1-31, or negative\n // for end-of-month: -1 = last day).\n byMonthDay?: number[];\n // For yearly: months to include (1-12).\n byMonth?: number[];\n // For weekly: which week of the year (1-53).\n byWeek?: number[];\n // Exclusions: dates to skip even if they match the rule.\n exDates?: unknown[];\n // Inclusions: dates to add even if they don't match the rule.\n rDates?: unknown[];\n}\n\nexport interface RecurrenceIterator {\n next(): { value: unknown; done: boolean } | { value: undefined; done: true };\n previous(): { value: unknown; done: boolean } | { value: undefined; done: true };\n}\n\n// Creates a recurrence iterator starting from `start`. The first call\n// to next() returns `start` itself (if it matches the rule); subsequent\n// calls return the next matching occurrence.\nexport function recurrence(start: unknown, rule: RecurrenceRule): RecurrenceIterator {\n let current: unknown = start;\n let count = 0;\n // Track past values for previous() — keeps a ring buffer of the last\n // N occurrences so previous() can walk back without recomputing.\n const history: unknown[] = [];\n const MAX_HISTORY = 10_000;\n let atEnd = false;\n let atStart = true;\n\n function matches(value: unknown): boolean {\n const v = value as { dayOfWeek?: number; day?: number; month?: number; year?: number; hour?: number; minute?: number; second?: number };\n if (rule.byWeekday && rule.byWeekday.length > 0) {\n if (!rule.byWeekday.includes(v.dayOfWeek ?? 0)) return false;\n }\n if (rule.byMonthDay && rule.byMonthDay.length > 0) {\n if (!rule.byMonthDay.includes(v.day ?? 0)) return false;\n }\n if (rule.byMonth && rule.byMonth.length > 0) {\n if (!rule.byMonth.includes(v.month ?? 0)) return false;\n }\n if (rule.exDates && rule.exDates.some((d) => compare(d, value) === 0)) return false;\n return true;\n }\n\n function advance(value: unknown, steps: number): unknown {\n // Add `steps` * interval units to `value`.\n const unit = rule.frequency === 'secondly' ? 'seconds'\n : rule.frequency === 'minutely' ? 'minutes'\n : rule.frequency === 'hourly' ? 'hours'\n : rule.frequency === 'daily' ? 'days'\n : rule.frequency === 'weekly' ? 'weeks'\n : rule.frequency === 'monthly' ? 'months'\n : 'years';\n return add(value, steps * rule.interval, unit as Parameters<typeof add>[2]);\n }\n\n function recordHistory(value: unknown): void {\n history.push(value);\n if (history.length > MAX_HISTORY) history.shift();\n }\n\n function nextMatch(value: unknown): { value: unknown; found: boolean } {\n // Advance one step at a time until we find a match. For\n // byWeekday/byMonthDay rules this can skip multiple steps.\n let candidate = value;\n let safetyCounter = 0;\n do {\n candidate = advance(candidate, 1);\n safetyCounter++;\n if (safetyCounter > 1000) {\n // Defensive — if the rule is so restrictive no match exists\n // within 1000 steps, give up rather than spin forever. The\n // caller must treat this as \"no more occurrences\", not as a\n // real match — returning the unmatched candidate here used to\n // get handed back to next()'s caller as if it were valid.\n atEnd = true;\n return { value: candidate, found: false };\n }\n if (rule.until && compare(candidate, rule.until) > 0) {\n atEnd = true;\n return { value: candidate, found: false };\n }\n } while (!matches(candidate));\n return { value: candidate, found: true };\n }\n\n return {\n next() {\n if (atEnd) return { value: undefined, done: true };\n if (atStart) {\n atStart = false;\n if (matches(current)) {\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n if (rule.until && compare(current, rule.until) > 0) {\n atEnd = true;\n return { value: undefined, done: true };\n }\n // Include rDates that match the current value.\n recordHistory(current);\n return { value: current, done: false };\n }\n // If start doesn't match, advance to first match.\n const advanced = nextMatch(current);\n if (!advanced.found) return { value: undefined, done: true };\n current = advanced.value;\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n recordHistory(current);\n return { value: current, done: false };\n }\n const advanced = nextMatch(current);\n if (!advanced.found) return { value: undefined, done: true };\n current = advanced.value;\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n recordHistory(current);\n return { value: current, done: false };\n },\n previous() {\n if (history.length === 0) return { value: undefined, done: true };\n const v = history.pop()!;\n return { value: v, done: false };\n },\n };\n}\n\n// Take N occurrences from a recurrence iterator.\nexport function take(iter: RecurrenceIterator, n: number): unknown[] {\n const result: unknown[] = [];\n for (let i = 0; i < n; i++) {\n const r = iter.next();\n if (r.done) break;\n result.push(r.value);\n }\n return result;\n}\n\n// Upper bound on how many occurrences skip() will collect after its\n// skip phase. skip() used to call take(iter, Number.MAX_SAFE_INTEGER),\n// and an iterator over an unbounded rule (no count, no until — e.g.\n// { frequency: 'daily', interval: 1 }) never returns done: next() always\n// has another day. The call then looped forever, pushing into a result\n// array until the process OOM'd. Same cap style as businessCalendar.ts.\nconst MAX_SKIP_COLLECTION = 100_000;\n\n// Skip N occurrences from a recurrence iterator, returning the ones\n// that follow. Throws a RangeError when the iterator is still producing\n// occurrences after MAX_SKIP_COLLECTION — that means the rule is\n// unbounded (no count/until) and \"everything after the skip\" has no\n// finite answer.\nexport function skip(iter: RecurrenceIterator, n: number): unknown[] {\n for (let i = 0; i < n; i++) {\n const r = iter.next();\n if (r.done) break;\n }\n const result: unknown[] = [];\n while (result.length < MAX_SKIP_COLLECTION) {\n const r = iter.next();\n if (r.done) return result;\n result.push(r.value);\n }\n throw new RangeError(\n `temporal-fmt: skip() collected ${MAX_SKIP_COLLECTION} occurrences and the rule is still ` +\n `producing — add a count or until to the rule, or use take(iter, n) for a bounded read.`\n );\n}\n\n// All occurrences between two dates (inclusive of start, exclusive of end).\nexport function between(start: unknown, rule: RecurrenceRule, rangeStart: unknown, rangeEnd: unknown): unknown[] {\n const iter = recurrence(start, rule);\n const result: unknown[] = [];\n while (true) {\n const r = iter.next();\n if (r.done) break;\n if (compare(r.value, rangeEnd) >= 0) break;\n if (compare(r.value, rangeStart) >= 0) result.push(r.value);\n }\n return result;\n}\n\n// Parses an RRULE string like \"FREQ=DAILY;INTERVAL=2;COUNT=5\" into a\n// RecurrenceRule. Doesn't support every RRULE feature (BYSETPOS,\n// BYHOUR, BYMINUTE, BYSECOND are parsed but not enforced by the\n// recurrence iterator above), but covers the common cases.\nexport function parseRRule(input: string): RecurrenceRule {\n const parts = input.trim().toUpperCase().replace(/^RRULE:/, '').split(';');\n const rule: RecurrenceRule = { frequency: 'daily', interval: 1 };\n const frequencies: ReadonlySet<string> = new Set([\n 'SECONDLY', 'MINUTELY', 'HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY',\n ]);\n for (const part of parts) {\n if (!part) continue;\n const eq = part.indexOf('=');\n if (eq < 0) continue;\n const key = part.slice(0, eq);\n const value = part.slice(eq + 1);\n switch (key) {\n case 'FREQ':\n if (!frequencies.has(value)) {\n throw new RangeError(`temporal-fmt: unsupported RRULE frequency \"${value}\".`);\n }\n rule.frequency = value.toLowerCase() as RecurrenceFrequency;\n break;\n case 'INTERVAL': {\n const interval = Number(value);\n if (!Number.isSafeInteger(interval) || interval < 1) {\n throw new RangeError(`temporal-fmt: RRULE INTERVAL must be a positive safe integer (got \"${value}\").`);\n }\n rule.interval = interval;\n break;\n }\n case 'COUNT': {\n const count = Number(value);\n if (!Number.isSafeInteger(count) || count < 1) {\n throw new RangeError(`temporal-fmt: RRULE COUNT must be a positive safe integer (got \"${value}\").`);\n }\n rule.count = count;\n break;\n }\n case 'UNTIL':\n rule.until = value;\n break;\n case 'BYDAY':\n rule.byWeekday = value.split(',').map((d) => {\n const m = d.match(/^([+-]?\\d)?([A-Z]{2})$/);\n if (!m) return 0;\n const wd = m[2];\n const map: Record<string, number> = { MO: 1, TU: 2, WE: 3, TH: 4, FR: 5, SA: 6, SU: 7 };\n return map[wd!] ?? 0;\n });\n break;\n case 'BYMONTHDAY': {\n const values = value.split(',').map(Number);\n if (values.some((day) => !Number.isInteger(day) || day === 0 || day < -31 || day > 31)) {\n throw new RangeError(`temporal-fmt: RRULE BYMONTHDAY contains an out-of-range value.`);\n }\n rule.byMonthDay = values;\n break;\n }\n case 'BYMONTH': {\n const values = value.split(',').map(Number);\n if (values.some((month) => !Number.isInteger(month) || month < 1 || month > 12)) {\n throw new RangeError(`temporal-fmt: RRULE BYMONTH contains an out-of-range value.`);\n }\n rule.byMonth = values;\n break;\n }\n }\n }\n return rule;\n}\n\nexport function formatRRule(rule: RecurrenceRule): string {\n const parts: string[] = [`FREQ=${rule.frequency.toUpperCase()}`];\n if (rule.interval !== 1) parts.push(`INTERVAL=${rule.interval}`);\n if (rule.count !== undefined) parts.push(`COUNT=${rule.count}`);\n if (rule.until !== undefined) parts.push(`UNTIL=${String(rule.until)}`);\n if (rule.byWeekday && rule.byWeekday.length > 0) {\n const map: Record<number, string> = { 1: 'MO', 2: 'TU', 3: 'WE', 4: 'TH', 5: 'FR', 6: 'SA', 7: 'SU' };\n parts.push(`BYDAY=${rule.byWeekday.map((d) => map[d]).join(',')}`);\n }\n if (rule.byMonthDay && rule.byMonthDay.length > 0) parts.push(`BYMONTHDAY=${rule.byMonthDay.join(',')}`);\n if (rule.byMonth && rule.byMonth.length > 0) parts.push(`BYMONTH=${rule.byMonth.join(',')}`);\n return parts.join(';');\n}","// ISO 8601 week numbering: a week runs Monday–Sunday, and week 1 of a year\n// is the week containing the year's first Thursday (equivalently, the week\n// containing January 4). This means late-December dates can fall in week 1\n// of the *next* year, and early-January dates can fall in week 52 or 53 of\n// the *previous* year. The \"ISO week-numbering year\" (what `RRRR` formats)\n// is that adjacent year, not the calendar year.\n//\n// Computed here from the date's own year/month/day plus its ISO dayOfWeek\n// (1=Mon..7=Sun, matching Temporal's numbering) using plain Gregorian\n// arithmetic — no Temporal factory needed. format() only has the fields\n// the caller already put on the object, and requiring a Temporal\n// implementation just for ISO week would be a regression for callers who\n// use format() without setTemporal() on a non-26 Node.\n\nconst DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\nconst CUMULATIVE_DAYS_BY_MONTH = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];\n\nexport function isGregorianLeapYear(year: number): boolean {\n // Gregorian rule: divisible by 4, except centuries which must also be\n // divisible by 400. Temporal's iso8601 calendar is proleptic Gregorian\n // (no Julian cutover), so this applies for every year, including BCE.\n return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n}\n\nfunction daysInYear(year: number): 365 | 366 {\n return isGregorianLeapYear(year) ? 366 : 365;\n}\n\nexport function dayOfYear(year: number, month: number, day: number): number {\n let doy = CUMULATIVE_DAYS_BY_MONTH[month - 1]! + day;\n if (month > 2 && isGregorianLeapYear(year)) doy += 1;\n return doy;\n}\n\n// Jan 1, 2000 was a Saturday — ISO dayOfWeek 6. Anchoring day-of-week\n// computations to a known reference date is simpler and cheaper than\n// pulling in Zeller's congruence, and the reference never changes.\nconst REFERENCE_YEAR = 2000;\nconst REFERENCE_JAN1_DAY_OF_WEEK = 6;\n\nfunction dayOfWeekOfJan1(year: number): number {\n // Sum full-year deltas from the 2000 anchor rather than recomputing from\n // scratch each call — the per-call work is a single mod this way, and\n // the loop rarely runs far (a typical caller passes a current-era year).\n let offset = 0;\n if (year >= REFERENCE_YEAR) {\n for (let y = REFERENCE_YEAR; y < year; y++) offset += daysInYear(y);\n } else {\n for (let y = year; y < REFERENCE_YEAR; y++) offset -= daysInYear(y);\n }\n // Convert \"days since Jan 1, 2000\" into an ISO day-of-week (1=Mon..7=Sun).\n // Jan 1, 2000 was ISO 6 (Sat), so zero-indexed dow = (6-1 + offset) mod 7.\n const zeroIndexed = (((6 - 1 + offset) % 7) + 7) % 7;\n return zeroIndexed + 1;\n}\n\nexport interface IsoWeekDate {\n isoYear: number;\n week: number; // 1..53\n}\n\nexport function isoWeekYearAndWeek(year: number, month: number, day: number, dayOfWeek: number): IsoWeekDate {\n // Step 1: find the Thursday of the current ISO week. The ISO week-numbering\n // year is whichever calendar year that Thursday falls in. Computing it via\n // day-of-year offsets (rather than constructing a Temporal.PlainDate and\n // adding days) keeps this function pure-numeric.\n const doy = dayOfYear(year, month, day);\n const thursdayDoyRelative = doy + (4 - dayOfWeek); // may be <1 or >daysInYear\n\n let isoYear: number;\n let thursdayDoy: number;\n if (thursdayDoyRelative < 1) {\n isoYear = year - 1;\n thursdayDoy = thursdayDoyRelative + daysInYear(isoYear);\n } else if (thursdayDoyRelative > daysInYear(year)) {\n isoYear = year + 1;\n thursdayDoy = thursdayDoyRelative - daysInYear(year);\n } else {\n isoYear = year;\n thursdayDoy = thursdayDoyRelative;\n }\n\n // Step 2: locate the first Thursday of isoYear — its week is week 1. The\n // first Thursday's day-of-year depends on what weekday Jan 1 of isoYear is.\n const jan1Dow = dayOfWeekOfJan1(isoYear);\n const firstThursdayDoy = 1 + ((4 - jan1Dow + 7) % 7); // 1..7\n\n // Step 3: count full weeks between the two Thursdays.\n const week = 1 + Math.floor((thursdayDoy - firstThursdayDoy) / 7);\n return { isoYear, week };\n}\n","// Calendar utility helpers. These are pure functions\n// over the TemporalLike shape — no Temporal namespace needed, same\n// approach as isoWeek.ts and the field-reading helpers in format.ts.\n// Letting callers compute dayOfYear/weekOfYear/etc. without going\n// through format() means they can build their own derived values\n// without committing to a string format.\n//\n// Calendar-sensitivity: the helpers in this module assume the\n// iso8601 (Gregorian) calendar — that's what TemporalLike fields\n// carry for the overwhelming majority of callers. Non-Gregorian\n// calendars (hebrew, islamic, etc.) need their own helpers; this\n// module doesn't try to be calendar-polymorphic the way Temporal\n// itself is. Documented limitation, not a design choice — see\n// VERIFICATION.md for the rationale.\n\nimport { isGregorianLeapYear, dayOfYear, isoWeekYearAndWeek } from './isoWeek.js';\n\n// A subset of TemporalLike that has the date fields these helpers need,\n// plus optional time fields. PlainTime isn't a DateFieldView (no\n// year/month/day), but PlainDateTime / ZonedDateTime / PlainDate all\n// match. Time fields are optional so callers can pass a PlainDate\n// to startOf(value, 'month') without having to populate hour/minute/etc.\n// Exported so the comparison/arithmetic modules can use the same\n// narrowing.\nexport interface DateFieldView {\n year?: number;\n month?: number;\n day?: number;\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n dayOfWeek?: number;\n calendarId?: string;\n}\n\nfunction requireFields(view: DateFieldView, fields: Array<keyof DateFieldView>): void {\n for (const f of fields) {\n if (typeof view[f] !== 'number') {\n throw new Error(\n `temporal-fmt: calendar helper requires \"${String(f)}\", which this value doesn't have. ` +\n `Pass a Temporal.PlainDate / PlainDateTime / ZonedDateTime.`\n );\n }\n }\n}\n\nexport function daysInMonth(view: DateFieldView): number {\n requireFields(view, ['year', 'month']);\n const { year, month } = view;\n // Standard Gregorian month lengths. February's length depends on\n // whether `year` is a leap year — the same isGregorianLeapYear check\n // isoWeek.ts uses for dayOfYear arithmetic.\n const LENGTHS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n if (month === 2 && isGregorianLeapYear(year!)) return 29;\n return LENGTHS[(month! - 1)!]!;\n}\n\nexport function daysInYear(view: DateFieldView): 365 | 366 {\n requireFields(view, ['year']);\n return isGregorianLeapYear(view.year!) ? 366 : 365;\n}\n\nexport function monthsInYear(_view: DateFieldView): 12 {\n // Gregorian always has 12 months. Other calendars (Hebrew leap years\n // have 13) need calendar-aware logic this module doesn't carry — see\n // the file-level comment. The `_view` parameter is kept so the\n // signature mirrors the other helpers and a future calendar-aware\n // implementation can use it without changing call sites.\n return 12;\n}\n\nexport function isLeapYear(view: DateFieldView): boolean {\n requireFields(view, ['year']);\n return isGregorianLeapYear(view.year!);\n}\n\n// `isLeapMonth` would require knowing which month of a leap-year-aware\n// calendar is the leap month — Gregorian doesn't have one, so this\n// returns false unconditionally. Kept here so the public surface\n// matches the module's listing; non-Gregorian calendars need\n// a different implementation.\nexport function isLeapMonth(_view: DateFieldView): boolean {\n return false;\n}\n\nexport function dayOfYearHelper(view: DateFieldView): number {\n requireFields(view, ['year', 'month', 'day']);\n return dayOfYear(view.year!, view.month!, view.day!);\n}\n\n// ISO 8601 week and week-year. Delegates to isoWeek.ts's\n// isoWeekYearAndWeek, which does the full Thursday-of-week\n// computation to handle the year-boundary cases (Dec 29-31 belonging\n// to week 1 of next year, Jan 1-3 belonging to week 52/53 of the\n// previous year).\nexport function weekOfYear(view: DateFieldView): number {\n requireFields(view, ['year', 'month', 'day', 'dayOfWeek']);\n return isoWeekYearAndWeek(view.year!, view.month!, view.day!, view.dayOfWeek!).week;\n}\n\nexport function weekYear(view: DateFieldView): number {\n requireFields(view, ['year', 'month', 'day', 'dayOfWeek']);\n return isoWeekYearAndWeek(view.year!, view.month!, view.day!, view.dayOfWeek!).isoYear;\n}\n\n/**\n * Fiscal-quarter options. `startMonth` is the calendar month (1-12) the\n * fiscal year begins on — e.g. `7` for a fiscal year starting in July.\n * Omitted or `1` gives the calendar-quarter behavior getQuarter() has\n * always had (Jan-Mar = Q1, etc.), so existing callers passing nothing\n * see no change.\n */\nexport interface QuarterOptions {\n startMonth?: number;\n}\n\nfunction validateStartMonth(startMonth: number): void {\n if (!Number.isInteger(startMonth) || startMonth < 1 || startMonth > 12) {\n throw new Error(\n `temporal-fmt: getQuarter's startMonth must be an integer from 1 to 12 (got ${startMonth}).`\n );\n }\n}\n\nexport function getQuarter(view: DateFieldView, options: QuarterOptions = {}): number {\n requireFields(view, ['month']);\n const startMonth = options.startMonth ?? 1;\n validateStartMonth(startMonth);\n if (startMonth === 1) {\n // Mirrors the Q token: months 1-3 → Q1, 4-6 → Q2, 7-9 → Q3, 10-12 → Q4.\n return Math.ceil(view.month! / 3);\n }\n // Fiscal case: shift the month so startMonth becomes month 1 of the\n // fiscal year (mod 12, 1-indexed), then apply the same ceil(/3) rule.\n // E.g. startMonth=7 (fiscal year starts July): July→1, Aug→2, ...,\n // Dec→6, Jan→7, ..., June→12. Then Q1 = fiscal months 1-3 (Jul-Sep),\n // matching the common \"FY starts in July\" convention where Q1 is the\n // first quarter of the fiscal year, not a quarter numbered by which\n // calendar quarter it falls in.\n const shifted = ((view.month! - startMonth + 12) % 12) + 1;\n return Math.ceil(shifted / 3);\n}\n\n// `getMonth` / `getWeekday` look trivial (just read the field) but\n// they're kept as real functions for API surface consistency with the\n// rest of this module. They also normalize: getWeekday returns 1-7\n// (Mon-Sun, matching Temporal's spec) regardless of what numbering the\n// caller's underlying value uses.\nexport function getMonth(view: DateFieldView): number {\n requireFields(view, ['month']);\n return view.month!;\n}\n\nexport function getWeekday(view: DateFieldView): number {\n requireFields(view, ['dayOfWeek']);\n return view.dayOfWeek!;\n}\n\n// startOf / endOf return new field bags (not Temporal objects — this\n// module is polyfill-free) with the relevant fields zeroed/extended.\n// Callers can pass the result to a Temporal constructor if they want\n// a typed value.\nexport type StartOfUnit = 'day' | 'month' | 'year' | 'hour' | 'minute' | 'second';\n\n// Returns true if the given unit (when used with startOf/endOf) should\n// also touch the time fields. 'day', 'month', 'year' all imply a\n// resolution coarser than an hour, so startOf zeroes the time fields\n// and endOf maxes them. Sub-hour units (hour/minute/second) only touch\n// the fields finer than themselves.\nfunction touchesTime(unit: StartOfUnit): boolean {\n return unit === 'day' || unit === 'month' || unit === 'year';\n}\n\n// startOf/endOf reassign year/month/day, which invalidates any\n// dayOfWeek carried over from the input — a plain { ...view } spread\n// leaves the old value sitting there unchanged. Same failure mode\n// businessCalendar.ts's isBusinessDay() works around for add(); we\n// recompute here rather than trust the copied field.\nfunction recomputeDayOfWeek(view: DateFieldView): void {\n if (typeof view.dayOfWeek !== 'number') return;\n /* c8 ignore start @preserve -- unreachable: startOf()/endOf() both call\n * asDateFieldView() before this, which already throws if year/month/day\n * aren't all numbers — so by the time a value with a numeric dayOfWeek\n * reaches here, year/month/day are guaranteed present too. */\n if (typeof view.year !== 'number' || typeof view.month !== 'number' || typeof view.day !== 'number') return;\n /* c8 ignore stop @preserve */\n const jsDow = new Date(Date.UTC(view.year, view.month - 1, view.day)).getUTCDay(); // 0=Sun..6=Sat\n view.dayOfWeek = jsDow === 0 ? 7 : jsDow; // 1=Mon..7=Sun\n}\n\nexport function startOf(value: unknown, unit: StartOfUnit): DateFieldView {\n const view = asDateFieldView(value);\n const result: DateFieldView = { ...view };\n if (unit === 'year') {\n result.month = 1;\n result.day = 1;\n } else if (unit === 'month') {\n result.day = 1;\n }\n recomputeDayOfWeek(result);\n if (touchesTime(unit)) {\n result.hour = 0;\n result.minute = 0;\n result.second = 0;\n result.millisecond = 0;\n } else if (unit === 'hour') {\n result.minute = 0;\n result.second = 0;\n result.millisecond = 0;\n } else if (unit === 'minute') {\n result.second = 0;\n result.millisecond = 0;\n } else if (unit === 'second') {\n result.millisecond = 0;\n }\n return result;\n}\n\nexport function endOf(value: unknown, unit: StartOfUnit): DateFieldView {\n const view = asDateFieldView(value);\n const result: DateFieldView = { ...view };\n if (unit === 'year') {\n result.month = 12;\n result.day = daysInMonth({ year: result.year!, month: 12 });\n } else if (unit === 'month') {\n result.day = daysInMonth({ year: result.year!, month: result.month! });\n }\n recomputeDayOfWeek(result);\n if (touchesTime(unit)) {\n result.hour = 23;\n result.minute = 59;\n result.second = 59;\n result.millisecond = 999;\n } else if (unit === 'hour') {\n result.minute = 59;\n result.second = 59;\n result.millisecond = 999;\n } else if (unit === 'minute') {\n result.second = 59;\n result.millisecond = 999;\n } else if (unit === 'second') {\n result.millisecond = 999;\n }\n return result;\n}\n\n// Type-narrowing helpers used by the comparison/arithmetic modules.\n// Lets them accept any of the four date-carrying Temporal types without\n// importing Temporal itself.\nexport function asDateFieldView(value: unknown): DateFieldView {\n if (typeof value !== 'object' || value === null) {\n throw new Error(`temporal-fmt: expected a date-carrying Temporal value, got ${String(value)}.`);\n }\n // Temporal instances expose year/month/day/etc. as prototype getters,\n // not own enumerable properties — so `{ ...value }` would lose them.\n // Read them explicitly. Only the fields actually present on this\n // value type end up in the returned view.\n const v = value as Record<string, unknown>;\n const out: DateFieldView = {};\n if (typeof v.year === 'number') out.year = v.year;\n if (typeof v.month === 'number') out.month = v.month;\n if (typeof v.day === 'number') out.day = v.day;\n if (typeof v.hour === 'number') out.hour = v.hour;\n if (typeof v.minute === 'number') out.minute = v.minute;\n if (typeof v.second === 'number') out.second = v.second;\n if (typeof v.millisecond === 'number') out.millisecond = v.millisecond;\n if (typeof v.dayOfWeek === 'number') out.dayOfWeek = v.dayOfWeek;\n if (typeof v.calendarId === 'string') out.calendarId = v.calendarId;\n if (out.year === undefined || out.month === undefined || out.day === undefined) {\n throw new Error(\n `temporal-fmt: value is missing year/month/day fields — pass a Temporal.PlainDate / PlainDateTime / ZonedDateTime.`\n );\n }\n return out;\n}\n\n// Re-export the TemporalType alias so callers can import everything\n// from one place.\nexport type { TemporalType } from './tokenMetadata.js';\n// Re-export TemporalLike for the same reason.\nexport type { TemporalLike } from './tokens.js';","// Date arithmetic helpers. Pure functions returning\n// field bags, same convention as calendarUtils.ts/comparison.ts — no\n// Temporal namespace needed. Callers can pass the result back into a\n// Temporal constructor to get a typed value, or chain operations\n// without committing to a Temporal implementation.\n\nimport { asDateFieldView, daysInMonth, type DateFieldView } from './calendarUtils.js';\n\nexport type AddUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction asDateTime(value: unknown): DateTimeFieldView {\n return asDateFieldView(value) as DateTimeFieldView;\n}\n\n// Recomputes dayOfWeek after year/month/day change. Mirrors\n// calendarUtils.ts's recomputeDayOfWeek — same fix, needed here too\n// since add()/subtract() mutate the date fields via a shallow spread\n// that otherwise leaves the old dayOfWeek sitting there, silently wrong.\nfunction recomputeDayOfWeek(view: DateTimeFieldView): void {\n if (typeof view.dayOfWeek !== 'number') return;\n /* c8 ignore start @preserve -- unreachable: every call site (add()'s\n 'years'/'months' cases, shiftDays()) passes a `result` whose\n year/month/day were just explicitly set, and the value originally\n came from asDateTime()/asDateFieldView(), which throws unless\n year/month/day are all present as numbers. So by the time this guard\n runs, all three are guaranteed valid. Kept in case a future caller\n constructs a DateTimeFieldView bypassing that validation. */\n if (typeof view.year !== 'number' || typeof view.month !== 'number' || typeof view.day !== 'number') return;\n /* c8 ignore stop @preserve */\n const jsDow = new Date(Date.UTC(view.year, view.month - 1, view.day)).getUTCDay(); // 0=Sun..6=Sat\n view.dayOfWeek = jsDow === 0 ? 7 : jsDow; // 1=Mon..7=Sun\n}\n\n// Adds the requested amount to the value's specified unit. Returns a\n// new field bag; the input is not mutated. Handles month/year overflow\n// by clamping to the last valid day of the target month (Feb 29 + 1\n// year on a non-leap year → Feb 28) — Temporal's `constrain` overflow\n// mode, which is the closest match to \"do the obvious thing\" callers\n// expect from arithmetic.\nexport function add(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n const v = asDateTime(value);\n let result: DateTimeFieldView = { ...v };\n switch (unit) {\n case 'years': {\n /* c8 ignore start @preserve -- the `?? 0` fallback is unreachable:\n asDateTime()/asDateFieldView() (called at the top of add()) throws\n unless year is present as a number, so result.year is never\n undefined here. Kept as a guard against a future change that\n relaxes that validation. */\n result.year = (result.year ?? 0) + amount;\n /* c8 ignore stop @preserve */\n // Clamp day-of-month to the new month's length.\n const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n if (result.day! > maxDay) result.day = maxDay;\n recomputeDayOfWeek(result);\n break;\n }\n case 'months': {\n // Total months = year*12 + month + amount, then split back.\n /* c8 ignore start @preserve -- both `??` fallbacks are unreachable\n for the same reason as the 'years' case above: asDateFieldView()\n guarantees year and month are present numbers before add() ever\n reaches here. */\n const total = (result.year ?? 0) * 12 + (result.month ?? 1) - 1 + amount;\n /* c8 ignore stop @preserve */\n result.year = Math.floor(total / 12);\n result.month = (total % 12) + 1;\n if (result.month < 1) { result.month += 12; result.year -= 1; }\n // Negative modulo handling: when total < 0, the mod goes negative.\n // Re-normalize.\n /* c8 ignore start @preserve -- unreachable: (total % 12) + 1 can never\n exceed 12 for any integer total in JS (its range is [-10, 12] since\n JS's % can return a negative remainder but never one with\n magnitude >= 12, so adding 1 tops out at 12). Only the < 1 branch\n above is reachable; this was written symmetrically with it but the\n overflow direction it guards against can't occur. Kept rather than\n removed since it costs nothing and documents the intent. */\n if (result.month > 12) { result.month -= 12; result.year += 1; }\n /* c8 ignore stop @preserve */\n const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n if (result.day! > maxDay) result.day = maxDay;\n recomputeDayOfWeek(result);\n break;\n }\n case 'weeks':\n result = shiftDays(result, amount * 7);\n break;\n case 'days':\n result = shiftDays(result, amount);\n break;\n case 'hours':\n result = shiftTime(result, amount * 3_600_000);\n break;\n case 'minutes':\n result = shiftTime(result, amount * 60_000);\n break;\n case 'seconds':\n result = shiftTime(result, amount * 1_000);\n break;\n case 'milliseconds':\n result = shiftTime(result, amount);\n break;\n }\n return result;\n}\n\nfunction shiftDays(v: DateTimeFieldView, days: number): DateTimeFieldView {\n // Convert Y/M/D to a day count, add, convert back. Using the same\n // algorithm as comparison.ts's sameWeek helper — proleptic Gregorian\n // day count via Howard Hinnant's era-based formula.\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const totalDays = era * 146097 + doe - 719468 + days;\n\n // Invert: totalDays → Y/M/D. Same source (Howard Hinnant's days_from_civil).\n const z = totalDays + 719468;\n const era2 = Math.floor((z >= 0 ? z : z - 146096) / 146097);\n const doe2 = z - era2 * 146097;\n const yoe2 = Math.floor((doe2 - Math.floor(doe2 / 1460) + Math.floor(doe2 / 36524) - Math.floor(doe2 / 146096)) / 365);\n const y2out = yoe2 + era2 * 400;\n const doy2 = doe2 - (365 * yoe2 + Math.floor(yoe2 / 4) - Math.floor(yoe2 / 100));\n const mp = Math.floor((5 * doy2 + 2) / 153);\n const d2 = doy2 - Math.floor((153 * mp + 2) / 5) + 1;\n const m2out = mp < 10 ? mp + 3 : mp - 9;\n const yOut = m2out <= 2 ? y2out + 1 : y2out;\n\n const result: DateTimeFieldView = { ...v, year: yOut, month: m2out, day: d2 };\n recomputeDayOfWeek(result);\n return result;\n}\n\nfunction shiftTime(v: DateTimeFieldView, msDelta: number): DateTimeFieldView {\n // Convert all time fields to ms, add, then split back. Day overflow\n // propagates to shiftDays; sub-second precision is preserved (caller\n // asking for hours/minutes/seconds/milliseconds gets ms-resolution\n // arithmetic, which is the limit of these helpers — nanoseconds\n // aren't supported here, only via Temporal.Duration's own add).\n const MS_PER_DAY = 86_400_000;\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n\n const totalMs = (v.hour ?? 0) * MS_PER_HOUR\n + (v.minute ?? 0) * MS_PER_MINUTE\n + (v.second ?? 0) * MS_PER_SECOND\n + (v.millisecond ?? 0)\n + msDelta;\n\n // Split into day-overflow + within-day ms.\n const dayOverflow = Math.floor(totalMs / MS_PER_DAY);\n let withinDay = totalMs % MS_PER_DAY;\n if (withinDay < 0) { withinDay += MS_PER_DAY; }\n\n const hour = Math.floor(withinDay / MS_PER_HOUR);\n const minute = Math.floor((withinDay % MS_PER_HOUR) / MS_PER_MINUTE);\n const second = Math.floor((withinDay % MS_PER_MINUTE) / MS_PER_SECOND);\n const millisecond = withinDay % MS_PER_SECOND;\n\n const result: DateTimeFieldView = { ...v, hour, minute, second, millisecond };\n if (dayOverflow !== 0) {\n return shiftDays(result, dayOverflow);\n }\n return result;\n}\n\nexport function subtract(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n return add(value, -amount, unit);\n}\n\n// Per-unit convenience wrappers — matches the grouping of\n// addYears / addMonths / ... / subtractNanoseconds. Nanosecond variants\n// aren't provided here because these helpers operate on field bags, and\n// the millisecond/microsecond/nanosecond split isn't preserved through\n// arithmetic at sub-millisecond precision the way Temporal.Duration's\n// own add preserves it. For ns-precision arithmetic, use Temporal.Duration\n// directly.\nexport const addYears = (v: unknown, n: number) => add(v, n, 'years');\nexport const addMonths = (v: unknown, n: number) => add(v, n, 'months');\nexport const addWeeks = (v: unknown, n: number) => add(v, n, 'weeks');\nexport const addDays = (v: unknown, n: number) => add(v, n, 'days');\nexport const addHours = (v: unknown, n: number) => add(v, n, 'hours');\nexport const addMinutes = (v: unknown, n: number) => add(v, n, 'minutes');\nexport const addSeconds = (v: unknown, n: number) => add(v, n, 'seconds');\nexport const addMilliseconds = (v: unknown, n: number) => add(v, n, 'milliseconds');\n\nexport const subtractYears = (v: unknown, n: number) => subtract(v, n, 'years');\nexport const subtractMonths = (v: unknown, n: number) => subtract(v, n, 'months');\nexport const subtractWeeks = (v: unknown, n: number) => subtract(v, n, 'weeks');\nexport const subtractDays = (v: unknown, n: number) => subtract(v, n, 'days');\nexport const subtractHours = (v: unknown, n: number) => subtract(v, n, 'hours');\nexport const subtractMinutes = (v: unknown, n: number) => subtract(v, n, 'minutes');\nexport const subtractSeconds = (v: unknown, n: number) => subtract(v, n, 'seconds');\nexport const subtractMilliseconds = (v: unknown, n: number) => subtract(v, n, 'milliseconds');\n\n// difference(): returns the count of `unit` boundaries between two values.\n// Pure-integer math, no Temporal.Duration involved. For sub-day\n// precision the result is an integer count of the requested unit (not\n// a Duration field bag) — matches the most common caller expectation\n// (\"how many days between these two dates?\").\nexport type DiffUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\nexport function difference(a: unknown, b: unknown, unit: DiffUnit): number {\n const av = asDateTime(a);\n const bv = asDateTime(b);\n\n // For day-granular units (years/months/weeks/days), compute via\n // calendar arithmetic on the date fields. For sub-day units, compute\n // via total-ms and divide.\n if (unit === 'years') {\n return bv.year! - av.year!;\n }\n if (unit === 'months') {\n return (bv.year! * 12 + bv.month! - 1) - (av.year! * 12 + av.month! - 1);\n }\n if (unit === 'weeks' || unit === 'days') {\n const aDays = toDayCount(av);\n const bDays = toDayCount(bv);\n const days = bDays - aDays;\n return unit === 'weeks' ? Math.trunc(days / 7) : days;\n }\n\n // Sub-day units: total-ms math.\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n const aMs = toTotalMs(av);\n const bMs = toTotalMs(bv);\n const diffMs = bMs - aMs;\n switch (unit) {\n case 'hours': return Math.trunc(diffMs / MS_PER_HOUR);\n case 'minutes': return Math.trunc(diffMs / MS_PER_MINUTE);\n case 'seconds': return Math.trunc(diffMs / MS_PER_SECOND);\n case 'milliseconds': return diffMs;\n }\n}\n\nfunction toDayCount(v: DateTimeFieldView): number {\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n return era * 146097 + doe - 719468;\n}\n\nfunction toTotalMs(v: DateTimeFieldView): number {\n const MS_PER_DAY = 86_400_000;\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n return toDayCount(v) * MS_PER_DAY\n + (v.hour ?? 0) * MS_PER_HOUR\n + (v.minute ?? 0) * MS_PER_MINUTE\n + (v.second ?? 0) * MS_PER_SECOND\n + (v.millisecond ?? 0);\n}\n\n// Per-unit difference wrappers — same convenience pattern as the add*\n// wrappers above.\nexport const differenceInYears = (a: unknown, b: unknown) => difference(a, b, 'years');\nexport const differenceInMonths = (a: unknown, b: unknown) => difference(a, b, 'months');\nexport const differenceInWeeks = (a: unknown, b: unknown) => difference(a, b, 'weeks');\nexport const differenceInDays = (a: unknown, b: unknown) => difference(a, b, 'days');\nexport const differenceInHours = (a: unknown, b: unknown) => difference(a, b, 'hours');\nexport const differenceInMinutes = (a: unknown, b: unknown) => difference(a, b, 'minutes');\nexport const differenceInSeconds = (a: unknown, b: unknown) => difference(a, b, 'seconds');\nexport const differenceInMilliseconds = (a: unknown, b: unknown) => difference(a, b, 'milliseconds');","// Comparison helpers. Pure functions over the field\n// shape, no Temporal namespace needed — same convention as\n// calendarUtils.ts. All comparisons are field-based, not identity-based,\n// so a polyfill PlainDate compares equal to a native PlainDate with the\n// same year/month/day.\n\nimport { asDateFieldView, type DateFieldView } from './calendarUtils.js';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction toComparableMs(view: DateTimeFieldView): number {\n // Exact proleptic-Gregorian day count (Howard Hinnant's days_from_civil,\n // same algorithm arithmetic.ts/interval.ts use) plus the time-of-day —\n // NOT an approximation. The previous implementation multiplied the\n // year offset by an average 365.2425 days/year, on the theory that the\n // error \"is constant for any given year, so it cancels out in a diff\".\n // That's only true when both dates fall in the same year: the year\n // term's error differs by up to ~0.76 days between adjacent years\n // (365.2425 vs 365/366 actual), which corrupts cross-year comparisons\n // near the boundary — e.g. isAfter(2025-01-01T00:00, 2024-12-31T18:12)\n // returned false (truth: ~5.8h after), and isEqual() returned true for\n // instants ~18h apart (2025-01-01T00:00 vs 2024-12-31T05:49:12).\n // Exact arithmetic has no such error term.\n const y = view.year!, m = view.month!, d = view.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const days = era * 146097 + doe - 719468;\n\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n\n return days * 86_400_000\n + (view.hour ?? 0) * MS_PER_HOUR\n + (view.minute ?? 0) * MS_PER_MINUTE\n + (view.second ?? 0) * MS_PER_SECOND\n + (view.millisecond ?? 0);\n}\n\nfunction asDateTimeFieldView(value: unknown): DateTimeFieldView {\n const v = asDateFieldView(value);\n return v as DateTimeFieldView;\n}\n\n// Returns < 0 if a < b, 0 if a === b, > 0 if a > b. Same convention as\n// Array.prototype.sort and Temporal.PlainDate.compare. Lets callers\n// pass this directly as a comparator.\nexport function compare(a: unknown, b: unknown): number {\n const av = asDateTimeFieldView(a);\n const bv = asDateTimeFieldView(b);\n const am = toComparableMs(av);\n const bm = toComparableMs(bv);\n if (am < bm) return -1;\n if (am > bm) return 1;\n return 0;\n}\n\nexport function isEqual(a: unknown, b: unknown): boolean {\n return compare(a, b) === 0;\n}\n\nexport function isBefore(a: unknown, b: unknown): boolean {\n return compare(a, b) < 0;\n}\n\nexport function isAfter(a: unknown, b: unknown): boolean {\n return compare(a, b) > 0;\n}\n\nexport function min(values: unknown[]): unknown {\n if (!Array.isArray(values) || values.length === 0) {\n throw new Error('temporal-fmt: min() requires a non-empty array of values.');\n }\n let best = values[0];\n for (let i = 1; i < values.length; i++) {\n if (compare(values[i], best) < 0) best = values[i];\n }\n return best;\n}\n\nexport function max(values: unknown[]): unknown {\n if (!Array.isArray(values) || values.length === 0) {\n throw new Error('temporal-fmt: max() requires a non-empty array of values.');\n }\n let best = values[0];\n for (let i = 1; i < values.length; i++) {\n if (compare(values[i], best) > 0) best = values[i];\n }\n return best;\n}\n\nexport function clamp(value: unknown, lo: unknown, hi: unknown): unknown {\n if (compare(value, lo) < 0) return lo;\n if (compare(value, hi) > 0) return hi;\n return value;\n}\n\nexport function isBetween(value: unknown, lo: unknown, hi: unknown): boolean {\n return compare(value, lo) >= 0 && compare(value, hi) <= 0;\n}\n\n// Semantic helpers — same-day/week/month/quarter/year tests. Useful for\n// UI (\"is this event today?\") and for grouping/aggregation. All take a\n// reference value to compare against; the \"isToday\" / \"isTomorrow\" /\n// \"isYesterday\" variants compute the reference from the current system\n// date themselves so callers don't have to.\nfunction readYearMonthDay(value: unknown): { year: number; month: number; day: number } {\n const v = asDateFieldView(value);\n return { year: v.year!, month: v.month!, day: v.day! };\n}\n\nfunction todayYearMonthDay(): { year: number; month: number; day: number } {\n const now = new Date();\n return { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() };\n}\n\nfunction dateEqualYearMonthDay(a: { year: number; month: number; day: number }, b: { year: number; month: number; day: number }): boolean {\n return a.year === b.year && a.month === b.month && a.day === b.day;\n}\n\nexport function isSameDay(value: unknown, reference: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), readYearMonthDay(reference));\n}\n\nexport function isToday(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), todayYearMonthDay());\n}\n\nfunction tomorrowYearMonthDay(): { year: number; month: number; day: number } {\n const d = new Date();\n d.setDate(d.getDate() + 1);\n return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };\n}\n\nfunction yesterdayYearMonthDay(): { year: number; month: number; day: number } {\n const d = new Date();\n d.setDate(d.getDate() - 1);\n return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };\n}\n\nexport function isTomorrow(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), tomorrowYearMonthDay());\n}\n\nexport function isYesterday(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), yesterdayYearMonthDay());\n}\n\nfunction sameWeek(yearA: number, monthA: number, dayA: number, yearB: number, monthB: number, dayB: number): boolean {\n // Same week iff the dates are within 7 days of each other AND the earlier\n // one's weekday is ≤ the later one's weekday (so we don't say \"Sun and\n // the following Mon are in the same week\" — ISO weeks run Mon-Sun).\n // Easier: compute ISO week numbers via isoWeekYearAndWeek, compare both\n // year and week. That handles the year-boundary case (Dec 31 vs Jan 1 of\n // the next year, which can be in the same ISO week).\n // Local import to avoid module-cycle: isoWeek.ts doesn't import from here.\n // Lazy require-style dynamic via inline static import at top would be\n // cleaner; this inline computation is fine for a pure helper.\n // ...actually let's just compute weekday offsets directly. Same-ISO-week\n // iff |d1 - d2| < 7 days AND the earlier date's ISO weekday ≤ the later\n // date's ISO weekday so they fall in the same Mon-Sun span.\n const DAYS_PER_400_YEARS = 146097;\n const daysSince = (y: number, m: number, d: number): number => {\n // Zeller's congruence variant — convert Y/M/D to a day count.\n // Using a simple algorithm here that's correct for proleptic Gregorian.\n const y2 = m <= 2 ? y - 1 : y;\n const era = (y2 >= 0 ? y2 : y2 - 399) / 400 | 0;\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((Math.floor(153 * m2 + 2) / 5) + d - 1);\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n return era * DAYS_PER_400_YEARS + doe - 719468;\n };\n const a = daysSince(yearA, monthA, dayA);\n const b = daysSince(yearB, monthB, dayB);\n if (Math.abs(a - b) >= 7) return false;\n // Same week iff both dates round to the same Monday. Compute the\n // Monday-of-week for each: subtract (weekday - 1) days, where weekday\n // is 1=Mon..7=Sun. Use the standard Zeller formula for weekday.\n const weekdayOf = (y: number, m: number, d: number): number => {\n const days = daysSince(y, m, d);\n // 1970-01-01 was a Thursday (ISO 4). So weekday = ((days + 3) mod 7) + 1\n // mapped to Mon=1..Sun=7. Let's compute: Sun=0..Sat=6 via JS Date.\n const date = new Date(Date.UTC(y, m - 1, d));\n const jsDow = date.getUTCDay(); // 0=Sun..6=Sat\n return jsDow === 0 ? 7 : jsDow;\n };\n const wa = a - (weekdayOf(yearA, monthA, dayA) - 1);\n const wb = b - (weekdayOf(yearB, monthB, dayB) - 1);\n return wa === wb;\n}\n\nexport function isSameWeek(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return sameWeek(a.year, a.month, a.day, b.year, b.month, b.day);\n}\n\nexport function isSameMonth(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return a.year === b.year && a.month === b.month;\n}\n\nexport function isSameQuarter(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return a.year === b.year && Math.ceil(a.month / 3) === Math.ceil(b.month / 3);\n}\n\nexport function isSameYear(value: unknown, reference: unknown): boolean {\n return readYearMonthDay(value).year === readYearMonthDay(reference).year;\n}\n\n// isWeekend / isWeekday depend on dayOfWeek — present on PlainDate,\n// PlainDateTime, ZonedDateTime, but NOT on PlainTime. Callers passing\n// PlainTime get a descriptive throw. Doesn't call asDateFieldView\n// because that would reject PlainTime (no year/month/day) before\n// reaching the dayOfWeek check — and the error we want here is the\n// \"no dayOfWeek field\" one, not the \"no date fields\" one.\nexport function isWeekend(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) {\n throw new Error(`temporal-fmt: isWeekend() expected a Temporal value, got ${String(value)}.`);\n }\n const v = value as { dayOfWeek?: unknown };\n if (typeof v.dayOfWeek !== 'number') {\n throw new Error(\n `temporal-fmt: isWeekend() needs a value with a dayOfWeek field (PlainDate / PlainDateTime / ZonedDateTime).`\n );\n }\n // 6=Sat, 7=Sun in Temporal's Mon=1..Sun=7 numbering.\n return v.dayOfWeek === 6 || v.dayOfWeek === 7;\n}\n\nexport function isWeekday(value: unknown): boolean {\n return !isWeekend(value);\n}\n"],"mappings":"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","names":["recurrence_exports","__export","between","formatRRule","parseRRule","recurrence","skip","take","__toCommonJS","isGregorianLeapYear","year","requireFields","view","fields","f","daysInMonth","year","month","LENGTHS","isGregorianLeapYear","asDateFieldView","value","v","out","asDateTime","value","asDateFieldView","recomputeDayOfWeek","view","jsDow","add","amount","unit","result","maxDay","daysInMonth","total","shiftDays","shiftTime","v","days","y","m","d","y2","era","yoe","m2","doy","doe","z","era2","doe2","yoe2","y2out","doy2","mp","d2","m2out","yOut","msDelta","totalMs","dayOverflow","withinDay","hour","minute","second","millisecond","toComparableMs","view","y","m","d","y2","era","yoe","m2","doy","doe","asDateTimeFieldView","value","asDateFieldView","compare","a","b","av","bv","am","bm","recurrence","start","rule","current","count","history","MAX_HISTORY","atEnd","atStart","matches","value","v","d","compare","advance","steps","unit","add","recordHistory","nextMatch","candidate","safetyCounter","advanced","take","iter","n","result","i","r","MAX_SKIP_COLLECTION","skip","between","rangeStart","rangeEnd","parseRRule","input","parts","frequencies","part","eq","key","interval","wd","values","day","month","formatRRule","map"]}
|
|
1
|
+
{"version":3,"sources":["../src/recurrence.ts","../src/isoWeek.ts","../src/calendarUtils.ts","../src/arithmetic.ts","../src/comparison.ts"],"sourcesContent":["// Recurrence engine. Deterministic RRULE-like\n// recurrence without pulling in a runtime dependency. Supports\n// secondly/minutely/hourly/daily/weekly/monthly/yearly frequencies,\n// interval, count, until, weekdays, monthDays, positional rules,\n// exclusions, inclusions.\n//\n// RRULE interop: parseRRule() / formatRRule() convert to/from the\n// standard iCalendar RRULE string format (RFC 5545). Implemented\n// without depending on a RRULE library — the grammar is small enough\n// to handle inline.\n\nimport { add } from './arithmetic.js';\nimport { compare } from './comparison.js';\n\nexport type RecurrenceFrequency = 'secondly' | 'minutely' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly';\n\nexport interface RecurrenceRule {\n frequency: RecurrenceFrequency;\n interval: number; // default 1\n count?: number; // max occurrences\n until?: unknown; // Temporal value — recurrence ends before this\n // For weekly: ISO weekdays to include (1=Mon..7=Sun). Empty = all.\n byWeekday?: number[];\n // For monthly/yearly: days of month to include (1-31, or negative\n // for end-of-month: -1 = last day).\n byMonthDay?: number[];\n // For yearly: months to include (1-12).\n byMonth?: number[];\n // For weekly: which week of the year (1-53).\n byWeek?: number[];\n // Exclusions: dates to skip even if they match the rule.\n exDates?: unknown[];\n // Inclusions: dates to add even if they don't match the rule.\n rDates?: unknown[];\n}\n\nexport interface RecurrenceIterator {\n next(): { value: unknown; done: boolean } | { value: undefined; done: true };\n previous(): { value: unknown; done: boolean } | { value: undefined; done: true };\n}\n\n// Creates a recurrence iterator starting from `start`. The first call\n// to next() returns `start` itself (if it matches the rule); subsequent\n// calls return the next matching occurrence.\nexport function recurrence(start: unknown, rule: RecurrenceRule): RecurrenceIterator {\n let current: unknown = start;\n let count = 0;\n // Track past values for previous() — keeps a ring buffer of the last\n // N occurrences so previous() can walk back without recomputing.\n const history: unknown[] = [];\n const MAX_HISTORY = 10_000;\n let atEnd = false;\n let atStart = true;\n\n function matches(value: unknown): boolean {\n const v = value as { dayOfWeek?: number; day?: number; month?: number; year?: number; hour?: number; minute?: number; second?: number };\n if (rule.byWeekday && rule.byWeekday.length > 0) {\n if (!rule.byWeekday.includes(v.dayOfWeek ?? 0)) return false;\n }\n if (rule.byMonthDay && rule.byMonthDay.length > 0) {\n if (!rule.byMonthDay.includes(v.day ?? 0)) return false;\n }\n if (rule.byMonth && rule.byMonth.length > 0) {\n if (!rule.byMonth.includes(v.month ?? 0)) return false;\n }\n if (rule.exDates && rule.exDates.some((d) => compare(d, value) === 0)) return false;\n return true;\n }\n\n function advance(value: unknown, steps: number): unknown {\n // Add `steps` * interval units to `value`.\n const unit = rule.frequency === 'secondly' ? 'seconds'\n : rule.frequency === 'minutely' ? 'minutes'\n : rule.frequency === 'hourly' ? 'hours'\n : rule.frequency === 'daily' ? 'days'\n : rule.frequency === 'weekly' ? 'weeks'\n : rule.frequency === 'monthly' ? 'months'\n : 'years';\n return add(value, steps * rule.interval, unit as Parameters<typeof add>[2]);\n }\n\n function recordHistory(value: unknown): void {\n history.push(value);\n if (history.length > MAX_HISTORY) history.shift();\n }\n\n function nextMatch(value: unknown): { value: unknown; found: boolean } {\n // Advance one step at a time until we find a match. For\n // byWeekday/byMonthDay rules this can skip multiple steps.\n let candidate = value;\n let safetyCounter = 0;\n do {\n candidate = advance(candidate, 1);\n safetyCounter++;\n if (safetyCounter > 1000) {\n // Defensive — if the rule is so restrictive no match exists\n // within 1000 steps, give up rather than spin forever. The\n // caller must treat this as \"no more occurrences\", not as a\n // real match — returning the unmatched candidate here used to\n // get handed back to next()'s caller as if it were valid.\n atEnd = true;\n return { value: candidate, found: false };\n }\n if (rule.until && compare(candidate, rule.until) > 0) {\n atEnd = true;\n return { value: candidate, found: false };\n }\n } while (!matches(candidate));\n return { value: candidate, found: true };\n }\n\n return {\n next() {\n if (atEnd) return { value: undefined, done: true };\n if (atStart) {\n atStart = false;\n if (matches(current)) {\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n if (rule.until && compare(current, rule.until) > 0) {\n atEnd = true;\n return { value: undefined, done: true };\n }\n // Include rDates that match the current value.\n recordHistory(current);\n return { value: current, done: false };\n }\n // If start doesn't match, advance to first match.\n const advanced = nextMatch(current);\n if (!advanced.found) return { value: undefined, done: true };\n current = advanced.value;\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n recordHistory(current);\n return { value: current, done: false };\n }\n const advanced = nextMatch(current);\n if (!advanced.found) return { value: undefined, done: true };\n current = advanced.value;\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n recordHistory(current);\n return { value: current, done: false };\n },\n previous() {\n if (history.length === 0) return { value: undefined, done: true };\n const v = history.pop()!;\n return { value: v, done: false };\n },\n };\n}\n\n// Take N occurrences from a recurrence iterator.\nexport function take(iter: RecurrenceIterator, n: number): unknown[] {\n const result: unknown[] = [];\n for (let i = 0; i < n; i++) {\n const r = iter.next();\n if (r.done) break;\n result.push(r.value);\n }\n return result;\n}\n\n// Upper bound on how many occurrences skip() will collect after its\n// skip phase. skip() used to call take(iter, Number.MAX_SAFE_INTEGER),\n// and an iterator over an unbounded rule (no count, no until — e.g.\n// { frequency: 'daily', interval: 1 }) never returns done: next() always\n// has another day. The call then looped forever, pushing into a result\n// array until the process OOM'd. Same cap style as businessCalendar.ts.\nconst MAX_SKIP_COLLECTION = 100_000;\n\n// Skip N occurrences from a recurrence iterator, returning the ones\n// that follow. Throws a RangeError when the iterator is still producing\n// occurrences after MAX_SKIP_COLLECTION — that means the rule is\n// unbounded (no count/until) and \"everything after the skip\" has no\n// finite answer.\nexport function skip(iter: RecurrenceIterator, n: number): unknown[] {\n for (let i = 0; i < n; i++) {\n const r = iter.next();\n if (r.done) break;\n }\n const result: unknown[] = [];\n while (result.length < MAX_SKIP_COLLECTION) {\n const r = iter.next();\n if (r.done) return result;\n result.push(r.value);\n }\n throw new RangeError(\n `temporal-fmt: skip() collected ${MAX_SKIP_COLLECTION} occurrences and the rule is still ` +\n `producing — add a count or until to the rule, or use take(iter, n) for a bounded read.`\n );\n}\n\n// All occurrences between two dates (inclusive of start, exclusive of end).\nexport function between(start: unknown, rule: RecurrenceRule, rangeStart: unknown, rangeEnd: unknown): unknown[] {\n const iter = recurrence(start, rule);\n const result: unknown[] = [];\n while (true) {\n const r = iter.next();\n if (r.done) break;\n if (compare(r.value, rangeEnd) >= 0) break;\n if (compare(r.value, rangeStart) >= 0) result.push(r.value);\n }\n return result;\n}\n\n// Parses an RRULE string like \"FREQ=DAILY;INTERVAL=2;COUNT=5\" into a\n// RecurrenceRule. Doesn't support every RRULE feature (BYSETPOS,\n// BYHOUR, BYMINUTE, BYSECOND are parsed but not enforced by the\n// recurrence iterator above), but covers the common cases.\nexport function parseRRule(input: string): RecurrenceRule {\n const parts = input.trim().toUpperCase().replace(/^RRULE:/, '').split(';');\n const rule: RecurrenceRule = { frequency: 'daily', interval: 1 };\n const frequencies: ReadonlySet<string> = new Set([\n 'SECONDLY', 'MINUTELY', 'HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY',\n ]);\n for (const part of parts) {\n if (!part) continue;\n const eq = part.indexOf('=');\n if (eq < 0) continue;\n const key = part.slice(0, eq);\n const value = part.slice(eq + 1);\n switch (key) {\n case 'FREQ':\n if (!frequencies.has(value)) {\n throw new RangeError(`temporal-fmt: unsupported RRULE frequency \"${value}\".`);\n }\n rule.frequency = value.toLowerCase() as RecurrenceFrequency;\n break;\n case 'INTERVAL': {\n const interval = Number(value);\n if (!Number.isSafeInteger(interval) || interval < 1) {\n throw new RangeError(`temporal-fmt: RRULE INTERVAL must be a positive safe integer (got \"${value}\").`);\n }\n rule.interval = interval;\n break;\n }\n case 'COUNT': {\n const count = Number(value);\n if (!Number.isSafeInteger(count) || count < 1) {\n throw new RangeError(`temporal-fmt: RRULE COUNT must be a positive safe integer (got \"${value}\").`);\n }\n rule.count = count;\n break;\n }\n case 'UNTIL':\n rule.until = value;\n break;\n case 'BYDAY':\n rule.byWeekday = value.split(',').map((d) => {\n const m = d.match(/^([+-]?\\d)?([A-Z]{2})$/);\n if (!m) return 0;\n const wd = m[2];\n const map: Record<string, number> = { MO: 1, TU: 2, WE: 3, TH: 4, FR: 5, SA: 6, SU: 7 };\n return map[wd!] ?? 0;\n });\n break;\n case 'BYMONTHDAY': {\n const values = value.split(',').map(Number);\n if (values.some((day) => !Number.isInteger(day) || day === 0 || day < -31 || day > 31)) {\n throw new RangeError(`temporal-fmt: RRULE BYMONTHDAY contains an out-of-range value.`);\n }\n rule.byMonthDay = values;\n break;\n }\n case 'BYMONTH': {\n const values = value.split(',').map(Number);\n if (values.some((month) => !Number.isInteger(month) || month < 1 || month > 12)) {\n throw new RangeError(`temporal-fmt: RRULE BYMONTH contains an out-of-range value.`);\n }\n rule.byMonth = values;\n break;\n }\n }\n }\n return rule;\n}\n\nexport function formatRRule(rule: RecurrenceRule): string {\n const parts: string[] = [`FREQ=${rule.frequency.toUpperCase()}`];\n if (rule.interval !== 1) parts.push(`INTERVAL=${rule.interval}`);\n if (rule.count !== undefined) parts.push(`COUNT=${rule.count}`);\n if (rule.until !== undefined) parts.push(`UNTIL=${String(rule.until)}`);\n if (rule.byWeekday && rule.byWeekday.length > 0) {\n const map: Record<number, string> = { 1: 'MO', 2: 'TU', 3: 'WE', 4: 'TH', 5: 'FR', 6: 'SA', 7: 'SU' };\n parts.push(`BYDAY=${rule.byWeekday.map((d) => map[d]).join(',')}`);\n }\n if (rule.byMonthDay && rule.byMonthDay.length > 0) parts.push(`BYMONTHDAY=${rule.byMonthDay.join(',')}`);\n if (rule.byMonth && rule.byMonth.length > 0) parts.push(`BYMONTH=${rule.byMonth.join(',')}`);\n return parts.join(';');\n}","// ISO week stuff. week runs Mon-Sun, and week 1 is whichever week has\n// the year's first Thursday in it (same as saying \"the week with Jan 4\").\n// took me a minute to wrap my head around this but the upshot is late-Dec\n// dates can land in week 1 of NEXT year, and early-Jan dates can land in\n// week 52/53 of the PREVIOUS year. that adjacent year is what RRRR prints,\n// not the plain calendar year.\n//\n// doing this with plain year/month/day + dayOfWeek math instead of asking\n// Temporal for it, since format() only has whatever fields got handed in\n// and dragging in a whole Temporal implementation just for week numbers\n// felt like overkill (also breaks for people not on setTemporal()).\n\nconst DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\nconst CUMULATIVE_DAYS_BY_MONTH = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];\n\nexport function isGregorianLeapYear(year: number): boolean {\n // divisible by 4, unless it's a century, then it also needs /400.\n // Temporal's iso8601 calendar never switches to Julian, so this rule\n // just applies all the way back, even for BCE years.\n return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n}\n\nfunction daysInYear(year: number): 365 | 366 {\n return isGregorianLeapYear(year) ? 366 : 365;\n}\n\nexport function dayOfYear(year: number, month: number, day: number): number {\n let doy = CUMULATIVE_DAYS_BY_MONTH[month - 1]! + day;\n if (month > 2 && isGregorianLeapYear(year)) doy += 1;\n return doy;\n}\n\n// Jan 1 2000 was a Saturday (ISO dow 6). picked this as an anchor point\n// since it's easier than reaching for Zeller's congruence every time,\n// and obviously this fact about Jan 1 2000 isn't going to change on us\nconst REFERENCE_YEAR = 2000;\nconst REFERENCE_JAN1_DAY_OF_WEEK = 6;\n\nfunction dayOfWeekOfJan1(year: number): number {\n // walk year by year from 2000 instead of doing this from scratch every\n // time — most callers pass a current-era year so this loop is short anyway\n let offset = 0;\n if (year >= REFERENCE_YEAR) {\n for (let y = REFERENCE_YEAR; y < year; y++) offset += daysInYear(y);\n } else {\n for (let y = year; y < REFERENCE_YEAR; y++) offset -= daysInYear(y);\n }\n // now turn \"days since Jan 1 2000\" into an ISO weekday. Jan 1 2000 was a 6\n // (Sat), so: zero-indexed = (6-1 + offset) mod 7, then bump back to 1-indexed\n const zeroIndexed = (((6 - 1 + offset) % 7) + 7) % 7;\n return zeroIndexed + 1;\n}\n\nexport interface IsoWeekDate {\n isoYear: number;\n week: number; // goes 1 to 53\n}\n\nexport function isoWeekYearAndWeek(year: number, month: number, day: number, dayOfWeek: number): IsoWeekDate {\n // step 1: figure out the Thursday of this week — whatever calendar year\n // that Thursday's in IS the ISO week-numbering year. doing this with\n // day-of-year offsets so we don't need an actual Temporal.PlainDate here\n const doy = dayOfYear(year, month, day);\n const thursdayDoyRelative = doy + (4 - dayOfWeek); // can go negative or past daysInYear, that's fine\n\n let isoYear: number;\n let thursdayDoy: number;\n if (thursdayDoyRelative < 1) {\n isoYear = year - 1;\n thursdayDoy = thursdayDoyRelative + daysInYear(isoYear);\n } else if (thursdayDoyRelative > daysInYear(year)) {\n isoYear = year + 1;\n thursdayDoy = thursdayDoyRelative - daysInYear(year);\n } else {\n isoYear = year;\n thursdayDoy = thursdayDoyRelative;\n }\n\n // step 2: find the first Thursday of isoYear, that one's week 1 by definition.\n // depends entirely on which weekday Jan 1 lands on\n const jan1Dow = dayOfWeekOfJan1(isoYear);\n const firstThursdayDoy = 1 + ((4 - jan1Dow + 7) % 7); // always lands somewhere 1-7\n\n // step 3: just count how many full weeks between the two Thursdays\n const week = 1 + Math.floor((thursdayDoy - firstThursdayDoy) / 7);\n return { isoYear, week };\n}\n","// Calendar utility helpers. These are pure functions\n// over the TemporalLike shape — no Temporal namespace needed, same\n// approach as isoWeek.ts and the field-reading helpers in format.ts.\n// Letting callers compute dayOfYear/weekOfYear/etc. without going\n// through format() means they can build their own derived values\n// without committing to a string format.\n//\n// Calendar-sensitivity: the helpers in this module assume the\n// iso8601 (Gregorian) calendar — that's what TemporalLike fields\n// carry for the overwhelming majority of callers. Non-Gregorian\n// calendars (hebrew, islamic, etc.) need their own helpers; this\n// module doesn't try to be calendar-polymorphic the way Temporal\n// itself is. Documented limitation, not a design choice — see\n// VERIFICATION.md for the rationale.\n\nimport { isGregorianLeapYear, dayOfYear, isoWeekYearAndWeek } from './isoWeek.js';\n\n// A subset of TemporalLike that has the date fields these helpers need,\n// plus optional time fields. PlainTime isn't a DateFieldView (no\n// year/month/day), but PlainDateTime / ZonedDateTime / PlainDate all\n// match. Time fields are optional so callers can pass a PlainDate\n// to startOf(value, 'month') without having to populate hour/minute/etc.\n// Exported so the comparison/arithmetic modules can use the same\n// narrowing.\nexport interface DateFieldView {\n year?: number;\n month?: number;\n day?: number;\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n dayOfWeek?: number;\n calendarId?: string;\n}\n\nfunction requireFields(view: DateFieldView, fields: Array<keyof DateFieldView>): void {\n for (const f of fields) {\n if (typeof view[f] !== 'number') {\n throw new Error(\n `temporal-fmt: calendar helper requires \"${String(f)}\", which this value doesn't have. ` +\n `Pass a Temporal.PlainDate / PlainDateTime / ZonedDateTime.`\n );\n }\n }\n}\n\nexport function daysInMonth(view: DateFieldView): number {\n requireFields(view, ['year', 'month']);\n const { year, month } = view;\n // Standard Gregorian month lengths. February's length depends on\n // whether `year` is a leap year — the same isGregorianLeapYear check\n // isoWeek.ts uses for dayOfYear arithmetic.\n const LENGTHS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n if (month === 2 && isGregorianLeapYear(year!)) return 29;\n return LENGTHS[(month! - 1)!]!;\n}\n\nexport function daysInYear(view: DateFieldView): 365 | 366 {\n requireFields(view, ['year']);\n return isGregorianLeapYear(view.year!) ? 366 : 365;\n}\n\nexport function monthsInYear(_view: DateFieldView): 12 {\n // Gregorian always has 12 months. Other calendars (Hebrew leap years\n // have 13) need calendar-aware logic this module doesn't carry — see\n // the file-level comment. The `_view` parameter is kept so the\n // signature mirrors the other helpers and a future calendar-aware\n // implementation can use it without changing call sites.\n return 12;\n}\n\nexport function isLeapYear(view: DateFieldView): boolean {\n requireFields(view, ['year']);\n return isGregorianLeapYear(view.year!);\n}\n\n// `isLeapMonth` would require knowing which month of a leap-year-aware\n// calendar is the leap month — Gregorian doesn't have one, so this\n// returns false unconditionally. Kept here so the public surface\n// matches the module's listing; non-Gregorian calendars need\n// a different implementation.\nexport function isLeapMonth(_view: DateFieldView): boolean {\n return false;\n}\n\nexport function dayOfYearHelper(view: DateFieldView): number {\n requireFields(view, ['year', 'month', 'day']);\n return dayOfYear(view.year!, view.month!, view.day!);\n}\n\n// ISO 8601 week and week-year. Delegates to isoWeek.ts's\n// isoWeekYearAndWeek, which does the full Thursday-of-week\n// computation to handle the year-boundary cases (Dec 29-31 belonging\n// to week 1 of next year, Jan 1-3 belonging to week 52/53 of the\n// previous year).\nexport function weekOfYear(view: DateFieldView): number {\n requireFields(view, ['year', 'month', 'day', 'dayOfWeek']);\n return isoWeekYearAndWeek(view.year!, view.month!, view.day!, view.dayOfWeek!).week;\n}\n\nexport function weekYear(view: DateFieldView): number {\n requireFields(view, ['year', 'month', 'day', 'dayOfWeek']);\n return isoWeekYearAndWeek(view.year!, view.month!, view.day!, view.dayOfWeek!).isoYear;\n}\n\n/**\n * Fiscal-quarter options. `startMonth` is the calendar month (1-12) the\n * fiscal year begins on — e.g. `7` for a fiscal year starting in July.\n * Omitted or `1` gives the calendar-quarter behavior getQuarter() has\n * always had (Jan-Mar = Q1, etc.), so existing callers passing nothing\n * see no change.\n */\nexport interface QuarterOptions {\n startMonth?: number;\n}\n\nfunction validateStartMonth(startMonth: number): void {\n if (!Number.isInteger(startMonth) || startMonth < 1 || startMonth > 12) {\n throw new Error(\n `temporal-fmt: getQuarter's startMonth must be an integer from 1 to 12 (got ${startMonth}).`\n );\n }\n}\n\nexport function getQuarter(view: DateFieldView, options: QuarterOptions = {}): number {\n requireFields(view, ['month']);\n const startMonth = options.startMonth ?? 1;\n validateStartMonth(startMonth);\n if (startMonth === 1) {\n // Mirrors the Q token: months 1-3 → Q1, 4-6 → Q2, 7-9 → Q3, 10-12 → Q4.\n return Math.ceil(view.month! / 3);\n }\n // Fiscal case: shift the month so startMonth becomes month 1 of the\n // fiscal year (mod 12, 1-indexed), then apply the same ceil(/3) rule.\n // E.g. startMonth=7 (fiscal year starts July): July→1, Aug→2, ...,\n // Dec→6, Jan→7, ..., June→12. Then Q1 = fiscal months 1-3 (Jul-Sep),\n // matching the common \"FY starts in July\" convention where Q1 is the\n // first quarter of the fiscal year, not a quarter numbered by which\n // calendar quarter it falls in.\n const shifted = ((view.month! - startMonth + 12) % 12) + 1;\n return Math.ceil(shifted / 3);\n}\n\n// `getMonth` / `getWeekday` look trivial (just read the field) but\n// they're kept as real functions for API surface consistency with the\n// rest of this module. They also normalize: getWeekday returns 1-7\n// (Mon-Sun, matching Temporal's spec) regardless of what numbering the\n// caller's underlying value uses.\nexport function getMonth(view: DateFieldView): number {\n requireFields(view, ['month']);\n return view.month!;\n}\n\nexport function getWeekday(view: DateFieldView): number {\n requireFields(view, ['dayOfWeek']);\n return view.dayOfWeek!;\n}\n\n// startOf / endOf return new field bags (not Temporal objects — this\n// module is polyfill-free) with the relevant fields zeroed/extended.\n// Callers can pass the result to a Temporal constructor if they want\n// a typed value.\nexport type StartOfUnit = 'day' | 'month' | 'year' | 'hour' | 'minute' | 'second';\n\n// Returns true if the given unit (when used with startOf/endOf) should\n// also touch the time fields. 'day', 'month', 'year' all imply a\n// resolution coarser than an hour, so startOf zeroes the time fields\n// and endOf maxes them. Sub-hour units (hour/minute/second) only touch\n// the fields finer than themselves.\nfunction touchesTime(unit: StartOfUnit): boolean {\n return unit === 'day' || unit === 'month' || unit === 'year';\n}\n\n// startOf/endOf reassign year/month/day, which invalidates any\n// dayOfWeek carried over from the input — a plain { ...view } spread\n// leaves the old value sitting there unchanged. Same failure mode\n// businessCalendar.ts's isBusinessDay() works around for add(); we\n// recompute here rather than trust the copied field.\nfunction recomputeDayOfWeek(view: DateFieldView): void {\n if (typeof view.dayOfWeek !== 'number') return;\n /* c8 ignore start @preserve -- unreachable: startOf()/endOf() both call\n * asDateFieldView() before this, which already throws if year/month/day\n * aren't all numbers — so by the time a value with a numeric dayOfWeek\n * reaches here, year/month/day are guaranteed present too. */\n if (typeof view.year !== 'number' || typeof view.month !== 'number' || typeof view.day !== 'number') return;\n /* c8 ignore stop @preserve */\n const jsDow = new Date(Date.UTC(view.year, view.month - 1, view.day)).getUTCDay(); // 0=Sun..6=Sat\n view.dayOfWeek = jsDow === 0 ? 7 : jsDow; // 1=Mon..7=Sun\n}\n\nexport function startOf(value: unknown, unit: StartOfUnit): DateFieldView {\n const view = asDateFieldView(value);\n const result: DateFieldView = { ...view };\n if (unit === 'year') {\n result.month = 1;\n result.day = 1;\n } else if (unit === 'month') {\n result.day = 1;\n }\n recomputeDayOfWeek(result);\n if (touchesTime(unit)) {\n result.hour = 0;\n result.minute = 0;\n result.second = 0;\n result.millisecond = 0;\n } else if (unit === 'hour') {\n result.minute = 0;\n result.second = 0;\n result.millisecond = 0;\n } else if (unit === 'minute') {\n result.second = 0;\n result.millisecond = 0;\n } else if (unit === 'second') {\n result.millisecond = 0;\n }\n return result;\n}\n\nexport function endOf(value: unknown, unit: StartOfUnit): DateFieldView {\n const view = asDateFieldView(value);\n const result: DateFieldView = { ...view };\n if (unit === 'year') {\n result.month = 12;\n result.day = daysInMonth({ year: result.year!, month: 12 });\n } else if (unit === 'month') {\n result.day = daysInMonth({ year: result.year!, month: result.month! });\n }\n recomputeDayOfWeek(result);\n if (touchesTime(unit)) {\n result.hour = 23;\n result.minute = 59;\n result.second = 59;\n result.millisecond = 999;\n } else if (unit === 'hour') {\n result.minute = 59;\n result.second = 59;\n result.millisecond = 999;\n } else if (unit === 'minute') {\n result.second = 59;\n result.millisecond = 999;\n } else if (unit === 'second') {\n result.millisecond = 999;\n }\n return result;\n}\n\n// Type-narrowing helpers used by the comparison/arithmetic modules.\n// Lets them accept any of the four date-carrying Temporal types without\n// importing Temporal itself.\nexport function asDateFieldView(value: unknown): DateFieldView {\n if (typeof value !== 'object' || value === null) {\n throw new Error(`temporal-fmt: expected a date-carrying Temporal value, got ${String(value)}.`);\n }\n // Temporal instances expose year/month/day/etc. as prototype getters,\n // not own enumerable properties — so `{ ...value }` would lose them.\n // Read them explicitly. Only the fields actually present on this\n // value type end up in the returned view.\n const v = value as Record<string, unknown>;\n const out: DateFieldView = {};\n if (typeof v.year === 'number') out.year = v.year;\n if (typeof v.month === 'number') out.month = v.month;\n if (typeof v.day === 'number') out.day = v.day;\n if (typeof v.hour === 'number') out.hour = v.hour;\n if (typeof v.minute === 'number') out.minute = v.minute;\n if (typeof v.second === 'number') out.second = v.second;\n if (typeof v.millisecond === 'number') out.millisecond = v.millisecond;\n if (typeof v.dayOfWeek === 'number') out.dayOfWeek = v.dayOfWeek;\n if (typeof v.calendarId === 'string') out.calendarId = v.calendarId;\n if (out.year === undefined || out.month === undefined || out.day === undefined) {\n throw new Error(\n `temporal-fmt: value is missing year/month/day fields — pass a Temporal.PlainDate / PlainDateTime / ZonedDateTime.`\n );\n }\n return out;\n}\n\n// Re-export the TemporalType alias so callers can import everything\n// from one place.\nexport type { TemporalType } from './tokenMetadata.js';\n// Re-export TemporalLike for the same reason.\nexport type { TemporalLike } from './tokens.js';","// Date arithmetic helpers. Pure functions returning\n// field bags, same convention as calendarUtils.ts/comparison.ts — no\n// Temporal namespace needed. Callers can pass the result back into a\n// Temporal constructor to get a typed value, or chain operations\n// without committing to a Temporal implementation.\n\nimport { asDateFieldView, daysInMonth, type DateFieldView } from './calendarUtils.js';\n\nexport type AddUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction asDateTime(value: unknown): DateTimeFieldView {\n return asDateFieldView(value) as DateTimeFieldView;\n}\n\n// Recomputes dayOfWeek after year/month/day change. Mirrors\n// calendarUtils.ts's recomputeDayOfWeek — same fix, needed here too\n// since add()/subtract() mutate the date fields via a shallow spread\n// that otherwise leaves the old dayOfWeek sitting there, silently wrong.\nfunction recomputeDayOfWeek(view: DateTimeFieldView): void {\n if (typeof view.dayOfWeek !== 'number') return;\n /* c8 ignore start @preserve -- unreachable: every call site (add()'s\n 'years'/'months' cases, shiftDays()) passes a `result` whose\n year/month/day were just explicitly set, and the value originally\n came from asDateTime()/asDateFieldView(), which throws unless\n year/month/day are all present as numbers. So by the time this guard\n runs, all three are guaranteed valid. Kept in case a future caller\n constructs a DateTimeFieldView bypassing that validation. */\n if (typeof view.year !== 'number' || typeof view.month !== 'number' || typeof view.day !== 'number') return;\n /* c8 ignore stop @preserve */\n const jsDow = new Date(Date.UTC(view.year, view.month - 1, view.day)).getUTCDay(); // 0=Sun..6=Sat\n view.dayOfWeek = jsDow === 0 ? 7 : jsDow; // 1=Mon..7=Sun\n}\n\n// Adds the requested amount to the value's specified unit. Returns a\n// new field bag; the input is not mutated. Handles month/year overflow\n// by clamping to the last valid day of the target month (Feb 29 + 1\n// year on a non-leap year → Feb 28) — Temporal's `constrain` overflow\n// mode, which is the closest match to \"do the obvious thing\" callers\n// expect from arithmetic.\nexport function add(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n const v = asDateTime(value);\n let result: DateTimeFieldView = { ...v };\n switch (unit) {\n case 'years': {\n /* c8 ignore start @preserve -- the `?? 0` fallback is unreachable:\n asDateTime()/asDateFieldView() (called at the top of add()) throws\n unless year is present as a number, so result.year is never\n undefined here. Kept as a guard against a future change that\n relaxes that validation. */\n result.year = (result.year ?? 0) + amount;\n /* c8 ignore stop @preserve */\n // Clamp day-of-month to the new month's length.\n const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n if (result.day! > maxDay) result.day = maxDay;\n recomputeDayOfWeek(result);\n break;\n }\n case 'months': {\n // Total months = year*12 + month + amount, then split back.\n /* c8 ignore start @preserve -- both `??` fallbacks are unreachable\n for the same reason as the 'years' case above: asDateFieldView()\n guarantees year and month are present numbers before add() ever\n reaches here. */\n const total = (result.year ?? 0) * 12 + (result.month ?? 1) - 1 + amount;\n /* c8 ignore stop @preserve */\n result.year = Math.floor(total / 12);\n result.month = (total % 12) + 1;\n if (result.month < 1) { result.month += 12; result.year -= 1; }\n // Negative modulo handling: when total < 0, the mod goes negative.\n // Re-normalize.\n /* c8 ignore start @preserve -- unreachable: (total % 12) + 1 can never\n exceed 12 for any integer total in JS (its range is [-10, 12] since\n JS's % can return a negative remainder but never one with\n magnitude >= 12, so adding 1 tops out at 12). Only the < 1 branch\n above is reachable; this was written symmetrically with it but the\n overflow direction it guards against can't occur. Kept rather than\n removed since it costs nothing and documents the intent. */\n if (result.month > 12) { result.month -= 12; result.year += 1; }\n /* c8 ignore stop @preserve */\n const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n if (result.day! > maxDay) result.day = maxDay;\n recomputeDayOfWeek(result);\n break;\n }\n case 'weeks':\n result = shiftDays(result, amount * 7);\n break;\n case 'days':\n result = shiftDays(result, amount);\n break;\n case 'hours':\n result = shiftTime(result, amount * 3_600_000);\n break;\n case 'minutes':\n result = shiftTime(result, amount * 60_000);\n break;\n case 'seconds':\n result = shiftTime(result, amount * 1_000);\n break;\n case 'milliseconds':\n result = shiftTime(result, amount);\n break;\n }\n return result;\n}\n\nfunction shiftDays(v: DateTimeFieldView, days: number): DateTimeFieldView {\n // Convert Y/M/D to a day count, add, convert back. Using the same\n // algorithm as comparison.ts's sameWeek helper — proleptic Gregorian\n // day count via Howard Hinnant's era-based formula.\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const totalDays = era * 146097 + doe - 719468 + days;\n\n // Invert: totalDays → Y/M/D. Same source (Howard Hinnant's days_from_civil).\n const z = totalDays + 719468;\n const era2 = Math.floor((z >= 0 ? z : z - 146096) / 146097);\n const doe2 = z - era2 * 146097;\n const yoe2 = Math.floor((doe2 - Math.floor(doe2 / 1460) + Math.floor(doe2 / 36524) - Math.floor(doe2 / 146096)) / 365);\n const y2out = yoe2 + era2 * 400;\n const doy2 = doe2 - (365 * yoe2 + Math.floor(yoe2 / 4) - Math.floor(yoe2 / 100));\n const mp = Math.floor((5 * doy2 + 2) / 153);\n const d2 = doy2 - Math.floor((153 * mp + 2) / 5) + 1;\n const m2out = mp < 10 ? mp + 3 : mp - 9;\n const yOut = m2out <= 2 ? y2out + 1 : y2out;\n\n const result: DateTimeFieldView = { ...v, year: yOut, month: m2out, day: d2 };\n recomputeDayOfWeek(result);\n return result;\n}\n\nfunction shiftTime(v: DateTimeFieldView, msDelta: number): DateTimeFieldView {\n // Convert all time fields to ms, add, then split back. Day overflow\n // propagates to shiftDays; sub-second precision is preserved (caller\n // asking for hours/minutes/seconds/milliseconds gets ms-resolution\n // arithmetic, which is the limit of these helpers — nanoseconds\n // aren't supported here, only via Temporal.Duration's own add).\n const MS_PER_DAY = 86_400_000;\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n\n const totalMs = (v.hour ?? 0) * MS_PER_HOUR\n + (v.minute ?? 0) * MS_PER_MINUTE\n + (v.second ?? 0) * MS_PER_SECOND\n + (v.millisecond ?? 0)\n + msDelta;\n\n // Split into day-overflow + within-day ms.\n const dayOverflow = Math.floor(totalMs / MS_PER_DAY);\n let withinDay = totalMs % MS_PER_DAY;\n if (withinDay < 0) { withinDay += MS_PER_DAY; }\n\n const hour = Math.floor(withinDay / MS_PER_HOUR);\n const minute = Math.floor((withinDay % MS_PER_HOUR) / MS_PER_MINUTE);\n const second = Math.floor((withinDay % MS_PER_MINUTE) / MS_PER_SECOND);\n const millisecond = withinDay % MS_PER_SECOND;\n\n const result: DateTimeFieldView = { ...v, hour, minute, second, millisecond };\n if (dayOverflow !== 0) {\n return shiftDays(result, dayOverflow);\n }\n return result;\n}\n\nexport function subtract(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n return add(value, -amount, unit);\n}\n\n// Per-unit convenience wrappers — matches the grouping of\n// addYears / addMonths / ... / subtractNanoseconds. Nanosecond variants\n// aren't provided here because these helpers operate on field bags, and\n// the millisecond/microsecond/nanosecond split isn't preserved through\n// arithmetic at sub-millisecond precision the way Temporal.Duration's\n// own add preserves it. For ns-precision arithmetic, use Temporal.Duration\n// directly.\nexport const addYears = (v: unknown, n: number) => add(v, n, 'years');\nexport const addMonths = (v: unknown, n: number) => add(v, n, 'months');\nexport const addWeeks = (v: unknown, n: number) => add(v, n, 'weeks');\nexport const addDays = (v: unknown, n: number) => add(v, n, 'days');\nexport const addHours = (v: unknown, n: number) => add(v, n, 'hours');\nexport const addMinutes = (v: unknown, n: number) => add(v, n, 'minutes');\nexport const addSeconds = (v: unknown, n: number) => add(v, n, 'seconds');\nexport const addMilliseconds = (v: unknown, n: number) => add(v, n, 'milliseconds');\n\nexport const subtractYears = (v: unknown, n: number) => subtract(v, n, 'years');\nexport const subtractMonths = (v: unknown, n: number) => subtract(v, n, 'months');\nexport const subtractWeeks = (v: unknown, n: number) => subtract(v, n, 'weeks');\nexport const subtractDays = (v: unknown, n: number) => subtract(v, n, 'days');\nexport const subtractHours = (v: unknown, n: number) => subtract(v, n, 'hours');\nexport const subtractMinutes = (v: unknown, n: number) => subtract(v, n, 'minutes');\nexport const subtractSeconds = (v: unknown, n: number) => subtract(v, n, 'seconds');\nexport const subtractMilliseconds = (v: unknown, n: number) => subtract(v, n, 'milliseconds');\n\n// difference(): returns the count of `unit` boundaries between two values.\n// Pure-integer math, no Temporal.Duration involved. For sub-day\n// precision the result is an integer count of the requested unit (not\n// a Duration field bag) — matches the most common caller expectation\n// (\"how many days between these two dates?\").\nexport type DiffUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\nexport function difference(a: unknown, b: unknown, unit: DiffUnit): number {\n const av = asDateTime(a);\n const bv = asDateTime(b);\n\n // For day-granular units (years/months/weeks/days), compute via\n // calendar arithmetic on the date fields. For sub-day units, compute\n // via total-ms and divide.\n if (unit === 'years') {\n return bv.year! - av.year!;\n }\n if (unit === 'months') {\n return (bv.year! * 12 + bv.month! - 1) - (av.year! * 12 + av.month! - 1);\n }\n if (unit === 'weeks' || unit === 'days') {\n const aDays = toDayCount(av);\n const bDays = toDayCount(bv);\n const days = bDays - aDays;\n return unit === 'weeks' ? Math.trunc(days / 7) : days;\n }\n\n // Sub-day units: total-ms math.\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n const aMs = toTotalMs(av);\n const bMs = toTotalMs(bv);\n const diffMs = bMs - aMs;\n switch (unit) {\n case 'hours': return Math.trunc(diffMs / MS_PER_HOUR);\n case 'minutes': return Math.trunc(diffMs / MS_PER_MINUTE);\n case 'seconds': return Math.trunc(diffMs / MS_PER_SECOND);\n case 'milliseconds': return diffMs;\n }\n}\n\nfunction toDayCount(v: DateTimeFieldView): number {\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n return era * 146097 + doe - 719468;\n}\n\nfunction toTotalMs(v: DateTimeFieldView): number {\n const MS_PER_DAY = 86_400_000;\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n return toDayCount(v) * MS_PER_DAY\n + (v.hour ?? 0) * MS_PER_HOUR\n + (v.minute ?? 0) * MS_PER_MINUTE\n + (v.second ?? 0) * MS_PER_SECOND\n + (v.millisecond ?? 0);\n}\n\n// Per-unit difference wrappers — same convenience pattern as the add*\n// wrappers above.\nexport const differenceInYears = (a: unknown, b: unknown) => difference(a, b, 'years');\nexport const differenceInMonths = (a: unknown, b: unknown) => difference(a, b, 'months');\nexport const differenceInWeeks = (a: unknown, b: unknown) => difference(a, b, 'weeks');\nexport const differenceInDays = (a: unknown, b: unknown) => difference(a, b, 'days');\nexport const differenceInHours = (a: unknown, b: unknown) => difference(a, b, 'hours');\nexport const differenceInMinutes = (a: unknown, b: unknown) => difference(a, b, 'minutes');\nexport const differenceInSeconds = (a: unknown, b: unknown) => difference(a, b, 'seconds');\nexport const differenceInMilliseconds = (a: unknown, b: unknown) => difference(a, b, 'milliseconds');","// Comparison helpers. Pure functions over the field\n// shape, no Temporal namespace needed — same convention as\n// calendarUtils.ts. All comparisons are field-based, not identity-based,\n// so a polyfill PlainDate compares equal to a native PlainDate with the\n// same year/month/day.\n\nimport { asDateFieldView, type DateFieldView } from './calendarUtils.js';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction toComparableMs(view: DateTimeFieldView): number {\n // Exact proleptic-Gregorian day count (Howard Hinnant's days_from_civil,\n // same algorithm arithmetic.ts/interval.ts use) plus the time-of-day —\n // NOT an approximation. The previous implementation multiplied the\n // year offset by an average 365.2425 days/year, on the theory that the\n // error \"is constant for any given year, so it cancels out in a diff\".\n // That's only true when both dates fall in the same year: the year\n // term's error differs by up to ~0.76 days between adjacent years\n // (365.2425 vs 365/366 actual), which corrupts cross-year comparisons\n // near the boundary — e.g. isAfter(2025-01-01T00:00, 2024-12-31T18:12)\n // returned false (truth: ~5.8h after), and isEqual() returned true for\n // instants ~18h apart (2025-01-01T00:00 vs 2024-12-31T05:49:12).\n // Exact arithmetic has no such error term.\n const y = view.year!, m = view.month!, d = view.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const days = era * 146097 + doe - 719468;\n\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n\n return days * 86_400_000\n + (view.hour ?? 0) * MS_PER_HOUR\n + (view.minute ?? 0) * MS_PER_MINUTE\n + (view.second ?? 0) * MS_PER_SECOND\n + (view.millisecond ?? 0);\n}\n\nfunction asDateTimeFieldView(value: unknown): DateTimeFieldView {\n const v = asDateFieldView(value);\n return v as DateTimeFieldView;\n}\n\n// Returns < 0 if a < b, 0 if a === b, > 0 if a > b. Same convention as\n// Array.prototype.sort and Temporal.PlainDate.compare. Lets callers\n// pass this directly as a comparator.\nexport function compare(a: unknown, b: unknown): number {\n const av = asDateTimeFieldView(a);\n const bv = asDateTimeFieldView(b);\n const am = toComparableMs(av);\n const bm = toComparableMs(bv);\n if (am < bm) return -1;\n if (am > bm) return 1;\n return 0;\n}\n\nexport function isEqual(a: unknown, b: unknown): boolean {\n return compare(a, b) === 0;\n}\n\nexport function isBefore(a: unknown, b: unknown): boolean {\n return compare(a, b) < 0;\n}\n\nexport function isAfter(a: unknown, b: unknown): boolean {\n return compare(a, b) > 0;\n}\n\nexport function min(values: unknown[]): unknown {\n if (!Array.isArray(values) || values.length === 0) {\n throw new Error('temporal-fmt: min() requires a non-empty array of values.');\n }\n let best = values[0];\n for (let i = 1; i < values.length; i++) {\n if (compare(values[i], best) < 0) best = values[i];\n }\n return best;\n}\n\nexport function max(values: unknown[]): unknown {\n if (!Array.isArray(values) || values.length === 0) {\n throw new Error('temporal-fmt: max() requires a non-empty array of values.');\n }\n let best = values[0];\n for (let i = 1; i < values.length; i++) {\n if (compare(values[i], best) > 0) best = values[i];\n }\n return best;\n}\n\nexport function clamp(value: unknown, lo: unknown, hi: unknown): unknown {\n if (compare(value, lo) < 0) return lo;\n if (compare(value, hi) > 0) return hi;\n return value;\n}\n\nexport function isBetween(value: unknown, lo: unknown, hi: unknown): boolean {\n return compare(value, lo) >= 0 && compare(value, hi) <= 0;\n}\n\n// Semantic helpers — same-day/week/month/quarter/year tests. Useful for\n// UI (\"is this event today?\") and for grouping/aggregation. All take a\n// reference value to compare against; the \"isToday\" / \"isTomorrow\" /\n// \"isYesterday\" variants compute the reference from the current system\n// date themselves so callers don't have to.\nfunction readYearMonthDay(value: unknown): { year: number; month: number; day: number } {\n const v = asDateFieldView(value);\n return { year: v.year!, month: v.month!, day: v.day! };\n}\n\nfunction todayYearMonthDay(): { year: number; month: number; day: number } {\n const now = new Date();\n return { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() };\n}\n\nfunction dateEqualYearMonthDay(a: { year: number; month: number; day: number }, b: { year: number; month: number; day: number }): boolean {\n return a.year === b.year && a.month === b.month && a.day === b.day;\n}\n\nexport function isSameDay(value: unknown, reference: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), readYearMonthDay(reference));\n}\n\nexport function isToday(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), todayYearMonthDay());\n}\n\nfunction tomorrowYearMonthDay(): { year: number; month: number; day: number } {\n const d = new Date();\n d.setDate(d.getDate() + 1);\n return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };\n}\n\nfunction yesterdayYearMonthDay(): { year: number; month: number; day: number } {\n const d = new Date();\n d.setDate(d.getDate() - 1);\n return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };\n}\n\nexport function isTomorrow(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), tomorrowYearMonthDay());\n}\n\nexport function isYesterday(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), yesterdayYearMonthDay());\n}\n\nfunction sameWeek(yearA: number, monthA: number, dayA: number, yearB: number, monthB: number, dayB: number): boolean {\n // Same week iff the dates are within 7 days of each other AND the earlier\n // one's weekday is ≤ the later one's weekday (so we don't say \"Sun and\n // the following Mon are in the same week\" — ISO weeks run Mon-Sun).\n // Easier: compute ISO week numbers via isoWeekYearAndWeek, compare both\n // year and week. That handles the year-boundary case (Dec 31 vs Jan 1 of\n // the next year, which can be in the same ISO week).\n // Local import to avoid module-cycle: isoWeek.ts doesn't import from here.\n // Lazy require-style dynamic via inline static import at top would be\n // cleaner; this inline computation is fine for a pure helper.\n // ...actually let's just compute weekday offsets directly. Same-ISO-week\n // iff |d1 - d2| < 7 days AND the earlier date's ISO weekday ≤ the later\n // date's ISO weekday so they fall in the same Mon-Sun span.\n const DAYS_PER_400_YEARS = 146097;\n const daysSince = (y: number, m: number, d: number): number => {\n // Zeller's congruence variant — convert Y/M/D to a day count.\n // Using a simple algorithm here that's correct for proleptic Gregorian.\n const y2 = m <= 2 ? y - 1 : y;\n const era = (y2 >= 0 ? y2 : y2 - 399) / 400 | 0;\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((Math.floor(153 * m2 + 2) / 5) + d - 1);\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n return era * DAYS_PER_400_YEARS + doe - 719468;\n };\n const a = daysSince(yearA, monthA, dayA);\n const b = daysSince(yearB, monthB, dayB);\n if (Math.abs(a - b) >= 7) return false;\n // Same week iff both dates round to the same Monday. Compute the\n // Monday-of-week for each: subtract (weekday - 1) days, where weekday\n // is 1=Mon..7=Sun. Use the standard Zeller formula for weekday.\n const weekdayOf = (y: number, m: number, d: number): number => {\n const days = daysSince(y, m, d);\n // 1970-01-01 was a Thursday (ISO 4). So weekday = ((days + 3) mod 7) + 1\n // mapped to Mon=1..Sun=7. Let's compute: Sun=0..Sat=6 via JS Date.\n const date = new Date(Date.UTC(y, m - 1, d));\n const jsDow = date.getUTCDay(); // 0=Sun..6=Sat\n return jsDow === 0 ? 7 : jsDow;\n };\n const wa = a - (weekdayOf(yearA, monthA, dayA) - 1);\n const wb = b - (weekdayOf(yearB, monthB, dayB) - 1);\n return wa === wb;\n}\n\nexport function isSameWeek(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return sameWeek(a.year, a.month, a.day, b.year, b.month, b.day);\n}\n\nexport function isSameMonth(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return a.year === b.year && a.month === b.month;\n}\n\nexport function isSameQuarter(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return a.year === b.year && Math.ceil(a.month / 3) === Math.ceil(b.month / 3);\n}\n\nexport function isSameYear(value: unknown, reference: unknown): boolean {\n return readYearMonthDay(value).year === readYearMonthDay(reference).year;\n}\n\n// isWeekend / isWeekday depend on dayOfWeek — present on PlainDate,\n// PlainDateTime, ZonedDateTime, but NOT on PlainTime. Callers passing\n// PlainTime get a descriptive throw. Doesn't call asDateFieldView\n// because that would reject PlainTime (no year/month/day) before\n// reaching the dayOfWeek check — and the error we want here is the\n// \"no dayOfWeek field\" one, not the \"no date fields\" one.\nexport function isWeekend(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) {\n throw new Error(`temporal-fmt: isWeekend() expected a Temporal value, got ${String(value)}.`);\n }\n const v = value as { dayOfWeek?: unknown };\n if (typeof v.dayOfWeek !== 'number') {\n throw new Error(\n `temporal-fmt: isWeekend() needs a value with a dayOfWeek field (PlainDate / PlainDateTime / ZonedDateTime).`\n );\n }\n // 6=Sat, 7=Sun in Temporal's Mon=1..Sun=7 numbering.\n return v.dayOfWeek === 6 || v.dayOfWeek === 7;\n}\n\nexport function isWeekday(value: unknown): boolean {\n return !isWeekend(value);\n}\n"],"mappings":"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","names":["advanced"]}
|
package/dist/recurrence.js
CHANGED
|
@@ -1,2 +1,20 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import {
|
|
2
|
+
between,
|
|
3
|
+
formatRRule,
|
|
4
|
+
parseRRule,
|
|
5
|
+
recurrence,
|
|
6
|
+
skip,
|
|
7
|
+
take
|
|
8
|
+
} from "./chunk-VNIUIQA4.js";
|
|
9
|
+
import "./chunk-TSLPIURC.js";
|
|
10
|
+
import "./chunk-HXY2GUUM.js";
|
|
11
|
+
import "./chunk-MKPDLQKD.js";
|
|
12
|
+
export {
|
|
13
|
+
between,
|
|
14
|
+
formatRRule,
|
|
15
|
+
parseRRule,
|
|
16
|
+
recurrence,
|
|
17
|
+
skip,
|
|
18
|
+
take
|
|
19
|
+
};
|
|
2
20
|
//# sourceMappingURL=recurrence.js.map
|