@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.
- package/LICENSE +57 -0
- package/README.md +73 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/constants.d.ts +29 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +29 -0
- package/dist/constants.js.map +1 -0
- package/dist/date-strings.d.ts +23 -0
- package/dist/date-strings.d.ts.map +1 -0
- package/dist/date-strings.js +25 -0
- package/dist/date-strings.js.map +1 -0
- package/dist/datetime.d.ts +100 -0
- package/dist/datetime.d.ts.map +1 -0
- package/dist/datetime.js +289 -0
- package/dist/datetime.js.map +1 -0
- package/dist/duration.d.ts +25 -0
- package/dist/duration.d.ts.map +1 -0
- package/dist/duration.js +43 -0
- package/dist/duration.js.map +1 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +19 -0
- package/dist/errors.js.map +1 -0
- package/dist/format.d.ts +13 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +51 -0
- package/dist/format.js.map +1 -0
- package/dist/granularity.d.ts +13 -0
- package/dist/granularity.d.ts.map +1 -0
- package/dist/granularity.js +70 -0
- package/dist/granularity.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/time-range.d.ts +38 -0
- package/dist/time-range.d.ts.map +1 -0
- package/dist/time-range.js +67 -0
- package/dist/time-range.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/vocabulary.d.ts +40 -0
- package/dist/vocabulary.d.ts.map +1 -0
- package/dist/vocabulary.js +38 -0
- package/dist/vocabulary.js.map +1 -0
- package/package.json +45 -0
package/dist/datetime.js
ADDED
|
@@ -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"}
|
package/dist/duration.js
ADDED
|
@@ -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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -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"}
|
package/dist/format.d.ts
ADDED
|
@@ -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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"}
|