@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 +567 -0
- package/dist/oz-time.cjs +1 -0
- package/dist/oz-time.esm.js +426 -0
- package/package.json +48 -0
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
|
package/dist/oz-time.cjs
ADDED
|
@@ -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
|
+
}
|