@bimetal/temporal 0.35.0

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.
Files changed (48) hide show
  1. package/LICENSE +57 -0
  2. package/README.md +73 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/constants.d.ts +29 -0
  5. package/dist/constants.d.ts.map +1 -0
  6. package/dist/constants.js +29 -0
  7. package/dist/constants.js.map +1 -0
  8. package/dist/date-strings.d.ts +23 -0
  9. package/dist/date-strings.d.ts.map +1 -0
  10. package/dist/date-strings.js +25 -0
  11. package/dist/date-strings.js.map +1 -0
  12. package/dist/datetime.d.ts +100 -0
  13. package/dist/datetime.d.ts.map +1 -0
  14. package/dist/datetime.js +289 -0
  15. package/dist/datetime.js.map +1 -0
  16. package/dist/duration.d.ts +25 -0
  17. package/dist/duration.d.ts.map +1 -0
  18. package/dist/duration.js +43 -0
  19. package/dist/duration.js.map +1 -0
  20. package/dist/errors.d.ts +22 -0
  21. package/dist/errors.d.ts.map +1 -0
  22. package/dist/errors.js +19 -0
  23. package/dist/errors.js.map +1 -0
  24. package/dist/format.d.ts +13 -0
  25. package/dist/format.d.ts.map +1 -0
  26. package/dist/format.js +51 -0
  27. package/dist/format.js.map +1 -0
  28. package/dist/granularity.d.ts +13 -0
  29. package/dist/granularity.d.ts.map +1 -0
  30. package/dist/granularity.js +70 -0
  31. package/dist/granularity.js.map +1 -0
  32. package/dist/index.d.ts +30 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +37 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/time-range.d.ts +38 -0
  37. package/dist/time-range.d.ts.map +1 -0
  38. package/dist/time-range.js +67 -0
  39. package/dist/time-range.js.map +1 -0
  40. package/dist/types.d.ts +50 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +9 -0
  43. package/dist/types.js.map +1 -0
  44. package/dist/vocabulary.d.ts +40 -0
  45. package/dist/vocabulary.d.ts.map +1 -0
  46. package/dist/vocabulary.js +38 -0
  47. package/dist/vocabulary.js.map +1 -0
  48. package/package.json +45 -0
