@alexstukovnikov/oz-time 1.0.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/README.md ADDED
@@ -0,0 +1,567 @@
1
+ # OzTime
2
+
3
+ JavaScript-библиотека для работы с датой и временем.
4
+
5
+ `OzTime` предоставляет неизменяемые объекты времени, фабричные функции создания экземпляров, арифметику по фиксированным и календарным единицам, сравнение, форматирование, работу с часовыми поясами, интервалы и длительности.
6
+
7
+ [Полная документация API](https://AlexStukovnikov.github.io/oz-time/)
8
+
9
+ ## Содержание
10
+
11
+ - [Почему OzTime](#почему-oztime)
12
+ - [Возможности](#возможности)
13
+ - [Установка](#установка)
14
+ - [Быстрый старт](#быстрый-старт)
15
+ - [Что использовать в каком случае](#что-использовать-в-каком-случае)
16
+ - [Создание экземпляров](#создание-экземпляров)
17
+ - [Арифметика](#арифметика)
18
+ - [Сравнение](#сравнение)
19
+ - [Форматирование](#форматирование)
20
+ - [Часовые пояса](#часовые-пояса)
21
+ - [Интервалы](#интервалы)
22
+ - [Длительности](#длительности)
23
+ - [Поддерживаемые единицы времени](#поддерживаемые-единицы-времени)
24
+ - [Токены форматирования](#токены-форматирования)
25
+ - [Публичный API](#публичный-api)
26
+ - [FAQ](#faq)
27
+ - [Разработка](#разработка)
28
+ - [Генерация документации](#генерация-документации)
29
+ - [Ограничения и особенности](#ограничения-и-особенности)
30
+ - [Лицензия](#лицензия)
31
+
32
+ ## Почему OzTime
33
+
34
+ `OzTime` подойдёт, если нужна небольшая и понятная библиотека без перегруженного API.
35
+
36
+ Основные идеи:
37
+
38
+ - время хранится как Unix timestamp в миллисекундах;
39
+ - экземпляры `OzTime` неизменяемы;
40
+ - часовой пояс и локаль сохраняются как часть объекта;
41
+ - арифметика, сравнение и форматирование работают через единый API.
42
+
43
+ Библиотека хорошо подходит для:
44
+
45
+ - прикладных JavaScript-проектов;
46
+ - учебных и дипломных проектов;
47
+ - небольших библиотек и утилит;
48
+ - кода, где важна предсказуемость и неизменяемость.
49
+
50
+ ## Возможности
51
+
52
+ - Неизменяемые экземпляры `OzTime`
53
+ - Фабричные функции:
54
+ - `now`
55
+ - `fromTimestamp`
56
+ - `fromDate`
57
+ - `fromISO`
58
+ - `fromComponents`
59
+ - Арифметика по времени:
60
+ - `add`
61
+ - `subtract`
62
+ - Сравнение:
63
+ - `isSame`
64
+ - `isBefore`
65
+ - `isAfter`
66
+ - `isBetween`
67
+ - Форматирование по шаблону
68
+ - Работа с часовыми поясами:
69
+ - `setTimezone`
70
+ - `getTimezoneOffset`
71
+ - Интервалы:
72
+ - `Interval`
73
+ - `interval`
74
+ - Длительности:
75
+ - `Duration`
76
+ - `duration`
77
+ - Календарные утилиты:
78
+ - `isLeapYear`
79
+ - `daysInMonth`
80
+
81
+ ## Установка
82
+
83
+ ### Как зависимость проекта
84
+
85
+ ```bash
86
+ npm install @alexstukovnikov/oz-time
87
+ ```
88
+
89
+ ### Для локальной разработки
90
+
91
+ ```bash
92
+ npm install
93
+ ```
94
+
95
+ ## Быстрый старт
96
+
97
+ ```js
98
+ import { now, fromISO, duration, interval } from '@alexstukovnikov/oz-time';
99
+
100
+ const current = now('Europe/Moscow', 'ru-RU');
101
+ const release = fromISO('2024-05-25T12:00:00Z', 'UTC', 'ru-RU');
102
+
103
+ const nextWeek = release.add(7, 'day');
104
+ const diffHours = nextWeek.diff(release, 'hour');
105
+
106
+ console.log(current.getTimezone()); // ожидаемый результат: Europe/Moscow
107
+ console.log(nextWeek.format('DD.MM.YYYY HH:mm')); // ожидаемый результат: 01.06.2024 12:00
108
+ console.log(diffHours); // ожидаемый результат: 168
109
+
110
+ const range = interval(release, nextWeek);
111
+ console.log(range.contains(release)); // ожидаемый результат: true
112
+
113
+ const oneHour = duration(1, 'hour');
114
+ console.log(oneHour.asMinutes()); // ожидаемый результат: 60
115
+ ```
116
+
117
+ ## Что использовать в каком случае
118
+
119
+ | Сценарий | Что использовать | Почему |
120
+ | ----------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------ |
121
+ | Нужен текущий момент времени | `now()` | Самый простой способ получить новый экземпляр `OzTime` для текущего момента. |
122
+ | Есть Unix timestamp в миллисекундах | `fromTimestamp()` | Подходит для работы с временем из API, БД и системных источников. |
123
+ | Есть нативный `Date` | `fromDate()` | Удобно интегрировать библиотеку в существующий код на JavaScript. |
124
+ | Есть ISO-строка | `fromISO()` | Лучший вариант для внешних API и сериализованных значений времени. |
125
+ | Нужно создать дату вручную из компонентов | `fromComponents()` | Подходит, когда год, месяц, день и время приходят по отдельности. |
126
+ | Нужно прибавить или вычесть время | `add()` / `subtract()` | Возвращают новый экземпляр и не изменяют исходный объект. |
127
+ | Нужно сравнить два значения | `isSame()` / `isBefore()` / `isAfter()` | Дают читаемый API для логики сравнения. |
128
+ | Нужно проверить попадание в диапазон | `isBetween()` | Удобнее, чем писать проверку вручную. |
129
+ | Нужно получить красивую строку | `format()` | Форматирование по шаблону с токенами. |
130
+ | Нужно изменить отображаемый часовой пояс | `setTimezone()` | Меняет timezone у нового экземпляра, не меняя абсолютный момент времени. |
131
+ | Нужно узнать смещение UTC | `getTimezoneOffset()` | Полезно для отображения, отладки и расчётов. |
132
+ | Нужно описать диапазон времени | `Interval` / `interval()` | Подходит для проверки попадания, пересечений и длительности интервала. |
133
+ | Нужно описать фиксированную длительность | `Duration` / `duration()` | Удобно для преобразования между миллисекундами, секундами, минутами, часами и днями. |
134
+ | Нужно проверить високосный год | `isLeapYear()` | Простая календарная утилита. |
135
+ | Нужно узнать число дней в месяце | `daysInMonth()` | Удобно для валидации и генерации дат. |
136
+
137
+ ## Создание экземпляров
138
+
139
+ ### `now(timezone?, locale?)`
140
+
141
+ Создаёт экземпляр `OzTime` для текущего момента времени.
142
+
143
+ ```js
144
+ import { now } from '@alexstukovnikov/oz-time';
145
+
146
+ const current = now('Europe/Moscow', 'ru-RU');
147
+ console.log(current.getTimezone()); // ожидаемый результат: Europe/Moscow
148
+ ```
149
+
150
+ ### `fromTimestamp(timestamp, timezone?, locale?)`
151
+
152
+ Создаёт экземпляр из Unix timestamp в миллисекундах.
153
+
154
+ ```js
155
+ import { fromTimestamp } from '@alexstukovnikov/oz-time';
156
+
157
+ const time = fromTimestamp(1716638400000, 'UTC', 'ru-RU');
158
+ console.log(time.toISOString()); // ожидаемый результат: 2024-05-25T12:00:00.000Z
159
+ ```
160
+
161
+ ### `fromDate(date, timezone?, locale?)`
162
+
163
+ Создаёт экземпляр из объекта `Date`.
164
+
165
+ ```js
166
+ import { fromDate } from '@alexstukovnikov/oz-time';
167
+
168
+ const time = fromDate(new Date('2024-05-25T12:00:00Z'), 'UTC', 'ru-RU');
169
+ console.log(time.toTimestamp()); // ожидаемый результат: 1716638400000
170
+ ```
171
+
172
+ ### `fromISO(isoString, timezone?, locale?)`
173
+
174
+ Создаёт экземпляр из ISO-строки.
175
+
176
+ ```js
177
+ import { fromISO } from '@alexstukovnikov/oz-time';
178
+
179
+ const time = fromISO('2024-05-25T12:00:00Z', 'UTC', 'ru-RU');
180
+ console.log(time.format('DD.MM.YYYY HH:mm')); // ожидаемый результат: 25.05.2024 12:00
181
+ ```
182
+
183
+ ### `fromComponents(year, month, day, hour?, minute?, second?, ms?, timezone?, locale?)`
184
+
185
+ Создаёт экземпляр из отдельных компонентов даты и времени.
186
+
187
+ ```js
188
+ import { fromComponents } from '@alexstukovnikov/oz-time';
189
+
190
+ const time = fromComponents(2024, 5, 25, 12, 0, 0, 0, 'UTC', 'ru-RU');
191
+ console.log(time.toISOString()); // ожидаемый результат: 2024-05-25T12:00:00.000Z
192
+ ```
193
+
194
+ ## Арифметика
195
+
196
+ Все арифметические операции возвращают новый экземпляр `OzTime` и не изменяют исходный объект.
197
+
198
+ ### Добавление времени
199
+
200
+ ```js
201
+ import { fromISO } from '@alexstukovnikov/oz-time';
202
+
203
+ const time = fromISO('2024-05-25T12:00:00Z');
204
+ const result = time.add(1, 'day');
205
+
206
+ console.log(result.toISOString()); // ожидаемый результат: 2024-05-26T12:00:00.000Z
207
+ ```
208
+
209
+ ### Вычитание времени
210
+
211
+ ```js
212
+ import { fromISO } from '@alexstukovnikov/oz-time';
213
+
214
+ const time = fromISO('2024-05-25T12:00:00Z');
215
+ const result = time.subtract(2, 'hour');
216
+
217
+ console.log(result.toISOString()); // ожидаемый результат: 2024-05-25T10:00:00.000Z
218
+ ```
219
+
220
+ ### Разница между датами
221
+
222
+ ```js
223
+ import { fromISO } from '@alexstukovnikov/oz-time';
224
+
225
+ const start = fromISO('2024-05-25T12:00:00Z');
226
+ const end = fromISO('2024-05-25T15:00:00Z');
227
+
228
+ console.log(end.diff(start, 'hour')); // ожидаемый результат: 3
229
+ ```
230
+
231
+ ### Календарная арифметика
232
+
233
+ ```js
234
+ import { fromISO } from '@alexstukovnikov/oz-time';
235
+
236
+ const date = fromISO('2024-01-31T00:00:00Z');
237
+ const result = date.add(1, 'month');
238
+
239
+ console.log(result.toISOString()); // ожидаемый результат: 2024-02-29T00:00:00.000Z
240
+ ```
241
+
242
+ ## Сравнение
243
+
244
+ ### `isSame`
245
+
246
+ ```js
247
+ import { fromISO } from '@alexstukovnikov/oz-time';
248
+
249
+ const a = fromISO('2024-05-25T12:00:00.100Z');
250
+ const b = fromISO('2024-05-25T12:00:00.900Z');
251
+
252
+ console.log(a.isSame(b, 'second')); // ожидаемый результат: true
253
+ ```
254
+
255
+ ### `isBefore`
256
+
257
+ ```js
258
+ import { fromISO } from '@alexstukovnikov/oz-time';
259
+
260
+ const a = fromISO('2024-05-25T12:00:00Z');
261
+ const b = fromISO('2024-05-26T12:00:00Z');
262
+
263
+ console.log(a.isBefore(b)); // ожидаемый результат: true
264
+ ```
265
+
266
+ ### `isAfter`
267
+
268
+ ```js
269
+ import { fromISO } from '@alexstukovnikov/oz-time';
270
+
271
+ const a = fromISO('2024-05-26T12:00:00Z');
272
+ const b = fromISO('2024-05-25T12:00:00Z');
273
+
274
+ console.log(a.isAfter(b)); // ожидаемый результат: true
275
+ ```
276
+
277
+ ### `isBetween`
278
+
279
+ ```js
280
+ import { fromISO } from '@alexstukovnikov/oz-time';
281
+
282
+ const target = fromISO('2024-05-25T12:00:00Z');
283
+ const start = fromISO('2024-05-25T10:00:00Z');
284
+ const end = fromISO('2024-05-25T14:00:00Z');
285
+
286
+ console.log(target.isBetween(start, end)); // ожидаемый результат: true
287
+ ```
288
+
289
+ ## Форматирование
290
+
291
+ Функция `format()` и метод `OzTime#format()` поддерживают шаблоны.
292
+
293
+ ```js
294
+ import { fromISO } from '@alexstukovnikov/oz-time';
295
+
296
+ const time = fromISO('2024-05-25T12:00:00Z', 'UTC', 'ru-RU');
297
+
298
+ console.log(time.format('DD.MM.YYYY HH:mm')); // ожидаемый результат: 25.05.2024 12:00
299
+ console.log(time.format('dddd, D MMMM YYYY', 'ru-RU')); // пример: суббота, 25 мая 2024
300
+ ```
301
+
302
+ ## Часовые пояса
303
+
304
+ `OzTime` хранит абсолютное время как timestamp, а часовой пояс используется как метаданные для отображения и форматирования.
305
+
306
+ ### Смена часового пояса
307
+
308
+ ```js
309
+ import { fromISO } from '@alexstukovnikov/oz-time';
310
+
311
+ const time = fromISO('2024-05-25T12:00:00Z', 'UTC', 'ru-RU');
312
+ const moscow = time.setTimezone('Europe/Moscow');
313
+
314
+ console.log(moscow.getTimezone()); // ожидаемый результат: Europe/Moscow
315
+ ```
316
+
317
+ ### Смещение относительно UTC
318
+
319
+ ```js
320
+ import { fromISO } from '@alexstukovnikov/oz-time';
321
+
322
+ const time = fromISO('2024-05-25T12:00:00Z', 'Europe/Moscow', 'ru-RU');
323
+ console.log(time.getTimezoneOffset()); // ожидаемый результат: 180
324
+ ```
325
+
326
+ ## Интервалы
327
+
328
+ Интервал представляет диапазон между двумя экземплярами `OzTime`, включая границы.
329
+
330
+ ```js
331
+ import { interval, fromISO } from '@alexstukovnikov/oz-time';
332
+
333
+ const start = fromISO('2024-05-25T10:00:00Z');
334
+ const end = fromISO('2024-05-25T12:00:00Z');
335
+ const range = interval(start, end);
336
+
337
+ console.log(range.contains(fromISO('2024-05-25T11:00:00Z'))); // ожидаемый результат: true
338
+ console.log(range.duration('hour')); // ожидаемый результат: 2
339
+ ```
340
+
341
+ ### Пересечение интервалов
342
+
343
+ ```js
344
+ import { Interval, fromISO } from '@alexstukovnikov/oz-time';
345
+
346
+ const a = new Interval(fromISO('2024-05-25T10:00:00Z'), fromISO('2024-05-25T12:00:00Z'));
347
+
348
+ const b = new Interval(fromISO('2024-05-25T11:00:00Z'), fromISO('2024-05-25T13:00:00Z'));
349
+
350
+ console.log(a.overlaps(b)); // ожидаемый результат: true
351
+ ```
352
+
353
+ ## Длительности
354
+
355
+ `Duration` используется для работы с фиксированными единицами времени.
356
+
357
+ ```js
358
+ import { duration } from '@alexstukovnikov/oz-time';
359
+
360
+ const twoHours = duration(2, 'hour');
361
+
362
+ console.log(twoHours.asMilliseconds()); // ожидаемый результат: 7200000
363
+ console.log(twoHours.asMinutes()); // ожидаемый результат: 120
364
+ console.log(twoHours.asHours()); // ожидаемый результат: 2
365
+ ```
366
+
367
+ ### Сложение длительностей
368
+
369
+ ```js
370
+ import { duration } from '@alexstukovnikov/oz-time';
371
+
372
+ const a = duration(30, 'minute');
373
+ const b = duration(45, 'minute');
374
+
375
+ console.log(a.add(b).asMinutes()); // ожидаемый результат: 75
376
+ ```
377
+
378
+ ## Поддерживаемые единицы времени
379
+
380
+ Поддерживаются канонические имена и алиасы.
381
+
382
+ | Единица | Каноническое имя | Алиасы |
383
+ | ------------ | ---------------- | -------------------- |
384
+ | Миллисекунда | `millisecond` | `milliseconds`, `ms` |
385
+ | Секунда | `second` | `seconds`, `s` |
386
+ | Минута | `minute` | `minutes`, `m` |
387
+ | Час | `hour` | `hours`, `h` |
388
+ | День | `day` | `days`, `d` |
389
+ | Месяц | `month` | `months` |
390
+ | Год | `year` | `years`, `y` |
391
+
392
+ Фиксированные единицы:
393
+
394
+ - `millisecond`
395
+ - `second`
396
+ - `minute`
397
+ - `hour`
398
+ - `day`
399
+
400
+ Календарные единицы:
401
+
402
+ - `month`
403
+ - `year`
404
+
405
+ ## Токены форматирования
406
+
407
+ | Токен | Описание | Пример |
408
+ | ------ | ---------------------------------- | ------------- |
409
+ | `YYYY` | Год из 4 цифр | `2024` |
410
+ | `YY` | Короткий год | `24` |
411
+ | `MMMM` | Полное название месяца | `май` / `May` |
412
+ | `MMM` | Короткое название месяца | `мая` / `May` |
413
+ | `MM` | Месяц с ведущим нулём | `05` |
414
+ | `M` | Месяц без ведущего нуля | `5` |
415
+ | `dddd` | Полное название дня недели | `суббота` |
416
+ | `ddd` | Короткое название дня недели | `сб` |
417
+ | `DD` | День месяца с ведущим нулём | `25` |
418
+ | `D` | День месяца без ведущего нуля | `25` |
419
+ | `HH` | Часы в 24-часовом формате | `09`, `18` |
420
+ | `H` | Часы в 24-часовом формате без нуля | `9`, `18` |
421
+ | `hh` | Часы в 12-часовом формате | `01`, `12` |
422
+ | `h` | Часы в 12-часовом формате без нуля | `1`, `12` |
423
+ | `mm` | Минуты | `07` |
424
+ | `ss` | Секунды | `05` |
425
+ | `SSS` | Миллисекунды | `123` |
426
+ | `A` | AM/PM | `AM`, `PM` |
427
+
428
+ ## Публичный API
429
+
430
+ ### Core
431
+
432
+ - `OzTime`
433
+ - `now`
434
+ - `fromTimestamp`
435
+ - `fromDate`
436
+ - `fromISO`
437
+ - `fromComponents`
438
+
439
+ ### Арифметика и сравнение
440
+
441
+ - `add`
442
+ - `subtract`
443
+ - `isSame`
444
+ - `isBefore`
445
+ - `isAfter`
446
+ - `isBetween`
447
+
448
+ ### Форматирование и часовые пояса
449
+
450
+ - `format`
451
+ - `setTimezone`
452
+ - `getTimezoneOffset`
453
+
454
+ ### Интервалы и длительности
455
+
456
+ - `Interval`
457
+ - `interval`
458
+ - `Duration`
459
+ - `duration`
460
+
461
+ ### Календарные утилиты
462
+
463
+ - `isLeapYear`
464
+ - `daysInMonth`
465
+
466
+ ## FAQ
467
+
468
+ ### Чем `fromISO()` отличается от `fromTimestamp()`?
469
+
470
+ `fromISO()` принимает строку в формате ISO 8601 и сначала парсит её, а `fromTimestamp()` принимает уже готовое число миллисекунд. Если данные приходят из JSON API как строка, чаще удобнее `fromISO()`. Если уже есть timestamp из базы, системы или вычислений, лучше использовать `fromTimestamp()`.
471
+
472
+ ### Меняет ли `setTimezone()` сам момент времени?
473
+
474
+ Нет. `setTimezone()` не меняет абсолютный момент времени. Он возвращает новый экземпляр с тем же timestamp, но с другим часовым поясом для форматирования и вычисления смещения.
475
+
476
+ ### Изменяют ли методы `add()` и `subtract()` текущий объект?
477
+
478
+ Нет. `OzTime` построен как неизменяемый объект. Все операции возвращают новый экземпляр, а исходный объект остаётся без изменений.
479
+
480
+ ### Почему `Duration` не поддерживает `month` и `year`?
481
+
482
+ Потому что `Duration` предназначен только для фиксированных единиц времени, которые можно точно перевести в миллисекунды. Месяцы и годы имеют переменную длину, поэтому для них используется календарная арифметика через `OzTime#add()` и `OzTime#diff()`.
483
+
484
+ ### Почему `diff(..., 'month')` и `diff(..., 'year')` работают не так, как разница в миллисекундах?
485
+
486
+ Потому что для месяцев и лет применяется календарная логика, а не деление на фиксированное число миллисекунд. Это позволяет корректнее учитывать разную длину месяцев и переходы между датами.
487
+
488
+ ### Что лучше использовать: метод экземпляра или функцию модуля?
489
+
490
+ Если уже есть экземпляр `OzTime`, обычно удобнее использовать методы экземпляра: `time.add(...)`, `time.format(...)`, `time.isBefore(...)`. Если нужен функциональный стиль или работаешь с импортированными утилитами напрямую, можно использовать функции модулей.
491
+
492
+ ### В каком часовом поясе хранится время внутри `OzTime`?
493
+
494
+ Внутри хранится Unix timestamp в миллисекундах. Это абсолютное значение времени. Часовой пояс хранится отдельно как метаданные экземпляра.
495
+
496
+ ### Можно ли использовать библиотеку без locale и timezone?
497
+
498
+ Да. По умолчанию используются:
499
+
500
+ - `timezone = 'UTC'`
501
+ - `locale = 'en-US'`
502
+
503
+ ### Когда использовать `Interval`, а когда `Duration`?
504
+
505
+ Используй `Interval`, когда есть начало и конец диапазона. Используй `Duration`, когда нужна именно фиксированная длина времени, например 2 часа, 30 минут или 7 дней.
506
+
507
+ ## Разработка
508
+
509
+ ### Запуск dev-режима
510
+
511
+ ```bash
512
+ npm run dev
513
+ ```
514
+
515
+ ### Сборка библиотеки
516
+
517
+ ```bash
518
+ npm run build
519
+ ```
520
+
521
+ ### Запуск тестов
522
+
523
+ ```bash
524
+ npm test
525
+ ```
526
+
527
+ ### Однократный запуск тестов
528
+
529
+ ```bash
530
+ npm run test:run
531
+ ```
532
+
533
+ ### Покрытие тестами
534
+
535
+ ```bash
536
+ npm run test:coverage
537
+ ```
538
+
539
+ ## Генерация документации
540
+
541
+ ### Генерация HTML-документации JSDoc
542
+
543
+ ```bash
544
+ npm run docs
545
+ ```
546
+
547
+ ### Полная пересборка документации
548
+
549
+ ```bash
550
+ npm run docs:build
551
+ ```
552
+
553
+ После генерации документация будет находиться в папке `docs/`.
554
+
555
+ ## Ограничения и особенности
556
+
557
+ - Внутреннее значение времени хранится как Unix timestamp в миллисекундах.
558
+ - Часовой пояс не меняет абсолютный момент времени, а влияет на форматирование и вычисление смещения.
559
+ - Методы `add`, `subtract`, `setTimezone` и другие операции не изменяют текущий экземпляр, а возвращают новый.
560
+ - `Duration` поддерживает только фиксированные единицы времени.
561
+ - Календарная арифметика через `month` и `year` учитывает реальную длину месяцев.
562
+ - Разница в `month` и `year` считается календарно, а не через точное число миллисекунд.
563
+ - Поддержка часовых поясов зависит от `Intl` и доступных IANA time zone в среде выполнения.
564
+
565
+ ## Лицензия
566
+
567
+ ISC
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e={millisecond:`millisecond`,milliseconds:`millisecond`,ms:`millisecond`,second:`second`,seconds:`second`,s:`second`,minute:`minute`,minutes:`minute`,m:`minute`,hour:`hour`,hours:`hour`,h:`hour`,day:`day`,days:`day`,d:`day`,month:`month`,months:`month`,year:`year`,years:`year`,y:`year`},t={millisecond:1,second:1e3,minute:60*1e3,hour:3600*1e3,day:1440*60*1e3},n=[`month`,`year`];function r(t){if(typeof t!=`string`||t.trim()===``)throw TypeError(`normalizeUnit: unit must be a non-empty string`);let n=e[t.toLowerCase().trim()];if(!n)throw Error(`Unsupported unit: ${t}`);return n}function i(e){return r(e)in t}function a(e){return n.includes(r(e))}function o(e){let n=r(e);if(!(n in t))throw Error(`Unit cannot be converted to milliseconds exactly: ${e}`);return t[n]}function s(e,t){if(typeof e!=`number`||Number.isNaN(e))throw TypeError(`${t}: timestamp must be a valid number`)}function c(e,t){if(typeof e!=`number`||Number.isNaN(e))throw TypeError(`${t}: amount must be a valid number`)}function l(e,t){if(!(e instanceof B))throw TypeError(`${t} must be OzTime`)}function u(e,t){let n=new Date(e),r=new Date(t),i=(n.getUTCFullYear()-r.getUTCFullYear())*12+(n.getUTCMonth()-r.getUTCMonth()),a=n.getUTCDate(),o=r.getUTCDate();return i>0&&a<o?--i:i<0&&a>o&&(i+=1),i}function d(e,t){let n=new Date(e),r=new Date(t),i=n.getUTCFullYear()-r.getUTCFullYear(),a=n.getUTCMonth(),o=r.getUTCMonth(),s=n.getUTCDate(),c=r.getUTCDate();return i>0&&(a<o||a===o&&s<c)?--i:i<0&&(a>o||a===o&&s>c)&&(i+=1),i}function f(e){if(!Number.isInteger(e))throw TypeError(`isLeapYear: year must be an integer`);return e%4==0&&(e%100!=0||e%400==0)}function p(e,t){if(!Number.isInteger(e)||!Number.isInteger(t))throw TypeError(`daysInMonth: year and month must be integers`);if(t<1||t>12)throw RangeError(`daysInMonth: month must be between 1 and 12`);return new Date(Date.UTC(e,t,0)).getUTCDate()}function m(e,t,n){s(e,`addByFixedUnit`),c(t,`addByFixedUnit`);let a=r(n);if(!i(a))throw Error(`addByFixedUnit does not support calendar unit: ${n}`);return e+t*o(a)}function h(e,t,n){s(e,`addByCalendarUnit`),c(t,`addByCalendarUnit`);let i=r(n),a=new Date(e);if(i===`month`){let e=a.getUTCDate();a.setUTCDate(1),a.setUTCMonth(a.getUTCMonth()+t);let n=p(a.getUTCFullYear(),a.getUTCMonth()+1);return a.setUTCDate(Math.min(e,n)),a.getTime()}if(i===`year`){let e=a.getUTCMonth(),n=a.getUTCDate();a.setUTCDate(1),a.setUTCFullYear(a.getUTCFullYear()+t),a.setUTCMonth(e);let r=p(a.getUTCFullYear(),e+1);return a.setUTCDate(Math.min(n,r)),a.getTime()}throw Error(`addByCalendarUnit supports only month and year: ${n}`)}function g(e,t,n=`millisecond`){l(e,`left`),l(t,`right`);let a=r(n),s=e.getTimestamp(),c=t.getTimestamp();if(i(a))return(s-c)/o(a);if(a===`month`)return u(s,c);if(a===`year`)return d(s,c);throw Error(`Unsupported unit: ${n}`)}function _(e,t){if(!(e instanceof B))throw TypeError(`${t} must be OzTime`)}function v(e){if(typeof e!=`number`||Number.isNaN(e))throw TypeError(`amount must be a valid number`)}function y(e,t,n){_(e,`time`),v(t);let o=r(n),s=e.getTimestamp(),c;return i(o)?c=m(s,t,o):a(o)&&(c=h(s,t,o)),new B(c,e.getTimezone(),e.getLocale())}function b(e,t,n){return _(e,`time`),v(t),y(e,-t,n)}function x(e){if(!(e instanceof B))throw TypeError(`format: first argument must be OzTime`)}function S(e,t=2){return String(e).padStart(t,`0`)}function C(e,t){let n=new Intl.DateTimeFormat(t,{timeZone:e.getTimezone(),year:`numeric`,month:`numeric`,day:`numeric`,hour:`numeric`,minute:`2-digit`,second:`2-digit`,hour12:!1}).formatToParts(new Date(e.getTimestamp()));return Object.fromEntries(n.map(e=>[e.type,e.value]))}function w(e,t,n){return new Intl.DateTimeFormat(t,{timeZone:e.getTimezone(),month:n}).format(new Date(e.getTimestamp()))}function T(e,t,n){return new Intl.DateTimeFormat(t,{timeZone:e.getTimezone(),weekday:n}).format(new Date(e.getTimestamp()))}function E(e,t,n){if(x(e),typeof t!=`string`||t.trim()===``)throw TypeError(`format: template must be a non-empty string`);let r=n??e.getLocale(),i=C(e,r),a=Number(i.year),o=Number(i.month),s=Number(i.day),c=Number(i.hour),l=Number(i.minute),u=Number(i.second),d=new Date(e.getTimestamp()).getUTCMilliseconds(),f=c%12,p=f===0?12:f,m=c>=12?`PM`:`AM`,h={YYYY:String(a),YY:String(a).slice(-2),MMMM:w(e,r,`long`),MMM:w(e,r,`short`),MM:S(o),M:String(o),dddd:T(e,r,`long`),ddd:T(e,r,`short`),DD:S(s),D:String(s),HH:S(c),H:String(c),hh:S(p),h:String(p),mm:S(l),ss:S(u),SSS:S(d,3),A:m};return t.replace(/YYYY|MMMM|MMM|MM|M|dddd|ddd|DD|D|HH|H|hh|h|mm|ss|SSS|YY|A/g,e=>h[e]??e)}function D(e,t){if(!(e instanceof B))throw TypeError(`${t} must be OzTime`)}function O(e,t){let n=r(t),i=new Date(e);switch(n){case`millisecond`:return i.getTime();case`second`:i.setUTCMilliseconds(0);break;case`minute`:i.setUTCSeconds(0,0);break;case`hour`:i.setUTCMinutes(0,0,0);break;case`day`:i.setUTCHours(0,0,0,0);break;case`month`:i.setUTCHours(0,0,0,0),i.setUTCDate(1);break;case`year`:i.setUTCHours(0,0,0,0),i.setUTCDate(1),i.setUTCMonth(0);break;default:throw Error(`Unsupported unit for truncateToUnit: ${t}`)}return i.getTime()}function k(e,t,n=`millisecond`){return D(e,`a`),D(t,`b`),O(e.getTimestamp(),n)===O(t.getTimestamp(),n)}function A(e,t,n=`millisecond`){return D(e,`a`),D(t,`b`),O(e.getTimestamp(),n)<O(t.getTimestamp(),n)}function j(e,t,n=`millisecond`){return D(e,`a`),D(t,`b`),O(e.getTimestamp(),n)>O(t.getTimestamp(),n)}function M(e,t,n,r=`millisecond`,i=`[]`){D(e,`target`),D(t,`left`),D(n,`right`);let a=O(e.getTimestamp(),r),o=O(t.getTimestamp(),r),s=O(n.getTimestamp(),r),c=Math.min(o,s),l=Math.max(o,s);switch(i){case`[]`:return a>=c&&a<=l;case`[)`:return a>=c&&a<l;case`(]`:return a>c&&a<=l;case`()`:return a>c&&a<l;default:throw Error(`Invalid inclusivity value: ${i}`)}}function N(e){if(typeof e!=`string`||e.trim()===``)throw TypeError(`setTimezone: timezone must be a non-empty string`);if(!Intl.supportedValuesOf(`timeZone`).includes(e))throw Error(`Unsupported timezone: ${e}`)}function P(e,t){let n=new Date(e),r=new Intl.DateTimeFormat(`en-US`,{timeZone:t,year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1}).formatToParts(n),i=Object.fromEntries(r.map(e=>[e.type,e.value])),a=Number(i.year),o=Number(i.month),s=Number(i.day),c=Number(i.hour),l=Number(i.minute),u=Number(i.second);return(Date.UTC(a,o-1,s,c,l,u)-e)/6e4}function F(e,t){if(!(e instanceof B))throw TypeError(`tz: first argument must be OzTime`);return N(t),new B(e.getTimestamp(),t,e.getLocale())}function I(e){if(!(e instanceof B))throw TypeError(`getTimezoneOffset: argument must be OzTime`);return P(e.getTimestamp(),e.getTimezone())}function L(e){if(typeof e!=`number`||Number.isNaN(e))throw TypeError(`OzTime: timestamp must be a valid number`)}function R(e){if(typeof e!=`string`||e.trim()===``)throw TypeError(`OzTime: timezone must be a non-empty string`)}function z(e){if(typeof e!=`string`||e.trim()===``)throw TypeError(`OzTime: locale must be a non-empty string`)}var B=class{constructor(e,t=`UTC`,n=`en-US`){L(e),R(t),z(n),this._timestamp=e,this._timezone=t,this._locale=n}getTimestamp(){return this._timestamp}getTimezone(){return this._timezone}getLocale(){return this._locale}toTimestamp(){return this._timestamp}toISOString(){return new Date(this._timestamp).toISOString()}add(e,t){return y(this,e,t)}subtract(e,t){return b(this,e,t)}format(e,t){return E(this,e,t)}isSame(e,t=`millisecond`){return k(this,e,t)}isBefore(e,t=`millisecond`){return A(this,e,t)}isAfter(e,t=`millisecond`){return j(this,e,t)}isBetween(e,t,n=`millisecond`,r=`[]`){return M(this,e,t,n,r)}setTimezone(e){return F(this,e)}getTimezoneOffset(){return I(this)}diff(e,t=`millisecond`){return g(this,e,t)}};function V(e){if(typeof e!=`number`||Number.isNaN(e))throw TypeError(`fromTimestamp: timestamp must be a valid number`)}function H(e){if(!(e instanceof Date)||Number.isNaN(e.getTime()))throw TypeError(`fromDate: date must be a valid Date`)}function U(e,t){if(!Number.isInteger(e))throw TypeError(`${t} must be an integer`)}function W(e=`UTC`,t=`en-US`){return new B(Date.now(),e,t)}function G(e,t=`UTC`,n=`en-US`){return V(e),new B(e,t,n)}function K(e,t=`UTC`,n=`en-US`){return H(e),new B(e.getTime(),t,n)}function q(e,t=`UTC`,n=`en-US`){if(typeof e!=`string`||e.trim()===``)throw TypeError(`fromISO: isoString must be a non-empty string`);let r=Date.parse(e);if(Number.isNaN(r))throw Error(`Invalid ISO date string: ${e}`);return new B(r,t,n)}function J(e,t,n,r=0,i=0,a=0,o=0,s=`UTC`,c=`en-US`){if(U(e,`year`),U(t,`month`),U(n,`day`),U(r,`hour`),U(i,`minute`),U(a,`second`),U(o,`millisecond`),t<1||t>12)throw RangeError(`month must be between 1 and 12`);if(r<0||r>23)throw RangeError(`hour must be between 0 and 23`);if(i<0||i>59)throw RangeError(`minute must be between 0 and 59`);if(a<0||a>59)throw RangeError(`second must be between 0 and 59`);if(o<0||o>999)throw RangeError(`millisecond must be between 0 and 999`);let l=p(e,t);if(n<1||n>l)throw RangeError(`day must be between 1 and ${l} for ${e}-${t}`);return new B(Date.UTC(e,t-1,n,r,i,a,o),s,c)}function Y(e,t){if(!(e instanceof B))throw TypeError(`${t} must be OzTime`)}var X=class e{constructor(e,t){if(Y(e,`start`),Y(t,`end`),e.getTimestamp()>t.getTimestamp())throw RangeError(`Interval: start must be before or equal to end`);this._start=e,this._end=t}getStart(){return this._start}getEnd(){return this._end}contains(e){Y(e,`moment`);let t=e.getTimestamp();return t>=this._start.getTimestamp()&&t<=this._end.getTimestamp()}overlaps(t){if(!(t instanceof e))throw TypeError(`other must be Interval`);let n=this._start.getTimestamp(),r=this._end.getTimestamp(),i=t.getStart().getTimestamp();return n<=t.getEnd().getTimestamp()&&i<=r}duration(e=`millisecond`){let t=r(e);if(!i(t))throw Error(`Interval.duration supports only fixed units: ${e}`);return(this._end.getTimestamp()-this._start.getTimestamp())/o(t)}};function Z(e,t){return new X(e,t)}function Q(e){if(typeof e!=`number`||Number.isNaN(e))throw TypeError(`amount must be a valid number`)}var $=class e{constructor(e){if(typeof e!=`number`||Number.isNaN(e))throw TypeError(`Duration: milliseconds must be a valid number`);this._milliseconds=e}asMilliseconds(){return this._milliseconds}asSeconds(){return this._milliseconds/o(`second`)}asMinutes(){return this._milliseconds/o(`minute`)}asHours(){return this._milliseconds/o(`hour`)}asDays(){return this._milliseconds/o(`day`)}add(t){if(!(t instanceof e))throw TypeError(`Duration.add: other must be Duration`);return new e(this._milliseconds+t._milliseconds)}};function ee(e,t){Q(e);let n=r(t);if(!i(n))throw Error(`duration supports only fixed units: ${t}`);return new $(e*o(n))}exports.Duration=$,exports.Interval=X,exports.OzTime=B,exports.add=y,exports.daysInMonth=p,exports.duration=ee,exports.format=E,exports.fromComponents=J,exports.fromDate=K,exports.fromISO=q,exports.fromTimestamp=G,exports.getTimezoneOffset=I,exports.interval=Z,exports.isAfter=j,exports.isBefore=A,exports.isBetween=M,exports.isLeapYear=f,exports.isSame=k,exports.now=W,exports.setTimezone=F,exports.subtract=b;
@@ -0,0 +1,426 @@
1
+ //#region src/utils/units.js
2
+ var e = {
3
+ millisecond: "millisecond",
4
+ milliseconds: "millisecond",
5
+ ms: "millisecond",
6
+ second: "second",
7
+ seconds: "second",
8
+ s: "second",
9
+ minute: "minute",
10
+ minutes: "minute",
11
+ m: "minute",
12
+ hour: "hour",
13
+ hours: "hour",
14
+ h: "hour",
15
+ day: "day",
16
+ days: "day",
17
+ d: "day",
18
+ month: "month",
19
+ months: "month",
20
+ year: "year",
21
+ years: "year",
22
+ y: "year"
23
+ }, t = {
24
+ millisecond: 1,
25
+ second: 1e3,
26
+ minute: 60 * 1e3,
27
+ hour: 3600 * 1e3,
28
+ day: 1440 * 60 * 1e3
29
+ }, n = ["month", "year"];
30
+ function r(t) {
31
+ if (typeof t != "string" || t.trim() === "") throw TypeError("normalizeUnit: unit must be a non-empty string");
32
+ let n = e[t.toLowerCase().trim()];
33
+ if (!n) throw Error(`Unsupported unit: ${t}`);
34
+ return n;
35
+ }
36
+ function i(e) {
37
+ return r(e) in t;
38
+ }
39
+ function a(e) {
40
+ return n.includes(r(e));
41
+ }
42
+ function o(e) {
43
+ let n = r(e);
44
+ if (!(n in t)) throw Error(`Unit cannot be converted to milliseconds exactly: ${e}`);
45
+ return t[n];
46
+ }
47
+ //#endregion
48
+ //#region src/utils/calendar.js
49
+ function s(e, t) {
50
+ if (typeof e != "number" || Number.isNaN(e)) throw TypeError(`${t}: timestamp must be a valid number`);
51
+ }
52
+ function c(e, t) {
53
+ if (typeof e != "number" || Number.isNaN(e)) throw TypeError(`${t}: amount must be a valid number`);
54
+ }
55
+ function l(e, t) {
56
+ if (!(e instanceof B)) throw TypeError(`${t} must be OzTime`);
57
+ }
58
+ function u(e, t) {
59
+ let n = new Date(e), r = new Date(t), i = (n.getUTCFullYear() - r.getUTCFullYear()) * 12 + (n.getUTCMonth() - r.getUTCMonth()), a = n.getUTCDate(), o = r.getUTCDate();
60
+ return i > 0 && a < o ? --i : i < 0 && a > o && (i += 1), i;
61
+ }
62
+ function d(e, t) {
63
+ let n = new Date(e), r = new Date(t), i = n.getUTCFullYear() - r.getUTCFullYear(), a = n.getUTCMonth(), o = r.getUTCMonth(), s = n.getUTCDate(), c = r.getUTCDate();
64
+ return i > 0 && (a < o || a === o && s < c) ? --i : i < 0 && (a > o || a === o && s > c) && (i += 1), i;
65
+ }
66
+ function f(e) {
67
+ if (!Number.isInteger(e)) throw TypeError("isLeapYear: year must be an integer");
68
+ return e % 4 == 0 && (e % 100 != 0 || e % 400 == 0);
69
+ }
70
+ function p(e, t) {
71
+ if (!Number.isInteger(e) || !Number.isInteger(t)) throw TypeError("daysInMonth: year and month must be integers");
72
+ if (t < 1 || t > 12) throw RangeError("daysInMonth: month must be between 1 and 12");
73
+ return new Date(Date.UTC(e, t, 0)).getUTCDate();
74
+ }
75
+ function m(e, t, n) {
76
+ s(e, "addByFixedUnit"), c(t, "addByFixedUnit");
77
+ let a = r(n);
78
+ if (!i(a)) throw Error(`addByFixedUnit does not support calendar unit: ${n}`);
79
+ return e + t * o(a);
80
+ }
81
+ function h(e, t, n) {
82
+ s(e, "addByCalendarUnit"), c(t, "addByCalendarUnit");
83
+ let i = r(n), a = new Date(e);
84
+ if (i === "month") {
85
+ let e = a.getUTCDate();
86
+ a.setUTCDate(1), a.setUTCMonth(a.getUTCMonth() + t);
87
+ let n = p(a.getUTCFullYear(), a.getUTCMonth() + 1);
88
+ return a.setUTCDate(Math.min(e, n)), a.getTime();
89
+ }
90
+ if (i === "year") {
91
+ let e = a.getUTCMonth(), n = a.getUTCDate();
92
+ a.setUTCDate(1), a.setUTCFullYear(a.getUTCFullYear() + t), a.setUTCMonth(e);
93
+ let r = p(a.getUTCFullYear(), e + 1);
94
+ return a.setUTCDate(Math.min(n, r)), a.getTime();
95
+ }
96
+ throw Error(`addByCalendarUnit supports only month and year: ${n}`);
97
+ }
98
+ function g(e, t, n = "millisecond") {
99
+ l(e, "left"), l(t, "right");
100
+ let a = r(n), s = e.getTimestamp(), c = t.getTimestamp();
101
+ if (i(a)) return (s - c) / o(a);
102
+ if (a === "month") return u(s, c);
103
+ if (a === "year") return d(s, c);
104
+ throw Error(`Unsupported unit: ${n}`);
105
+ }
106
+ //#endregion
107
+ //#region src/modules/arithmetic.js
108
+ function _(e, t) {
109
+ if (!(e instanceof B)) throw TypeError(`${t} must be OzTime`);
110
+ }
111
+ function v(e) {
112
+ if (typeof e != "number" || Number.isNaN(e)) throw TypeError("amount must be a valid number");
113
+ }
114
+ function y(e, t, n) {
115
+ _(e, "time"), v(t);
116
+ let o = r(n), s = e.getTimestamp(), c;
117
+ return i(o) ? c = m(s, t, o) : a(o) && (c = h(s, t, o)), new B(c, e.getTimezone(), e.getLocale());
118
+ }
119
+ function b(e, t, n) {
120
+ return _(e, "time"), v(t), y(e, -t, n);
121
+ }
122
+ //#endregion
123
+ //#region src/modules/format.js
124
+ function x(e) {
125
+ if (!(e instanceof B)) throw TypeError("format: first argument must be OzTime");
126
+ }
127
+ function S(e, t = 2) {
128
+ return String(e).padStart(t, "0");
129
+ }
130
+ function C(e, t) {
131
+ let n = new Intl.DateTimeFormat(t, {
132
+ timeZone: e.getTimezone(),
133
+ year: "numeric",
134
+ month: "numeric",
135
+ day: "numeric",
136
+ hour: "numeric",
137
+ minute: "2-digit",
138
+ second: "2-digit",
139
+ hour12: !1
140
+ }).formatToParts(new Date(e.getTimestamp()));
141
+ return Object.fromEntries(n.map((e) => [e.type, e.value]));
142
+ }
143
+ function w(e, t, n) {
144
+ return new Intl.DateTimeFormat(t, {
145
+ timeZone: e.getTimezone(),
146
+ month: n
147
+ }).format(new Date(e.getTimestamp()));
148
+ }
149
+ function T(e, t, n) {
150
+ return new Intl.DateTimeFormat(t, {
151
+ timeZone: e.getTimezone(),
152
+ weekday: n
153
+ }).format(new Date(e.getTimestamp()));
154
+ }
155
+ function E(e, t, n) {
156
+ if (x(e), typeof t != "string" || t.trim() === "") throw TypeError("format: template must be a non-empty string");
157
+ let r = n ?? e.getLocale(), i = C(e, r), a = Number(i.year), o = Number(i.month), s = Number(i.day), c = Number(i.hour), l = Number(i.minute), u = Number(i.second), d = new Date(e.getTimestamp()).getUTCMilliseconds(), f = c % 12, p = f === 0 ? 12 : f, m = c >= 12 ? "PM" : "AM", h = {
158
+ YYYY: String(a),
159
+ YY: String(a).slice(-2),
160
+ MMMM: w(e, r, "long"),
161
+ MMM: w(e, r, "short"),
162
+ MM: S(o),
163
+ M: String(o),
164
+ dddd: T(e, r, "long"),
165
+ ddd: T(e, r, "short"),
166
+ DD: S(s),
167
+ D: String(s),
168
+ HH: S(c),
169
+ H: String(c),
170
+ hh: S(p),
171
+ h: String(p),
172
+ mm: S(l),
173
+ ss: S(u),
174
+ SSS: S(d, 3),
175
+ A: m
176
+ };
177
+ return t.replace(/YYYY|MMMM|MMM|MM|M|dddd|ddd|DD|D|HH|H|hh|h|mm|ss|SSS|YY|A/g, (e) => h[e] ?? e);
178
+ }
179
+ //#endregion
180
+ //#region src/modules/compare.js
181
+ function D(e, t) {
182
+ if (!(e instanceof B)) throw TypeError(`${t} must be OzTime`);
183
+ }
184
+ function O(e, t) {
185
+ let n = r(t), i = new Date(e);
186
+ switch (n) {
187
+ case "millisecond": return i.getTime();
188
+ case "second":
189
+ i.setUTCMilliseconds(0);
190
+ break;
191
+ case "minute":
192
+ i.setUTCSeconds(0, 0);
193
+ break;
194
+ case "hour":
195
+ i.setUTCMinutes(0, 0, 0);
196
+ break;
197
+ case "day":
198
+ i.setUTCHours(0, 0, 0, 0);
199
+ break;
200
+ case "month":
201
+ i.setUTCHours(0, 0, 0, 0), i.setUTCDate(1);
202
+ break;
203
+ case "year":
204
+ i.setUTCHours(0, 0, 0, 0), i.setUTCDate(1), i.setUTCMonth(0);
205
+ break;
206
+ default: throw Error(`Unsupported unit for truncateToUnit: ${t}`);
207
+ }
208
+ return i.getTime();
209
+ }
210
+ function k(e, t, n = "millisecond") {
211
+ return D(e, "a"), D(t, "b"), O(e.getTimestamp(), n) === O(t.getTimestamp(), n);
212
+ }
213
+ function A(e, t, n = "millisecond") {
214
+ return D(e, "a"), D(t, "b"), O(e.getTimestamp(), n) < O(t.getTimestamp(), n);
215
+ }
216
+ function j(e, t, n = "millisecond") {
217
+ return D(e, "a"), D(t, "b"), O(e.getTimestamp(), n) > O(t.getTimestamp(), n);
218
+ }
219
+ function M(e, t, n, r = "millisecond", i = "[]") {
220
+ D(e, "target"), D(t, "left"), D(n, "right");
221
+ let a = O(e.getTimestamp(), r), o = O(t.getTimestamp(), r), s = O(n.getTimestamp(), r), c = Math.min(o, s), l = Math.max(o, s);
222
+ switch (i) {
223
+ case "[]": return a >= c && a <= l;
224
+ case "[)": return a >= c && a < l;
225
+ case "(]": return a > c && a <= l;
226
+ case "()": return a > c && a < l;
227
+ default: throw Error(`Invalid inclusivity value: ${i}`);
228
+ }
229
+ }
230
+ //#endregion
231
+ //#region src/modules/timezone.js
232
+ function N(e) {
233
+ if (typeof e != "string" || e.trim() === "") throw TypeError("setTimezone: timezone must be a non-empty string");
234
+ if (!Intl.supportedValuesOf("timeZone").includes(e)) throw Error(`Unsupported timezone: ${e}`);
235
+ }
236
+ function P(e, t) {
237
+ let n = new Date(e), r = new Intl.DateTimeFormat("en-US", {
238
+ timeZone: t,
239
+ year: "numeric",
240
+ month: "2-digit",
241
+ day: "2-digit",
242
+ hour: "2-digit",
243
+ minute: "2-digit",
244
+ second: "2-digit",
245
+ hour12: !1
246
+ }).formatToParts(n), i = Object.fromEntries(r.map((e) => [e.type, e.value])), a = Number(i.year), o = Number(i.month), s = Number(i.day), c = Number(i.hour), l = Number(i.minute), u = Number(i.second);
247
+ return (Date.UTC(a, o - 1, s, c, l, u) - e) / 6e4;
248
+ }
249
+ function F(e, t) {
250
+ if (!(e instanceof B)) throw TypeError("tz: first argument must be OzTime");
251
+ return N(t), new B(e.getTimestamp(), t, e.getLocale());
252
+ }
253
+ function I(e) {
254
+ if (!(e instanceof B)) throw TypeError("getTimezoneOffset: argument must be OzTime");
255
+ return P(e.getTimestamp(), e.getTimezone());
256
+ }
257
+ //#endregion
258
+ //#region src/core/core.js
259
+ function L(e) {
260
+ if (typeof e != "number" || Number.isNaN(e)) throw TypeError("OzTime: timestamp must be a valid number");
261
+ }
262
+ function R(e) {
263
+ if (typeof e != "string" || e.trim() === "") throw TypeError("OzTime: timezone must be a non-empty string");
264
+ }
265
+ function z(e) {
266
+ if (typeof e != "string" || e.trim() === "") throw TypeError("OzTime: locale must be a non-empty string");
267
+ }
268
+ var B = class {
269
+ constructor(e, t = "UTC", n = "en-US") {
270
+ L(e), R(t), z(n), this._timestamp = e, this._timezone = t, this._locale = n;
271
+ }
272
+ getTimestamp() {
273
+ return this._timestamp;
274
+ }
275
+ getTimezone() {
276
+ return this._timezone;
277
+ }
278
+ getLocale() {
279
+ return this._locale;
280
+ }
281
+ toTimestamp() {
282
+ return this._timestamp;
283
+ }
284
+ toISOString() {
285
+ return new Date(this._timestamp).toISOString();
286
+ }
287
+ add(e, t) {
288
+ return y(this, e, t);
289
+ }
290
+ subtract(e, t) {
291
+ return b(this, e, t);
292
+ }
293
+ format(e, t) {
294
+ return E(this, e, t);
295
+ }
296
+ isSame(e, t = "millisecond") {
297
+ return k(this, e, t);
298
+ }
299
+ isBefore(e, t = "millisecond") {
300
+ return A(this, e, t);
301
+ }
302
+ isAfter(e, t = "millisecond") {
303
+ return j(this, e, t);
304
+ }
305
+ isBetween(e, t, n = "millisecond", r = "[]") {
306
+ return M(this, e, t, n, r);
307
+ }
308
+ setTimezone(e) {
309
+ return F(this, e);
310
+ }
311
+ getTimezoneOffset() {
312
+ return I(this);
313
+ }
314
+ diff(e, t = "millisecond") {
315
+ return g(this, e, t);
316
+ }
317
+ };
318
+ //#endregion
319
+ //#region src/core/factory.js
320
+ function V(e) {
321
+ if (typeof e != "number" || Number.isNaN(e)) throw TypeError("fromTimestamp: timestamp must be a valid number");
322
+ }
323
+ function H(e) {
324
+ if (!(e instanceof Date) || Number.isNaN(e.getTime())) throw TypeError("fromDate: date must be a valid Date");
325
+ }
326
+ function U(e, t) {
327
+ if (!Number.isInteger(e)) throw TypeError(`${t} must be an integer`);
328
+ }
329
+ function W(e = "UTC", t = "en-US") {
330
+ return new B(Date.now(), e, t);
331
+ }
332
+ function G(e, t = "UTC", n = "en-US") {
333
+ return V(e), new B(e, t, n);
334
+ }
335
+ function K(e, t = "UTC", n = "en-US") {
336
+ return H(e), new B(e.getTime(), t, n);
337
+ }
338
+ function q(e, t = "UTC", n = "en-US") {
339
+ if (typeof e != "string" || e.trim() === "") throw TypeError("fromISO: isoString must be a non-empty string");
340
+ let r = Date.parse(e);
341
+ if (Number.isNaN(r)) throw Error(`Invalid ISO date string: ${e}`);
342
+ return new B(r, t, n);
343
+ }
344
+ function J(e, t, n, r = 0, i = 0, a = 0, o = 0, s = "UTC", c = "en-US") {
345
+ if (U(e, "year"), U(t, "month"), U(n, "day"), U(r, "hour"), U(i, "minute"), U(a, "second"), U(o, "millisecond"), t < 1 || t > 12) throw RangeError("month must be between 1 and 12");
346
+ if (r < 0 || r > 23) throw RangeError("hour must be between 0 and 23");
347
+ if (i < 0 || i > 59) throw RangeError("minute must be between 0 and 59");
348
+ if (a < 0 || a > 59) throw RangeError("second must be between 0 and 59");
349
+ if (o < 0 || o > 999) throw RangeError("millisecond must be between 0 and 999");
350
+ let l = p(e, t);
351
+ if (n < 1 || n > l) throw RangeError(`day must be between 1 and ${l} for ${e}-${t}`);
352
+ return new B(Date.UTC(e, t - 1, n, r, i, a, o), s, c);
353
+ }
354
+ //#endregion
355
+ //#region src/modules/interval.js
356
+ function Y(e, t) {
357
+ if (!(e instanceof B)) throw TypeError(`${t} must be OzTime`);
358
+ }
359
+ var X = class e {
360
+ constructor(e, t) {
361
+ if (Y(e, "start"), Y(t, "end"), e.getTimestamp() > t.getTimestamp()) throw RangeError("Interval: start must be before or equal to end");
362
+ this._start = e, this._end = t;
363
+ }
364
+ getStart() {
365
+ return this._start;
366
+ }
367
+ getEnd() {
368
+ return this._end;
369
+ }
370
+ contains(e) {
371
+ Y(e, "moment");
372
+ let t = e.getTimestamp();
373
+ return t >= this._start.getTimestamp() && t <= this._end.getTimestamp();
374
+ }
375
+ overlaps(t) {
376
+ if (!(t instanceof e)) throw TypeError("other must be Interval");
377
+ let n = this._start.getTimestamp(), r = this._end.getTimestamp(), i = t.getStart().getTimestamp();
378
+ return n <= t.getEnd().getTimestamp() && i <= r;
379
+ }
380
+ duration(e = "millisecond") {
381
+ let t = r(e);
382
+ if (!i(t)) throw Error(`Interval.duration supports only fixed units: ${e}`);
383
+ return (this._end.getTimestamp() - this._start.getTimestamp()) / o(t);
384
+ }
385
+ };
386
+ function Z(e, t) {
387
+ return new X(e, t);
388
+ }
389
+ //#endregion
390
+ //#region src/modules/duration.js
391
+ function Q(e) {
392
+ if (typeof e != "number" || Number.isNaN(e)) throw TypeError("amount must be a valid number");
393
+ }
394
+ var $ = class e {
395
+ constructor(e) {
396
+ if (typeof e != "number" || Number.isNaN(e)) throw TypeError("Duration: milliseconds must be a valid number");
397
+ this._milliseconds = e;
398
+ }
399
+ asMilliseconds() {
400
+ return this._milliseconds;
401
+ }
402
+ asSeconds() {
403
+ return this._milliseconds / o("second");
404
+ }
405
+ asMinutes() {
406
+ return this._milliseconds / o("minute");
407
+ }
408
+ asHours() {
409
+ return this._milliseconds / o("hour");
410
+ }
411
+ asDays() {
412
+ return this._milliseconds / o("day");
413
+ }
414
+ add(t) {
415
+ if (!(t instanceof e)) throw TypeError("Duration.add: other must be Duration");
416
+ return new e(this._milliseconds + t._milliseconds);
417
+ }
418
+ };
419
+ function ee(e, t) {
420
+ Q(e);
421
+ let n = r(t);
422
+ if (!i(n)) throw Error(`duration supports only fixed units: ${t}`);
423
+ return new $(e * o(n));
424
+ }
425
+ //#endregion
426
+ export { $ as Duration, X as Interval, B as OzTime, y as add, p as daysInMonth, ee as duration, E as format, J as fromComponents, K as fromDate, q as fromISO, G as fromTimestamp, I as getTimezoneOffset, Z as interval, j as isAfter, A as isBefore, M as isBetween, f as isLeapYear, k as isSame, W as now, F as setTimezone, b as subtract };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@alexstukovnikov/oz-time",
3
+ "version": "1.0.0",
4
+ "description": "Lightweight JavaScript date-time library",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/AlexStukovnikov/oz-time.git"
8
+ },
9
+ "type": "module",
10
+ "main": "./dist/oz-time.cjs",
11
+ "module": "./dist/oz-time.esm.js",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/oz-time.esm.js",
15
+ "require": "./dist/oz-time.cjs"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "sideEffects": false,
22
+ "scripts": {
23
+ "dev": "vite",
24
+ "build": "vite build",
25
+ "test": "vitest",
26
+ "test:run": "vitest run",
27
+ "test:coverage": "vitest run --coverage",
28
+ "docs": "jsdoc -c jsdoc.json",
29
+ "docs:clean": "rimraf docs",
30
+ "docs:build": "npm run docs:clean && npm run docs",
31
+ "prepublishOnly": "npm run build"
32
+ },
33
+ "keywords": [
34
+ "date",
35
+ "time",
36
+ "datetime"
37
+ ],
38
+ "author": "",
39
+ "license": "ISC",
40
+ "devDependencies": {
41
+ "@vitest/coverage-v8": "^4.1.7",
42
+ "@vitest/ui": "^4.1.7",
43
+ "docdash": "^2.0.2",
44
+ "jsdoc": "^4.0.5",
45
+ "vite": "^8.0.14",
46
+ "vitest": "^4.1.7"
47
+ }
48
+ }