@@ -0,0 +1,289 @@
1
+ /**
2
+ * CalendarDateTime — creation, arithmetic, and queries.
3
+ *
4
+ * Internal representation: UTC epoch milliseconds + IANA timezone string.
5
+ *
6
+ * Two arithmetic models, named by contract (not by accident):
7
+ * - CIVIL (addDays / addWeeks / addMonths / addYears): wall-clock preserving
8
+ * and DST-safe. "Add 1 unit" means "same wall-clock time, one unit
9
+ * later", even when the elapsed time differs (a DST day is 23 or 25h).
10
+ * - ELAPSED (addHours / addMinutes): exact physical milliseconds. A duration is
11
+ * a physical quantity, so across a DST transition the wall clock may
12
+ * shift. This is intentional — durations and timeline drags rely on it
13
+ * (a 60-minute event stays 60 minutes), NOT a missing DST fix.
14
+ */
15
+ import { CalendarValidationError } from './errors.js';
16
+ import { MS_PER_MINUTE, MS_PER_HOUR, MS_PER_EXACT_DAY, MINUTES_PER_HOUR, } from './constants.js';
17
+ // ── Timezone helpers (Intl-based, no external deps) ─────────────
18
+ /** Cache for Intl.DateTimeFormat instances. */
19
+ const formatCache = new Map();
20
+ function getFormatter(timezone) {
21
+ let fmt = formatCache.get(timezone);
22
+ if (!fmt) {
23
+ try {
24
+ fmt = new Intl.DateTimeFormat('en-US', {
25
+ timeZone: timezone,
26
+ year: 'numeric',
27
+ month: 'numeric',
28
+ day: 'numeric',
29
+ hour: 'numeric',
30
+ minute: 'numeric',
31
+ second: 'numeric',
32
+ hour12: false,
33
+ });
34
+ }
35
+ catch {
36
+ throw new CalendarValidationError({ type: 'INVALID_TIMEZONE', timezone });
37
+ }
38
+ formatCache.set(timezone, fmt);
39
+ }
40
+ return fmt;
41
+ }
42
+ /** Parse Intl.DateTimeFormat parts into components. */
43
+ function getWallClockParts(epochMs, timezone) {
44
+ const fmt = getFormatter(timezone);
45
+ const parts = fmt.formatToParts(new Date(epochMs));
46
+ const get = (type) => Number(parts.find(p => p.type === type).value);
47
+ return {
48
+ year: get('year'),
49
+ month: get('month') - 1, // 0-based
50
+ day: get('day'),
51
+ hour: get('hour') === 24 ? 0 : get('hour'),
52
+ minute: get('minute'),
53
+ second: get('second'),
54
+ };
55
+ }
56
+ /**
57
+ * Convert wall-clock time in a timezone to UTC epoch milliseconds.
58
+ * Handles DST gaps by snapping forward to the first valid instant.
59
+ * Handles DST overlaps by preferring the earlier occurrence.
60
+ */
61
+ function wallClockToEpochMs(year, month, day, hour, minute, timezone) {
62
+ // Try two offsets: the one before and after a potential DST transition.
63
+ // We sample the offset at two known-good points: midnight of the requested
64
+ // day and noon. One of them should be on the correct side of any transition.
65
+ const midnightUtc = Date.UTC(year, month, day, 0, 0, 0, 0);
66
+ const noonUtc = Date.UTC(year, month, day, 12, 0, 0, 0);
67
+ const offsetAtMidnight = getUtcOffset(midnightUtc, timezone);
68
+ const offsetAtNoon = getUtcOffset(noonUtc, timezone);
69
+ // Try both offsets and see which gives the correct wall-clock time
70
+ const candidates = [offsetAtMidnight, offsetAtNoon];
71
+ // Remove duplicates
72
+ const uniqueOffsets = [...new Set(candidates)];
73
+ const requestedMinutes = hour * MINUTES_PER_HOUR + minute;
74
+ for (const offset of uniqueOffsets) {
75
+ const epochMs = Date.UTC(year, month, day, hour, minute, 0, 0) - offset;
76
+ const verify = getWallClockParts(epochMs, timezone);
77
+ if (verify.year === year && verify.month === month && verify.day === day
78
+ && verify.hour === hour && verify.minute === minute) {
79
+ return epochMs;
80
+ }
81
+ }
82
+ // DST gap: requested time doesn't exist.
83
+ // Snap forward: try with the pre-transition offset (typically midnight's).
84
+ // This produces a UTC instant that, when interpreted in the timezone,
85
+ // lands on the post-gap wall-clock time (e.g., 02:30 CET → 03:30 CEST).
86
+ const preTransitionOffset = offsetAtMidnight;
87
+ const snapped = Date.UTC(year, month, day, hour, minute, 0, 0) - preTransitionOffset;
88
+ return snapped;
89
+ }
90
+ /** Get the UTC offset in milliseconds for a given instant in a timezone. */
91
+ function getUtcOffset(epochMs, timezone) {
92
+ const wall = getWallClockParts(epochMs, timezone);
93
+ const wallAsUtc = Date.UTC(wall.year, wall.month, wall.day, wall.hour, wall.minute, wall.second);
94
+ return wallAsUtc - epochMs;
95
+ }
96
+ /** Number of days in a 0-based month of a year (leap-year aware). Timezone-agnostic:
97
+ * only the day component of the constructed local date is read. */
98
+ function daysInMonth(year, month) {
99
+ return new Date(year, month + 1, 0).getDate();
100
+ }
101
+ // ── Creation ────────────────────────────────────────────────────
102
+ /** Create a CalendarDateTime from wall-clock components in a timezone. */
103
+ export function createDateTime(year, month, day, hour, minute, timezone) {
104
+ // Reject NaN, Infinity, non-integer for all numeric components
105
+ if (!Number.isInteger(year))
106
+ throw new CalendarValidationError({ type: 'INVALID_EVENT', field: 'year', message: `year must be an integer, got ${year}` });
107
+ if (!Number.isInteger(month) || month < 0 || month > 11)
108
+ throw new CalendarValidationError({ type: 'INVALID_EVENT', field: 'month', message: `month must be 0-11, got ${month}` });
109
+ if (!Number.isInteger(hour) || hour < 0 || hour > 23)
110
+ throw new CalendarValidationError({ type: 'INVALID_EVENT', field: 'hour', message: `hour must be 0-23, got ${hour}` });
111
+ if (!Number.isInteger(minute) || minute < 0 || minute > 59)
112
+ throw new CalendarValidationError({ type: 'INVALID_EVENT', field: 'minute', message: `minute must be 0-59, got ${minute}` });
113
+ // Validate day against actual month length
114
+ if (!Number.isInteger(day) || day < 1)
115
+ throw new CalendarValidationError({ type: 'INVALID_EVENT', field: 'day', message: `day must be >= 1, got ${day}` });
116
+ const maxDay = daysInMonth(year, month); // last day of month
117
+ if (day > maxDay)
118
+ throw new CalendarValidationError({ type: 'INVALID_EVENT', field: 'day', message: `day ${day} does not exist in month ${month} of year ${year} (max: ${maxDay})` });
119
+ const epochMs = wallClockToEpochMs(year, month, day, hour, minute, timezone);
120
+ return { epochMs, timezone };
121
+ }
122
+ /** Create a CalendarDateTime from a UTC epoch and timezone. */
123
+ export function fromEpochMs(epochMs, timezone) {
124
+ if (!Number.isFinite(epochMs)) {
125
+ throw new CalendarValidationError({
126
+ type: 'INVALID_TIME_RANGE',
127
+ message: 'epochMs must be a finite number',
128
+ });
129
+ }
130
+ // Validate timezone by attempting to use it
131
+ getFormatter(timezone);
132
+ return { epochMs, timezone };
133
+ }
134
+ /** Get the current time in a timezone. Uses config.clock if provided. */
135
+ export function now(timezone, clock) {
136
+ const epochMs = clock ? clock.now() : Date.now();
137
+ return fromEpochMs(epochMs, timezone);
138
+ }
139
+ // ── Civil (wall-clock) arithmetic — days and up, DST-safe ───────
140
+ /**
141
+ * Add days, preserving wall-clock time (DST-safe).
142
+ *
143
+ * "Add 1 day" means "same time tomorrow", not "+24 hours".
144
+ * On a DST transition day, the actual elapsed time may be 23 or 25 hours.
145
+ */
146
+ export function addDays(dt, n) {
147
+ const wall = getWallClockParts(dt.epochMs, dt.timezone);
148
+ const epochMs = wallClockToEpochMs(wall.year, wall.month, wall.day + n, wall.hour, wall.minute, dt.timezone);
149
+ return { epochMs, timezone: dt.timezone };
150
+ }
151
+ /**
152
+ * Add weeks (n × 7 days), preserving wall-clock time. DST-safe. A CIVIL unit:
153
+ * delegates to addDays, so "add 1 week" means "same weekday and time, seven
154
+ * days later", not "+168 hours".
155
+ */
156
+ export function addWeeks(dt, n) {
157
+ return addDays(dt, n * 7);
158
+ }
159
+ /**
160
+ * Add calendar months, preserving wall-clock time, with year carry. DST-safe.
161
+ *
162
+ * "Add 1 month" means "same day-of-month next month", not "+30 days". The
163
+ * day-of-month is clamped to the target month's length (31 Jan + 1 month →
164
+ * 28/29 Feb, never 3 March). Negative n and multi-year carry are handled via
165
+ * integer division, so December + 1 → January of the next year. Symmetric to
166
+ * addDays: computed through the wall-clock helpers, not naive epoch arithmetic.
167
+ */
168
+ export function addMonths(dt, n) {
169
+ const wall = getWallClockParts(dt.epochMs, dt.timezone);
170
+ const totalMonths = wall.year * 12 + wall.month + n;
171
+ const year = Math.floor(totalMonths / 12);
172
+ const month = totalMonths - year * 12; // 0-11, non-negative even for negative n
173
+ const day = Math.min(wall.day, daysInMonth(year, month));
174
+ const epochMs = wallClockToEpochMs(year, month, day, wall.hour, wall.minute, dt.timezone);
175
+ return { epochMs, timezone: dt.timezone };
176
+ }
177
+ /**
178
+ * Add calendar years, preserving wall-clock time. DST-safe. A CIVIL unit:
179
+ * delegates to addMonths (n × 12 months), so the day-of-month is clamped to the
180
+ * target month's length (29 Feb + 1 year → 28 Feb in a non-leap year).
181
+ */
182
+ export function addYears(dt, n) {
183
+ return addMonths(dt, n * 12);
184
+ }
185
+ // ── Elapsed (physical) time — hours and minutes ─────────────────
186
+ // These add exact elapsed milliseconds. ELAPSED, not civil, BY CONTRACT: a
187
+ // duration is a physical quantity, so across a DST transition the wall clock
188
+ // may shift. For calendar-unit arithmetic use the CIVIL functions above.
189
+ /**
190
+ * Add exact elapsed hours (n × 3 600 000 ms). ELAPSED semantics by contract:
191
+ * a physical duration, so across a DST transition the wall-clock hour shifts.
192
+ * For "same time n hours later" use civil day/week/month/year arithmetic.
193
+ */
194
+ export function addHours(dt, n) {
195
+ return { epochMs: dt.epochMs + n * MS_PER_HOUR, timezone: dt.timezone };
196
+ }
197
+ /**
198
+ * Add exact elapsed minutes (n × 60 000 ms). ELAPSED semantics by contract —
199
+ * see addHours. Event durations and timeline drags depend on this being physical
200
+ * time (a 60-minute event stays 60 minutes across a DST change).
201
+ */
202
+ export function addMinutes(dt, n) {
203
+ return { epochMs: dt.epochMs + n * MS_PER_MINUTE, timezone: dt.timezone };
204
+ }
205
+ /** Re-interpret the same instant in a different timezone. */
206
+ export function withTimezone(dt, timezone) {
207
+ getFormatter(timezone); // validate
208
+ return { epochMs: dt.epochMs, timezone };
209
+ }
210
+ /** Get 00:00:00.000 of the day containing dt, in dt's timezone. */
211
+ export function startOfDay(dt) {
212
+ const wall = getWallClockParts(dt.epochMs, dt.timezone);
213
+ const epochMs = wallClockToEpochMs(wall.year, wall.month, wall.day, 0, 0, dt.timezone);
214
+ return { epochMs, timezone: dt.timezone };
215
+ }
216
+ /** Get 23:59:59.999 of the day containing dt, in dt's timezone. */
217
+ export function endOfDay(dt) {
218
+ const wall = getWallClockParts(dt.epochMs, dt.timezone);
219
+ const epochMs = wallClockToEpochMs(wall.year, wall.month, wall.day + 1, 0, 0, dt.timezone) - 1;
220
+ return { epochMs, timezone: dt.timezone };
221
+ }
222
+ // ── Queries ─────────────────────────────────────────────────────
223
+ /** Get the wall-clock year of dt in its own timezone. */
224
+ export function getYear(dt) {
225
+ return getWallClockParts(dt.epochMs, dt.timezone).year;
226
+ }
227
+ /** Returns 0-based month (0 = January). */
228
+ export function getMonth(dt) {
229
+ return getWallClockParts(dt.epochMs, dt.timezone).month;
230
+ }
231
+ /** Get the wall-clock day of month (1-based) of dt in its own timezone. */
232
+ export function getDayOfMonth(dt) {
233
+ return getWallClockParts(dt.epochMs, dt.timezone).day;
234
+ }
235
+ /** Returns the Weekday (Monday = 0 … Sunday = 6). HOUSE convention — NOT ISO-8601 (1–7) and NOT
236
+ * JS `Date.getDay()` (0 = Sunday); this remaps getDay's Sunday-first to Monday-first. */
237
+ export function getDayOfWeek(dt) {
238
+ // We need wall-clock day, not UTC day
239
+ const wall = getWallClockParts(dt.epochMs, dt.timezone);
240
+ const wallDate = new Date(wall.year, wall.month, wall.day);
241
+ const jsWallDay = wallDate.getDay();
242
+ return (jsWallDay === 0 ? 6 : jsWallDay - 1);
243
+ }
244
+ /** Get the wall-clock hour (0-23) of dt in its own timezone. */
245
+ export function getHours(dt) {
246
+ return getWallClockParts(dt.epochMs, dt.timezone).hour;
247
+ }
248
+ /** Get the wall-clock minute (0-59) of dt in its own timezone. */
249
+ export function getMinutes(dt) {
250
+ return getWallClockParts(dt.epochMs, dt.timezone).minute;
251
+ }
252
+ /** Wall-clock minutes since midnight in dt's timezone — `hour*60 + minute`. */
253
+ export function toMinutesOfDay(dt) {
254
+ const w = getWallClockParts(dt.epochMs, dt.timezone);
255
+ return w.hour * MINUTES_PER_HOUR + w.minute;
256
+ }
257
+ /** Check if two DateTimes fall on the same calendar day (in their respective timezones). */
258
+ export function isSameDay(a, b) {
259
+ const wa = getWallClockParts(a.epochMs, a.timezone);
260
+ const wb = getWallClockParts(b.epochMs, b.timezone);
261
+ return wa.year === wb.year && wa.month === wb.month && wa.day === wb.day;
262
+ }
263
+ /** Check if a is strictly before b (by epoch instant, not wall-clock day). */
264
+ export function isBefore(a, b) {
265
+ return a.epochMs < b.epochMs;
266
+ }
267
+ /** Check if a is strictly after b (by epoch instant, not wall-clock day). */
268
+ export function isAfter(a, b) {
269
+ return a.epochMs > b.epochMs;
270
+ }
271
+ /** Check if a and b refer to the same instant (same epoch, regardless of timezone). */
272
+ export function isEqual(a, b) {
273
+ return a.epochMs === b.epochMs;
274
+ }
275
+ /**
276
+ * Count calendar days between two DateTimes (in their respective timezones).
277
+ * Returns positive if b is after a.
278
+ * This counts actual calendar days, NOT epochMs / MS_PER_EXACT_DAY — it is DST-safe.
279
+ */
280
+ export function daysBetween(a, b) {
281
+ const wa = getWallClockParts(a.epochMs, a.timezone);
282
+ const wb = getWallClockParts(b.epochMs, b.timezone);
283
+ // Use UTC dates (no timezone shifts) for a clean day count; safe to divide
284
+ // by MS_PER_EXACT_DAY here because both endpoints are UTC midnight (no DST).
285
+ const utcA = Date.UTC(wa.year, wa.month, wa.day);
286
+ const utcB = Date.UTC(wb.year, wb.month, wb.day);
287
+ return Math.round((utcB - utcA) / MS_PER_EXACT_DAY);
288
+ }
289
+ //# sourceMappingURL=datetime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datetime.js","sourceRoot":"","sources":["../src/datetime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { CalendarDateTime, Duration, TimeRange } from './types.js';
2
+ /** Create a Duration from minutes. */
3
+ export declare function minutes(n: number): Duration;
4
+ /** Create a Duration from hours. */
5
+ export declare function hours(n: number): Duration;
6
+ /** Create a Duration from days (24-hour periods, NOT DST-safe — see MS_PER_EXACT_DAY). */
7
+ export declare function days(n: number): Duration;
8
+ /** Duration between two CalendarDateTimes. */
9
+ export declare function between(a: CalendarDateTime, b: CalendarDateTime): Duration;
10
+ /** Duration of a TimeRange. */
11
+ export declare function duration(range: TimeRange): Duration;
12
+ /** Convert Duration to fractional minutes. */
13
+ export declare function toMinutes(d: Duration): number;
14
+ /** Convert Duration to fractional hours. */
15
+ export declare function toHours(d: Duration): number;
16
+ /** Convert Duration to fractional days (24-hour periods, NOT DST-safe). */
17
+ export declare function toDays(d: Duration): number;
18
+ /**
19
+ * Length of a TimeRange in fractional minutes. Convenience for the
20
+ * very common `(range.end.epochMs - range.start.epochMs) / MS_PER_MINUTE`
21
+ * pattern in interaction/rule code. Composition with `toMinutes(duration(range))`
22
+ * would do the same thing in two steps.
23
+ */
24
+ export declare function durationInMinutes(range: TimeRange): number;
25
+ //# sourceMappingURL=duration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGxE,sCAAsC;AACtC,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,QAAQ,CAE3C;AAED,oCAAoC;AACpC,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,QAAQ,CAEzC;AAED,0FAA0F;AAC1F,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,QAAQ,CAExC;AAED,8CAA8C;AAC9C,wBAAgB,OAAO,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,gBAAgB,GAAG,QAAQ,CAE1E;AAED,+BAA+B;AAC/B,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,CAEnD;AAED,8CAA8C;AAC9C,wBAAgB,SAAS,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAE7C;AAED,4CAA4C;AAC5C,wBAAgB,OAAO,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAE3C;AAED,2EAA2E;AAC3E,wBAAgB,MAAM,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAE1C;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAE1D"}
@@ -0,0 +1,43 @@
1
+ import { MS_PER_MINUTE, MS_PER_HOUR, MS_PER_EXACT_DAY } from './constants.js';
2
+ /** Create a Duration from minutes. */
3
+ export function minutes(n) {
4
+ return { ms: n * MS_PER_MINUTE };
5
+ }
6
+ /** Create a Duration from hours. */
7
+ export function hours(n) {
8
+ return { ms: n * MS_PER_HOUR };
9
+ }
10
+ /** Create a Duration from days (24-hour periods, NOT DST-safe — see MS_PER_EXACT_DAY). */
11
+ export function days(n) {
12
+ return { ms: n * MS_PER_EXACT_DAY };
13
+ }
14
+ /** Duration between two CalendarDateTimes. */
15
+ export function between(a, b) {
16
+ return { ms: Math.abs(b.epochMs - a.epochMs) };
17
+ }
18
+ /** Duration of a TimeRange. */
19
+ export function duration(range) {
20
+ return { ms: range.end.epochMs - range.start.epochMs };
21
+ }
22
+ /** Convert Duration to fractional minutes. */
23
+ export function toMinutes(d) {
24
+ return d.ms / MS_PER_MINUTE;
25
+ }
26
+ /** Convert Duration to fractional hours. */
27
+ export function toHours(d) {
28
+ return d.ms / MS_PER_HOUR;
29
+ }
30
+ /** Convert Duration to fractional days (24-hour periods, NOT DST-safe). */
31
+ export function toDays(d) {
32
+ return d.ms / MS_PER_EXACT_DAY;
33
+ }
34
+ /**
35
+ * Length of a TimeRange in fractional minutes. Convenience for the
36
+ * very common `(range.end.epochMs - range.start.epochMs) / MS_PER_MINUTE`
37
+ * pattern in interaction/rule code. Composition with `toMinutes(duration(range))`
38
+ * would do the same thing in two steps.
39
+ */
40
+ export function durationInMinutes(range) {
41
+ return (range.end.epochMs - range.start.epochMs) / MS_PER_MINUTE;
42
+ }
43
+ //# sourceMappingURL=duration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duration.js","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAE9E,sCAAsC;AACtC,MAAM,UAAU,OAAO,CAAC,CAAS;IAC/B,OAAO,EAAE,EAAE,EAAE,CAAC,GAAG,aAAa,EAAE,CAAC;AACnC,CAAC;AAED,oCAAoC;AACpC,MAAM,UAAU,KAAK,CAAC,CAAS;IAC7B,OAAO,EAAE,EAAE,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC;AACjC,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,IAAI,CAAC,CAAS;IAC5B,OAAO,EAAE,EAAE,EAAE,CAAC,GAAG,gBAAgB,EAAE,CAAC;AACtC,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,OAAO,CAAC,CAAmB,EAAE,CAAmB;IAC9D,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;AACjD,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,QAAQ,CAAC,KAAgB;IACvC,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;AACzD,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,SAAS,CAAC,CAAW;IACnC,OAAO,CAAC,CAAC,EAAE,GAAG,aAAa,CAAC;AAC9B,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,OAAO,CAAC,CAAW;IACjC,OAAO,CAAC,CAAC,EAAE,GAAG,WAAW,CAAC;AAC5B,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,MAAM,CAAC,CAAW;IAChC,OAAO,CAAC,CAAC,EAAE,GAAG,gBAAgB,CAAC;AACjC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAgB;IAChD,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,aAAa,CAAC;AACnE,CAAC"}
@@ -0,0 +1,22 @@
1
+ /** Typed error union for calendar core operations. */
2
+ export type CalendarError = {
3
+ readonly type: 'INVALID_TIME_RANGE';
4
+ readonly message: string;
5
+ } | {
6
+ readonly type: 'INVALID_TIMEZONE';
7
+ readonly timezone: string;
8
+ } | {
9
+ readonly type: 'INVALID_GRANULARITY';
10
+ readonly minutes: number;
11
+ } | {
12
+ readonly type: 'INVALID_EVENT';
13
+ readonly field: string;
14
+ readonly message: string;
15
+ };
16
+ /** Error thrown by core validation (invalid date components, timezone, granularity, or time range). */
17
+ export declare class CalendarValidationError extends Error {
18
+ readonly error: CalendarError;
19
+ /** @param error - The typed error detail; also used to derive `message`. */
20
+ constructor(error: CalendarError);
21
+ }
22
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjE;IAAE,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAChE;IAAE,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAWzF,uGAAuG;AACvG,qBAAa,uBAAwB,SAAQ,KAAK;aAEpB,KAAK,EAAE,aAAa;IADhD,4EAA4E;gBAChD,KAAK,EAAE,aAAa;CAIjD"}
package/dist/errors.js ADDED
@@ -0,0 +1,19 @@
1
+ function errorMessage(error) {
2
+ switch (error.type) {
3
+ case 'INVALID_TIMEZONE': return `Invalid timezone: ${error.timezone}`;
4
+ case 'INVALID_GRANULARITY': return `Invalid granularity: ${error.minutes} minutes`;
5
+ case 'INVALID_TIME_RANGE': return error.message;
6
+ case 'INVALID_EVENT': return `${error.field}: ${error.message}`;
7
+ }
8
+ }
9
+ /** Error thrown by core validation (invalid date components, timezone, granularity, or time range). */
10
+ export class CalendarValidationError extends Error {
11
+ error;
12
+ /** @param error - The typed error detail; also used to derive `message`. */
13
+ constructor(error) {
14
+ super(errorMessage(error));
15
+ this.error = error;
16
+ this.name = 'CalendarValidationError';
17
+ }
18
+ }
19
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAOA,SAAS,YAAY,CAAC,KAAoB;IACxC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,kBAAkB,CAAC,CAAC,OAAO,qBAAqB,KAAK,CAAC,QAAQ,EAAE,CAAC;QACtE,KAAK,qBAAqB,CAAC,CAAC,OAAO,wBAAwB,KAAK,CAAC,OAAO,UAAU,CAAC;QACnF,KAAK,oBAAoB,CAAC,CAAC,OAAO,KAAK,CAAC,OAAO,CAAC;QAChD,KAAK,eAAe,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;IAClE,CAAC;AACH,CAAC;AAED,uGAAuG;AACvG,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAEpB;IAD5B,4EAA4E;IAC5E,YAA4B,KAAoB;QAC9C,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;QADD,UAAK,GAAL,KAAK,CAAe;QAE9C,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ import type { CalendarDateTime, Duration, TimeRange, FormatOptions } from './types.js';
2
+ /** Format a CalendarDateTime using Intl.DateTimeFormat. */
3
+ export declare function formatDateTime(dt: CalendarDateTime, options: FormatOptions): string;
4
+ /**
5
+ * Format a day number label for month grid cells.
6
+ * Returns just the number for most days, and "1. Mai" (short month name) for the 1st.
7
+ */
8
+ export declare function formatDayLabel(dt: CalendarDateTime, locale: string): string;
9
+ /** Format a TimeRange. Shows start – end with appropriate formatting. */
10
+ export declare function formatTimeRange(range: TimeRange, options: FormatOptions): string;
11
+ /** Format a Duration in a human-readable way. */
12
+ export declare function formatDuration(d: Duration): string;
13
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIvF,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,EAAE,EAAE,gBAAgB,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAanF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK3E;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAMhF;AAED,iDAAiD;AACjD,wBAAgB,cAAc,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAalD"}
package/dist/format.js ADDED
@@ -0,0 +1,51 @@
1
+ import { getDayOfMonth } from './datetime.js';
2
+ import { MS_PER_MINUTE, MINUTES_PER_HOUR } from './constants.js';
3
+ /** Format a CalendarDateTime using Intl.DateTimeFormat. */
4
+ export function formatDateTime(dt, options) {
5
+ const intlOptions = {
6
+ timeZone: dt.timezone,
7
+ };
8
+ if (options.dateStyle)
9
+ intlOptions.dateStyle = options.dateStyle;
10
+ if (options.timeStyle)
11
+ intlOptions.timeStyle = options.timeStyle;
12
+ // If neither dateStyle nor timeStyle, default to medium date
13
+ if (!options.dateStyle && !options.timeStyle) {
14
+ intlOptions.dateStyle = 'medium';
15
+ }
16
+ return new Intl.DateTimeFormat(options.locale, intlOptions).format(new Date(dt.epochMs));
17
+ }
18
+ /**
19
+ * Format a day number label for month grid cells.
20
+ * Returns just the number for most days, and "1. Mai" (short month name) for the 1st.
21
+ */
22
+ export function formatDayLabel(dt, locale) {
23
+ const d = getDayOfMonth(dt);
24
+ if (d !== 1)
25
+ return String(d);
26
+ const monthName = new Intl.DateTimeFormat(locale, { month: 'short', timeZone: dt.timezone }).format(new Date(dt.epochMs));
27
+ return `${d}. ${monthName}`;
28
+ }
29
+ /** Format a TimeRange. Shows start – end with appropriate formatting. */
30
+ export function formatTimeRange(range, options) {
31
+ const start = formatDateTime(range.start, options);
32
+ const end = formatDateTime(range.end, options);
33
+ if (start === end)
34
+ return start;
35
+ return `${start} – ${end}`;
36
+ }
37
+ /** Format a Duration in a human-readable way. */
38
+ export function formatDuration(d) {
39
+ const totalMinutes = Math.round(d.ms / MS_PER_MINUTE);
40
+ const hours = Math.floor(totalMinutes / MINUTES_PER_HOUR);
41
+ const minutes = totalMinutes % MINUTES_PER_HOUR;
42
+ const parts = [];
43
+ if (hours > 0) {
44
+ parts.push(`${hours} h`);
45
+ }
46
+ if (minutes > 0 || parts.length === 0) {
47
+ parts.push(`${minutes} min`);
48
+ }
49
+ return parts.join(' ');
50
+ }
51
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEjE,2DAA2D;AAC3D,MAAM,UAAU,cAAc,CAAC,EAAoB,EAAE,OAAsB;IACzE,MAAM,WAAW,GAA+B;QAC9C,QAAQ,EAAE,EAAE,CAAC,QAAQ;KACtB,CAAC;IACF,IAAI,OAAO,CAAC,SAAS;QAAE,WAAW,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACjE,IAAI,OAAO,CAAC,SAAS;QAAE,WAAW,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IAEjE,6DAA6D;IAC7D,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;QAC7C,WAAW,CAAC,SAAS,GAAG,QAAQ,CAAC;IACnC,CAAC;IAED,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;AAC3F,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAAoB,EAAE,MAAc;IACjE,MAAM,CAAC,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IAC5B,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1H,OAAO,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;AAC9B,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,eAAe,CAAC,KAAgB,EAAE,OAAsB;IACtE,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAE/C,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAChC,OAAO,GAAG,KAAK,MAAM,GAAG,EAAE,CAAC;AAC7B,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,cAAc,CAAC,CAAW;IACxC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,aAAa,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,gBAAgB,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,YAAY,GAAG,gBAAgB,CAAC;IAEhD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IAC3B,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,MAAM,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { CalendarDateTime, Granularity, TimeRange, TimeSlot, SnapStrategy } from './types.js';
2
+ /** Snap a datetime to the granularity grid. */
3
+ export declare function snap(dt: CalendarDateTime, granularity: Granularity, strategy: SnapStrategy): CalendarDateTime;
4
+ /** Generate time slots for a range at the given granularity.
5
+ * Contract policies (V1_PUBLIC_CONTRACT §11): the LAST slot is truncated to end
6
+ * exactly at `range.end` when the range is not an integer multiple of the
7
+ * granularity (final slot width may be shorter — a renderer must not assume
8
+ * uniform width); slots advance by ELAPSED milliseconds (§8), not wall-clock,
9
+ * so an "hourly" grid drifts off wall-clock hours across a DST transition. */
10
+ export declare function generateSlots(range: TimeRange, granularity: Granularity): TimeSlot[];
11
+ /** Calculate the slot index of a datetime relative to a day start. */
12
+ export declare function slotIndex(dt: CalendarDateTime, dayStart: CalendarDateTime, granularity: Granularity): number;
13
+ //# sourceMappingURL=granularity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"granularity.d.ts","sourceRoot":"","sources":["../src/granularity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAMnG,+CAA+C;AAC/C,wBAAgB,IAAI,CAClB,EAAE,EAAE,gBAAgB,EACpB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,YAAY,GACrB,gBAAgB,CA8BlB;AAED;;;;;+EAK+E;AAC/E,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,GAAG,QAAQ,EAAE,CAwBpF;AAED,sEAAsE;AACtE,wBAAgB,SAAS,CACvB,EAAE,EAAE,gBAAgB,EACpB,QAAQ,EAAE,gBAAgB,EAC1B,WAAW,EAAE,WAAW,GACvB,MAAM,CAOR"}
@@ -0,0 +1,70 @@
1
+ import { CalendarValidationError } from './errors.js';
2
+ import { getYear, getMonth, getDayOfMonth, createDateTime, addDays, toMinutesOfDay } from './datetime.js';
3
+ import { monthIndex } from './vocabulary.js';
4
+ import { MINUTES_PER_HOUR, MINUTES_PER_DAY, MS_PER_MINUTE } from './constants.js';
5
+ /** Snap a datetime to the granularity grid. */
6
+ export function snap(dt, granularity, strategy) {
7
+ if (granularity.minutes <= 0) {
8
+ throw new CalendarValidationError({ type: 'INVALID_GRANULARITY', minutes: granularity.minutes });
9
+ }
10
+ const totalMinutes = toMinutesOfDay(dt);
11
+ const interval = granularity.minutes;
12
+ let snapped;
13
+ switch (strategy) {
14
+ case 'floor':
15
+ snapped = Math.floor(totalMinutes / interval) * interval;
16
+ break;
17
+ case 'ceil':
18
+ snapped = Math.ceil(totalMinutes / interval) * interval;
19
+ break;
20
+ case 'round':
21
+ snapped = Math.round(totalMinutes / interval) * interval;
22
+ break;
23
+ }
24
+ // Ceil/round past end of day → start of next day
25
+ if (snapped >= MINUTES_PER_DAY) {
26
+ return addDays(createDateTime(getYear(dt), monthIndex(getMonth(dt)), getDayOfMonth(dt), 0, 0, dt.timezone), 1);
27
+ }
28
+ // Use wall-clock construction (DST-safe) instead of elapsed-time addMinutes
29
+ const snapH = Math.floor(snapped / MINUTES_PER_HOUR);
30
+ const snapM = snapped % MINUTES_PER_HOUR;
31
+ return createDateTime(getYear(dt), monthIndex(getMonth(dt)), getDayOfMonth(dt), snapH, snapM, dt.timezone);
32
+ }
33
+ /** Generate time slots for a range at the given granularity.
34
+ * Contract policies (V1_PUBLIC_CONTRACT §11): the LAST slot is truncated to end
35
+ * exactly at `range.end` when the range is not an integer multiple of the
36
+ * granularity (final slot width may be shorter — a renderer must not assume
37
+ * uniform width); slots advance by ELAPSED milliseconds (§8), not wall-clock,
38
+ * so an "hourly" grid drifts off wall-clock hours across a DST transition. */
39
+ export function generateSlots(range, granularity) {
40
+ if (granularity.minutes <= 0) {
41
+ throw new CalendarValidationError({ type: 'INVALID_GRANULARITY', minutes: granularity.minutes });
42
+ }
43
+ const intervalMs = granularity.minutes * MS_PER_MINUTE;
44
+ const slots = [];
45
+ let current = range.start.epochMs;
46
+ let index = 0;
47
+ while (current < range.end.epochMs) {
48
+ const next = Math.min(current + intervalMs, range.end.epochMs);
49
+ slots.push({
50
+ range: {
51
+ start: { epochMs: current, timezone: range.start.timezone },
52
+ end: { epochMs: next, timezone: range.start.timezone },
53
+ },
54
+ index,
55
+ });
56
+ current = next;
57
+ index++;
58
+ }
59
+ return slots;
60
+ }
61
+ /** Calculate the slot index of a datetime relative to a day start. */
62
+ export function slotIndex(dt, dayStart, granularity) {
63
+ if (granularity.minutes <= 0) {
64
+ throw new CalendarValidationError({ type: 'INVALID_GRANULARITY', minutes: granularity.minutes });
65
+ }
66
+ const diffMs = dt.epochMs - dayStart.epochMs;
67
+ const diffMinutes = diffMs / MS_PER_MINUTE;
68
+ return Math.floor(diffMinutes / granularity.minutes);
69
+ }
70
+ //# sourceMappingURL=granularity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"granularity.js","sourceRoot":"","sources":["../src/granularity.ts"],"names":[],"mappings":"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"}