@alexstukovnikov/oz-time 1.0.0 → 1.0.2
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 +70 -251
- package/package.json +45 -48
- package/src/core/core.js +509 -0
- package/src/core/factory.js +194 -0
- package/src/index.js +22 -0
- package/src/modules/arithmetic.js +100 -0
- package/src/modules/compare.js +197 -0
- package/src/modules/duration.js +165 -0
- package/src/modules/format.js +162 -0
- package/src/modules/interval.js +190 -0
- package/src/modules/timezone.js +112 -0
- package/src/utils/calendar.js +277 -0
- package/src/utils/units.js +135 -0
- package/dist/oz-time.cjs +0 -1
- package/dist/oz-time.esm.js +0 -426
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { OzTime } from './core.js';
|
|
2
|
+
import { daysInMonth } from '../utils/calendar.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Фабричные функции для создания экземпляров {@link OzTime}.
|
|
6
|
+
*
|
|
7
|
+
* @module core/factory
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Проверяет корректность timestamp.
|
|
12
|
+
*
|
|
13
|
+
* @private
|
|
14
|
+
* @param {number} timestamp - Unix timestamp в миллисекундах.
|
|
15
|
+
* @throws {TypeError} Выбрасывается, если timestamp некорректен.
|
|
16
|
+
* @returns {void}
|
|
17
|
+
*/
|
|
18
|
+
function assertValidTimestamp(timestamp) {
|
|
19
|
+
if (typeof timestamp !== 'number' || Number.isNaN(timestamp)) {
|
|
20
|
+
throw new TypeError('fromTimestamp: timestamp must be a valid number');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Проверяет корректность объекта Date.
|
|
26
|
+
*
|
|
27
|
+
* @private
|
|
28
|
+
* @param {Date} date - Проверяемый объект Date.
|
|
29
|
+
* @throws {TypeError} Выбрасывается, если date некорректен.
|
|
30
|
+
* @returns {void}
|
|
31
|
+
*/
|
|
32
|
+
function assertValidDate(date) {
|
|
33
|
+
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
|
34
|
+
throw new TypeError('fromDate: date must be a valid Date');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Проверяет, что значение является целым числом.
|
|
40
|
+
*
|
|
41
|
+
* @private
|
|
42
|
+
* @param {number} value - Проверяемое значение.
|
|
43
|
+
* @param {string} name - Имя параметра.
|
|
44
|
+
* @throws {TypeError} Выбрасывается, если значение не является целым числом.
|
|
45
|
+
* @returns {void}
|
|
46
|
+
*/
|
|
47
|
+
function assertInteger(value, name) {
|
|
48
|
+
if (!Number.isInteger(value)) {
|
|
49
|
+
throw new TypeError(`${name} must be an integer`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Создаёт и возвращает экземпляр {@link OzTime} для текущего момента времени.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} [timezone='UTC'] - Часовой пояс в формате IANA.
|
|
57
|
+
* @param {string} [locale='en-US'] - Локаль форматирования.
|
|
58
|
+
* @returns {OzTime} Экземпляр с текущим временем.
|
|
59
|
+
* @example
|
|
60
|
+
* import { now } from '@alexstukovnikov/oz-time';
|
|
61
|
+
*
|
|
62
|
+
* const current = now('Europe/Moscow', 'ru-RU');
|
|
63
|
+
* console.log(current.getTimezone()); // ожидаемый результат: Europe/Moscow
|
|
64
|
+
*/
|
|
65
|
+
export function now(timezone = 'UTC', locale = 'en-US') {
|
|
66
|
+
return new OzTime(Date.now(), timezone, locale);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Создаёт и возвращает экземпляр {@link OzTime} на основе Unix timestamp.
|
|
71
|
+
*
|
|
72
|
+
* @param {number} timestamp - Unix timestamp в миллисекундах.
|
|
73
|
+
* @param {string} [timezone='UTC'] - Часовой пояс в формате IANA.
|
|
74
|
+
* @param {string} [locale='en-US'] - Локаль форматирования.
|
|
75
|
+
* @throws {TypeError} Выбрасывается, если timestamp некорректен.
|
|
76
|
+
* @returns {OzTime} Экземпляр времени.
|
|
77
|
+
* @example
|
|
78
|
+
* import { fromTimestamp } from '@alexstukovnikov/oz-time';
|
|
79
|
+
*
|
|
80
|
+
* const time = fromTimestamp(1716638400000, 'UTC', 'ru-RU');
|
|
81
|
+
* console.log(time.toISOString()); // ожидаемый результат: 2024-05-25T12:00:00.000Z
|
|
82
|
+
*/
|
|
83
|
+
export function fromTimestamp(timestamp, timezone = 'UTC', locale = 'en-US') {
|
|
84
|
+
assertValidTimestamp(timestamp);
|
|
85
|
+
return new OzTime(timestamp, timezone, locale);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Создаёт и возвращает экземпляр {@link OzTime} на основе объекта {@link Date}.
|
|
90
|
+
*
|
|
91
|
+
* @param {Date} date - Нативный объект Date.
|
|
92
|
+
* @param {string} [timezone='UTC'] - Часовой пояс в формате IANA.
|
|
93
|
+
* @param {string} [locale='en-US'] - Локаль форматирования.
|
|
94
|
+
* @throws {TypeError} Выбрасывается, если date некорректен.
|
|
95
|
+
* @returns {OzTime} Экземпляр времени.
|
|
96
|
+
* @example
|
|
97
|
+
* import { fromDate } from '@alexstukovnikov/oz-time';
|
|
98
|
+
*
|
|
99
|
+
* const time = fromDate(new Date('2024-05-25T12:00:00Z'), 'UTC', 'ru-RU');
|
|
100
|
+
* console.log(time.toTimestamp()); // ожидаемый результат: 1716638400000
|
|
101
|
+
*/
|
|
102
|
+
export function fromDate(date, timezone = 'UTC', locale = 'en-US') {
|
|
103
|
+
assertValidDate(date);
|
|
104
|
+
return new OzTime(date.getTime(), timezone, locale);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Создаёт и возвращает экземпляр {@link OzTime} на основе ISO-строки.
|
|
109
|
+
*
|
|
110
|
+
* @param {string} isoString - Строка даты и времени в формате ISO 8601.
|
|
111
|
+
* @param {string} [timezone='UTC'] - Часовой пояс в формате IANA.
|
|
112
|
+
* @param {string} [locale='en-US'] - Локаль форматирования.
|
|
113
|
+
* @throws {TypeError} Выбрасывается, если строка пустая или не является строкой.
|
|
114
|
+
* @throws {Error} Выбрасывается, если строку не удалось распарсить.
|
|
115
|
+
* @returns {OzTime} Экземпляр времени.
|
|
116
|
+
* @example
|
|
117
|
+
* import { fromISO } from '@alexstukovnikov/oz-time';
|
|
118
|
+
*
|
|
119
|
+
* const time = fromISO('2024-05-25T12:00:00Z', 'UTC', 'ru-RU');
|
|
120
|
+
* console.log(time.format('DD.MM.YYYY HH:mm')); // ожидаемый результат: 25.05.2024 12:00
|
|
121
|
+
*/
|
|
122
|
+
export function fromISO(isoString, timezone = 'UTC', locale = 'en-US') {
|
|
123
|
+
if (typeof isoString !== 'string' || isoString.trim() === '') {
|
|
124
|
+
throw new TypeError('fromISO: isoString must be a non-empty string');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const ts = Date.parse(isoString);
|
|
128
|
+
|
|
129
|
+
if (Number.isNaN(ts)) {
|
|
130
|
+
throw new Error(`Invalid ISO date string: ${isoString}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return new OzTime(ts, timezone, locale);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Создаёт и возвращает экземпляр {@link OzTime} на основе отдельных компонентов даты и времени.
|
|
138
|
+
*
|
|
139
|
+
* @param {number} year - Год.
|
|
140
|
+
* @param {number} month - Месяц от 1 до 12.
|
|
141
|
+
* @param {number} day - День месяца.
|
|
142
|
+
* @param {number} [hour=0] - Час от 0 до 23.
|
|
143
|
+
* @param {number} [minute=0] - Минута от 0 до 59.
|
|
144
|
+
* @param {number} [second=0] - Секунда от 0 до 59.
|
|
145
|
+
* @param {number} [ms=0] - Миллисекунда от 0 до 999.
|
|
146
|
+
* @param {string} [timezone='UTC'] - Часовой пояс в формате IANA.
|
|
147
|
+
* @param {string} [locale='en-US'] - Локаль форматирования.
|
|
148
|
+
* @throws {TypeError} Выбрасывается, если любой числовой параметр не является целым числом.
|
|
149
|
+
* @throws {RangeError} Выбрасывается, если любой компонент даты или времени вне допустимого диапазона.
|
|
150
|
+
* @returns {OzTime} Экземпляр времени.
|
|
151
|
+
* @example
|
|
152
|
+
* import { fromComponents } from '@alexstukovnikov/oz-time';
|
|
153
|
+
*
|
|
154
|
+
* const time = fromComponents(2024, 5, 25, 12, 0, 0, 0, 'UTC', 'ru-RU');
|
|
155
|
+
* console.log(time.toISOString()); // ожидаемый результат: 2024-05-25T12:00:00.000Z
|
|
156
|
+
*/
|
|
157
|
+
export function fromComponents(year, month, day, hour = 0, minute = 0, second = 0, ms = 0, timezone = 'UTC', locale = 'en-US') {
|
|
158
|
+
assertInteger(year, 'year');
|
|
159
|
+
assertInteger(month, 'month');
|
|
160
|
+
assertInteger(day, 'day');
|
|
161
|
+
assertInteger(hour, 'hour');
|
|
162
|
+
assertInteger(minute, 'minute');
|
|
163
|
+
assertInteger(second, 'second');
|
|
164
|
+
assertInteger(ms, 'millisecond');
|
|
165
|
+
|
|
166
|
+
if (month < 1 || month > 12) {
|
|
167
|
+
throw new RangeError('month must be between 1 and 12');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (hour < 0 || hour > 23) {
|
|
171
|
+
throw new RangeError('hour must be between 0 and 23');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (minute < 0 || minute > 59) {
|
|
175
|
+
throw new RangeError('minute must be between 0 and 59');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (second < 0 || second > 59) {
|
|
179
|
+
throw new RangeError('second must be between 0 and 59');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (ms < 0 || ms > 999) {
|
|
183
|
+
throw new RangeError('millisecond must be between 0 and 999');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const maxDay = daysInMonth(year, month);
|
|
187
|
+
|
|
188
|
+
if (day < 1 || day > maxDay) {
|
|
189
|
+
throw new RangeError(`day must be between 1 and ${maxDay} for ${year}-${month}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const ts = Date.UTC(year, month - 1, day, hour, minute, second, ms);
|
|
193
|
+
return new OzTime(ts, timezone, locale);
|
|
194
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Публичная точка входа библиотеки OzTime.
|
|
3
|
+
*
|
|
4
|
+
* @module index
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { OzTime } from './core/core.js';
|
|
8
|
+
export { now, fromTimestamp, fromDate, fromISO, fromComponents } from './core/factory.js';
|
|
9
|
+
|
|
10
|
+
export { add, subtract } from './modules/arithmetic.js';
|
|
11
|
+
|
|
12
|
+
export { isSame, isBefore, isAfter, isBetween } from './modules/compare.js';
|
|
13
|
+
|
|
14
|
+
export { setTimezone, getTimezoneOffset } from './modules/timezone.js';
|
|
15
|
+
|
|
16
|
+
export { Interval, interval } from './modules/interval.js';
|
|
17
|
+
|
|
18
|
+
export { Duration, duration } from './modules/duration.js';
|
|
19
|
+
|
|
20
|
+
export { format } from './modules/format.js';
|
|
21
|
+
|
|
22
|
+
export { isLeapYear, daysInMonth } from './utils/calendar.js';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { OzTime } from '../core/core.js';
|
|
2
|
+
import { normalizeUnit, isFixedUnit, isCalendarUnit } from '../utils/units.js';
|
|
3
|
+
import { addByFixedUnit, addByCalendarUnit } from '../utils/calendar.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Модуль арифметических операций над экземплярами {@link OzTime}.
|
|
7
|
+
*
|
|
8
|
+
* @module modules/arithmetic
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Проверяет, является ли значение экземпляром OzTime.
|
|
13
|
+
*
|
|
14
|
+
* @private
|
|
15
|
+
* @param {*} value - Проверяемое значение.
|
|
16
|
+
* @param {string} name - Имя параметра.
|
|
17
|
+
* @throws {TypeError} Выбрасывается, если значение не является экземпляром OzTime.
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
20
|
+
function assertOzTime(value, name) {
|
|
21
|
+
if (!(value instanceof OzTime)) {
|
|
22
|
+
throw new TypeError(`${name} must be OzTime`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Проверяет корректность числового значения amount.
|
|
28
|
+
*
|
|
29
|
+
* @private
|
|
30
|
+
* @param {number} amount - Количество единиц времени.
|
|
31
|
+
* @throws {TypeError} Выбрасывается, если amount не является корректным числом.
|
|
32
|
+
* @returns {void}
|
|
33
|
+
*/
|
|
34
|
+
function assertAmount(amount) {
|
|
35
|
+
if (typeof amount !== 'number' || Number.isNaN(amount)) {
|
|
36
|
+
throw new TypeError('amount must be a valid number');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Возвращает новый экземпляр {@link OzTime}, у которого timestamp увеличен
|
|
42
|
+
* на указанное количество единиц времени.
|
|
43
|
+
*
|
|
44
|
+
* Поддерживает как фиксированные, так и календарные единицы времени.
|
|
45
|
+
* Исходный экземпляр не изменяется.
|
|
46
|
+
*
|
|
47
|
+
* @param {OzTime} time - Исходное значение времени.
|
|
48
|
+
* @param {number} amount - Количество единиц времени.
|
|
49
|
+
* @param {string} unit - Единица времени.
|
|
50
|
+
* @throws {TypeError} Выбрасывается, если time или amount некорректны.
|
|
51
|
+
* @returns {OzTime} Новый экземпляр OzTime с timestamp, сдвинутым вперёд.
|
|
52
|
+
* @example
|
|
53
|
+
* import { add, fromISO } from '@alexstukovnikov/oz-time';
|
|
54
|
+
*
|
|
55
|
+
* const time = fromISO('2024-05-25T12:00:00Z', 'UTC', 'ru-RU');
|
|
56
|
+
* const result = add(time, 2, 'day');
|
|
57
|
+
* console.log(result.toISOString()); // ожидаемый результат: 2024-05-27T12:00:00.000Z
|
|
58
|
+
*/
|
|
59
|
+
export function add(time, amount, unit) {
|
|
60
|
+
assertOzTime(time, 'time');
|
|
61
|
+
assertAmount(amount);
|
|
62
|
+
|
|
63
|
+
const normalizedUnit = normalizeUnit(unit);
|
|
64
|
+
const timestamp = time.getTimestamp();
|
|
65
|
+
|
|
66
|
+
let nextTimestamp;
|
|
67
|
+
|
|
68
|
+
if (isFixedUnit(normalizedUnit)) {
|
|
69
|
+
nextTimestamp = addByFixedUnit(timestamp, amount, normalizedUnit);
|
|
70
|
+
} else if (isCalendarUnit(normalizedUnit)) {
|
|
71
|
+
nextTimestamp = addByCalendarUnit(timestamp, amount, normalizedUnit);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return new OzTime(nextTimestamp, time.getTimezone(), time.getLocale());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Возвращает новый экземпляр {@link OzTime}, у которого timestamp уменьшен
|
|
79
|
+
* на указанное количество единиц времени.
|
|
80
|
+
*
|
|
81
|
+
* Исходный экземпляр не изменяется.
|
|
82
|
+
*
|
|
83
|
+
* @param {OzTime} time - Исходное значение времени.
|
|
84
|
+
* @param {number} amount - Количество единиц времени.
|
|
85
|
+
* @param {string} unit - Единица времени.
|
|
86
|
+
* @throws {TypeError} Выбрасывается, если time или amount некорректны.
|
|
87
|
+
* @returns {OzTime} Новый экземпляр OzTime с timestamp, сдвинутым назад.
|
|
88
|
+
* @example
|
|
89
|
+
* import { subtract, fromISO } from '@alexstukovnikov/oz-time';
|
|
90
|
+
*
|
|
91
|
+
* const time = fromISO('2024-05-25T12:00:00Z', 'UTC', 'ru-RU');
|
|
92
|
+
* const result = subtract(time, 3, 'hour');
|
|
93
|
+
* console.log(result.toISOString()); // ожидаемый результат: 2024-05-25T09:00:00.000Z
|
|
94
|
+
*/
|
|
95
|
+
export function subtract(time, amount, unit) {
|
|
96
|
+
assertOzTime(time, 'time');
|
|
97
|
+
assertAmount(amount);
|
|
98
|
+
|
|
99
|
+
return add(time, -amount, unit);
|
|
100
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { OzTime } from '../core/core.js';
|
|
2
|
+
import { normalizeUnit } from '../utils/units.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Модуль сравнений для экземпляров {@link OzTime}.
|
|
6
|
+
*
|
|
7
|
+
* @module modules/compare
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Проверяет, является ли значение экземпляром OzTime.
|
|
12
|
+
*
|
|
13
|
+
* @private
|
|
14
|
+
* @param {*} value - Проверяемое значение.
|
|
15
|
+
* @param {string} name - Имя параметра.
|
|
16
|
+
* @throws {TypeError} Выбрасывается, если значение не является экземпляром OzTime.
|
|
17
|
+
* @returns {void}
|
|
18
|
+
*/
|
|
19
|
+
function assertOzTime(value, name) {
|
|
20
|
+
if (!(value instanceof OzTime)) {
|
|
21
|
+
throw new TypeError(`${name} must be OzTime`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Обрезает timestamp до заданной точности.
|
|
27
|
+
*
|
|
28
|
+
* @private
|
|
29
|
+
* @param {number} timestamp - Исходный timestamp в миллисекундах.
|
|
30
|
+
* @param {string} unit - Единица точности.
|
|
31
|
+
* @throws {Error} Выбрасывается, если единица не поддерживается.
|
|
32
|
+
* @returns {number} Timestamp, обрезанный до заданной единицы времени.
|
|
33
|
+
*/
|
|
34
|
+
function truncateToUnit(timestamp, unit) {
|
|
35
|
+
const normalizedUnit = normalizeUnit(unit);
|
|
36
|
+
const d = new Date(timestamp);
|
|
37
|
+
|
|
38
|
+
switch (normalizedUnit) {
|
|
39
|
+
case 'millisecond':
|
|
40
|
+
return d.getTime();
|
|
41
|
+
|
|
42
|
+
case 'second':
|
|
43
|
+
d.setUTCMilliseconds(0);
|
|
44
|
+
break;
|
|
45
|
+
|
|
46
|
+
case 'minute':
|
|
47
|
+
d.setUTCSeconds(0, 0);
|
|
48
|
+
break;
|
|
49
|
+
|
|
50
|
+
case 'hour':
|
|
51
|
+
d.setUTCMinutes(0, 0, 0);
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
case 'day':
|
|
55
|
+
d.setUTCHours(0, 0, 0, 0);
|
|
56
|
+
break;
|
|
57
|
+
|
|
58
|
+
case 'month':
|
|
59
|
+
d.setUTCHours(0, 0, 0, 0);
|
|
60
|
+
d.setUTCDate(1);
|
|
61
|
+
break;
|
|
62
|
+
|
|
63
|
+
case 'year':
|
|
64
|
+
d.setUTCHours(0, 0, 0, 0);
|
|
65
|
+
d.setUTCDate(1);
|
|
66
|
+
d.setUTCMonth(0);
|
|
67
|
+
break;
|
|
68
|
+
|
|
69
|
+
default:
|
|
70
|
+
throw new Error(`Unsupported unit for truncateToUnit: ${unit}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return d.getTime();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Проверяет, равны ли два значения времени с учётом заданной точности.
|
|
78
|
+
*
|
|
79
|
+
* @param {OzTime} a - Первое значение.
|
|
80
|
+
* @param {OzTime} b - Второе значение.
|
|
81
|
+
* @param {string} [unit='millisecond'] - Точность сравнения.
|
|
82
|
+
* @throws {TypeError} Выбрасывается, если хотя бы один аргумент не является экземпляром OzTime.
|
|
83
|
+
* @returns {boolean} `true`, если значения равны на заданной точности.
|
|
84
|
+
* @example
|
|
85
|
+
* import { isSame, fromISO } from '@alexstukovnikov/oz-time';
|
|
86
|
+
*
|
|
87
|
+
* const a = fromISO('2024-05-25T12:00:00.100Z');
|
|
88
|
+
* const b = fromISO('2024-05-25T12:00:00.900Z');
|
|
89
|
+
* console.log(isSame(a, b, 'second')); // ожидаемый результат: true
|
|
90
|
+
*/
|
|
91
|
+
export function isSame(a, b, unit = 'millisecond') {
|
|
92
|
+
assertOzTime(a, 'a');
|
|
93
|
+
assertOzTime(b, 'b');
|
|
94
|
+
|
|
95
|
+
const tsA = truncateToUnit(a.getTimestamp(), unit);
|
|
96
|
+
const tsB = truncateToUnit(b.getTimestamp(), unit);
|
|
97
|
+
|
|
98
|
+
return tsA === tsB;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Проверяет, находится ли первое значение раньше второго
|
|
103
|
+
* с учётом заданной точности.
|
|
104
|
+
*
|
|
105
|
+
* @param {OzTime} a - Первое значение.
|
|
106
|
+
* @param {OzTime} b - Второе значение.
|
|
107
|
+
* @param {string} [unit='millisecond'] - Точность сравнения.
|
|
108
|
+
* @throws {TypeError} Выбрасывается, если хотя бы один аргумент не является экземпляром OzTime.
|
|
109
|
+
* @returns {boolean} `true`, если первое значение раньше второго.
|
|
110
|
+
* @example
|
|
111
|
+
* import { isBefore, fromISO } from '@alexstukovnikov/oz-time';
|
|
112
|
+
*
|
|
113
|
+
* const a = fromISO('2024-05-25T12:00:00Z');
|
|
114
|
+
* const b = fromISO('2024-05-26T12:00:00Z');
|
|
115
|
+
* console.log(isBefore(a, b)); // ожидаемый результат: true
|
|
116
|
+
*/
|
|
117
|
+
export function isBefore(a, b, unit = 'millisecond') {
|
|
118
|
+
assertOzTime(a, 'a');
|
|
119
|
+
assertOzTime(b, 'b');
|
|
120
|
+
|
|
121
|
+
const tsA = truncateToUnit(a.getTimestamp(), unit);
|
|
122
|
+
const tsB = truncateToUnit(b.getTimestamp(), unit);
|
|
123
|
+
|
|
124
|
+
return tsA < tsB;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Проверяет, находится ли первое значение позже второго
|
|
129
|
+
* с учётом заданной точности.
|
|
130
|
+
*
|
|
131
|
+
* @param {OzTime} a - Первое значение.
|
|
132
|
+
* @param {OzTime} b - Второе значение.
|
|
133
|
+
* @param {string} [unit='millisecond'] - Точность сравнения.
|
|
134
|
+
* @throws {TypeError} Выбрасывается, если хотя бы один аргумент не является экземпляром OzTime.
|
|
135
|
+
* @returns {boolean} `true`, если первое значение позже второго.
|
|
136
|
+
* @example
|
|
137
|
+
* import { isAfter, fromISO } from '@alexstukovnikov/oz-time';
|
|
138
|
+
*
|
|
139
|
+
* const a = fromISO('2024-05-26T12:00:00Z');
|
|
140
|
+
* const b = fromISO('2024-05-25T12:00:00Z');
|
|
141
|
+
* console.log(isAfter(a, b)); // ожидаемый результат: true
|
|
142
|
+
*/
|
|
143
|
+
export function isAfter(a, b, unit = 'millisecond') {
|
|
144
|
+
assertOzTime(a, 'a');
|
|
145
|
+
assertOzTime(b, 'b');
|
|
146
|
+
|
|
147
|
+
const tsA = truncateToUnit(a.getTimestamp(), unit);
|
|
148
|
+
const tsB = truncateToUnit(b.getTimestamp(), unit);
|
|
149
|
+
|
|
150
|
+
return tsA > tsB;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Проверяет, попадает ли целевое значение в диапазон между двумя границами
|
|
155
|
+
* с учётом заданной точности.
|
|
156
|
+
*
|
|
157
|
+
* @param {OzTime} target - Проверяемое значение.
|
|
158
|
+
* @param {OzTime} left - Левая граница.
|
|
159
|
+
* @param {OzTime} right - Правая граница.
|
|
160
|
+
* @param {string} [unit='millisecond'] - Точность сравнения.
|
|
161
|
+
* @param {'[]'|'[)'|'(]'|'()'} [inclusivity='[]'] - Формат включённости границ.
|
|
162
|
+
* @throws {TypeError} Выбрасывается, если хотя бы один аргумент не является экземпляром OzTime.
|
|
163
|
+
* @throws {Error} Выбрасывается, если inclusivity задан некорректно.
|
|
164
|
+
* @returns {boolean} `true`, если значение находится внутри диапазона.
|
|
165
|
+
* @example
|
|
166
|
+
* import { isBetween, fromISO } from '@alexstukovnikov/oz-time';
|
|
167
|
+
*
|
|
168
|
+
* const target = fromISO('2024-05-25T12:00:00Z');
|
|
169
|
+
* const start = fromISO('2024-05-25T10:00:00Z');
|
|
170
|
+
* const end = fromISO('2024-05-25T14:00:00Z');
|
|
171
|
+
* console.log(isBetween(target, start, end)); // ожидаемый результат: true
|
|
172
|
+
*/
|
|
173
|
+
export function isBetween(target, left, right, unit = 'millisecond', inclusivity = '[]') {
|
|
174
|
+
assertOzTime(target, 'target');
|
|
175
|
+
assertOzTime(left, 'left');
|
|
176
|
+
assertOzTime(right, 'right');
|
|
177
|
+
|
|
178
|
+
const t = truncateToUnit(target.getTimestamp(), unit);
|
|
179
|
+
const l = truncateToUnit(left.getTimestamp(), unit);
|
|
180
|
+
const r = truncateToUnit(right.getTimestamp(), unit);
|
|
181
|
+
|
|
182
|
+
const min = Math.min(l, r);
|
|
183
|
+
const max = Math.max(l, r);
|
|
184
|
+
|
|
185
|
+
switch (inclusivity) {
|
|
186
|
+
case '[]':
|
|
187
|
+
return t >= min && t <= max;
|
|
188
|
+
case '[)':
|
|
189
|
+
return t >= min && t < max;
|
|
190
|
+
case '(]':
|
|
191
|
+
return t > min && t <= max;
|
|
192
|
+
case '()':
|
|
193
|
+
return t > min && t < max;
|
|
194
|
+
default:
|
|
195
|
+
throw new Error(`Invalid inclusivity value: ${inclusivity}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { isFixedUnit, unitToMilliseconds, normalizeUnit } from '../utils/units.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Модуль для работы с фиксированными длительностями.
|
|
5
|
+
*
|
|
6
|
+
* @module modules/duration
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Проверяет корректность числового значения amount.
|
|
11
|
+
*
|
|
12
|
+
* @private
|
|
13
|
+
* @param {number} amount - Количество единиц времени.
|
|
14
|
+
* @throws {TypeError} Выбрасывается, если amount не является корректным числом.
|
|
15
|
+
* @returns {void}
|
|
16
|
+
*/
|
|
17
|
+
function assertAmount(amount) {
|
|
18
|
+
if (typeof amount !== 'number' || Number.isNaN(amount)) {
|
|
19
|
+
throw new TypeError('amount must be a valid number');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Представляет фиксированную длительность, хранящуюся в миллисекундах.
|
|
25
|
+
*
|
|
26
|
+
* @class
|
|
27
|
+
* @example
|
|
28
|
+
* import { Duration } from '@alexstukovnikov/oz-time';
|
|
29
|
+
*
|
|
30
|
+
* const duration = new Duration(3600000);
|
|
31
|
+
* console.log(duration.asHours()); // ожидаемый результат: 1
|
|
32
|
+
*/
|
|
33
|
+
export class Duration {
|
|
34
|
+
/**
|
|
35
|
+
* Создаёт экземпляр Duration.
|
|
36
|
+
*
|
|
37
|
+
* @param {number} milliseconds - Длительность в миллисекундах.
|
|
38
|
+
* @throws {TypeError} Выбрасывается, если milliseconds некорректен.
|
|
39
|
+
*/
|
|
40
|
+
constructor(milliseconds) {
|
|
41
|
+
if (typeof milliseconds !== 'number' || Number.isNaN(milliseconds)) {
|
|
42
|
+
throw new TypeError('Duration: milliseconds must be a valid number');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this._milliseconds = milliseconds;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Возвращает длительность в миллисекундах.
|
|
50
|
+
*
|
|
51
|
+
* @returns {number} Длительность в миллисекундах.
|
|
52
|
+
* @example
|
|
53
|
+
* import { duration } from '@alexstukovnikov/oz-time';
|
|
54
|
+
*
|
|
55
|
+
* const value = duration(3600000, 'millisecond');
|
|
56
|
+
* console.log(value.asMilliseconds()); // ожидаемый результат: 3600000
|
|
57
|
+
*/
|
|
58
|
+
asMilliseconds() {
|
|
59
|
+
return this._milliseconds;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Возвращает длительность в секундах.
|
|
64
|
+
*
|
|
65
|
+
* @returns {number} Длительность в секундах.
|
|
66
|
+
* @example
|
|
67
|
+
* import { duration } from '@alexstukovnikov/oz-time';
|
|
68
|
+
*
|
|
69
|
+
* const value = duration(3600000, 'millisecond');
|
|
70
|
+
* console.log(value.asSeconds()); // ожидаемый результат: 3600
|
|
71
|
+
*/
|
|
72
|
+
asSeconds() {
|
|
73
|
+
return this._milliseconds / unitToMilliseconds('second');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Возвращает длительность в минутах.
|
|
78
|
+
*
|
|
79
|
+
* @returns {number} Длительность в минутах.
|
|
80
|
+
* @example
|
|
81
|
+
* import { duration } from '@alexstukovnikov/oz-time';
|
|
82
|
+
*
|
|
83
|
+
* const value = duration(3600000, 'millisecond');
|
|
84
|
+
* console.log(value.asMinutes()); // ожидаемый результат: 60
|
|
85
|
+
*/
|
|
86
|
+
asMinutes() {
|
|
87
|
+
return this._milliseconds / unitToMilliseconds('minute');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Возвращает длительность в часах.
|
|
92
|
+
*
|
|
93
|
+
* @returns {number} Длительность в часах.
|
|
94
|
+
* @example
|
|
95
|
+
* import { duration } from '@alexstukovnikov/oz-time';
|
|
96
|
+
*
|
|
97
|
+
* const value = duration(36000000, 'millisecond');
|
|
98
|
+
* console.log(value.asHours()); // ожидаемый результат: 10
|
|
99
|
+
*/
|
|
100
|
+
asHours() {
|
|
101
|
+
return this._milliseconds / unitToMilliseconds('hour');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Возвращает длительность в днях.
|
|
106
|
+
*
|
|
107
|
+
* @returns {number} Длительность в днях.
|
|
108
|
+
* @example
|
|
109
|
+
* import { duration } from '@alexstukovnikov/oz-time';
|
|
110
|
+
*
|
|
111
|
+
* const value = duration(120, 'hour');
|
|
112
|
+
* console.log(value.asDays()); // ожидаемый результат: 5
|
|
113
|
+
*/
|
|
114
|
+
asDays() {
|
|
115
|
+
return this._milliseconds / unitToMilliseconds('day');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Возвращает новую длительность, равную сумме текущей и переданной длительности.
|
|
120
|
+
*
|
|
121
|
+
* @param {Duration} other - Вторая длительность.
|
|
122
|
+
* @throws {TypeError} Выбрасывается, если other не является экземпляром Duration.
|
|
123
|
+
* @returns {Duration} Новая длительность, равная сумме двух значений.
|
|
124
|
+
* @example
|
|
125
|
+
* import { duration } from '@alexstukovnikov/oz-time';
|
|
126
|
+
*
|
|
127
|
+
* const a = duration(1, 'second');
|
|
128
|
+
* const b = duration(2, 'second');
|
|
129
|
+
* console.log(a.add(b).asMilliseconds()); // ожидаемый результат: 3000
|
|
130
|
+
*/
|
|
131
|
+
add(other) {
|
|
132
|
+
if (!(other instanceof Duration)) {
|
|
133
|
+
throw new TypeError('Duration.add: other must be Duration');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return new Duration(this._milliseconds + other._milliseconds);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Создаёт и возвращает экземпляр {@link Duration} из числа
|
|
142
|
+
* и фиксированной единицы времени.
|
|
143
|
+
*
|
|
144
|
+
* @param {number} amount - Количество единиц времени.
|
|
145
|
+
* @param {string} unit - Фиксированная единица времени.
|
|
146
|
+
* @throws {TypeError} Выбрасывается, если amount некорректен.
|
|
147
|
+
* @throws {Error} Выбрасывается, если unit не является фиксированной единицей времени.
|
|
148
|
+
* @returns {Duration} Экземпляр Duration.
|
|
149
|
+
* @example
|
|
150
|
+
* import { duration } from '@alexstukovnikov/oz-time';
|
|
151
|
+
*
|
|
152
|
+
* const value = duration(2, 'hour');
|
|
153
|
+
* console.log(value.asMinutes()); // ожидаемый результат: 120
|
|
154
|
+
*/
|
|
155
|
+
export function duration(amount, unit) {
|
|
156
|
+
assertAmount(amount);
|
|
157
|
+
|
|
158
|
+
const normalizedUnit = normalizeUnit(unit);
|
|
159
|
+
|
|
160
|
+
if (!isFixedUnit(normalizedUnit)) {
|
|
161
|
+
throw new Error(`duration supports only fixed units: ${unit}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return new Duration(amount * unitToMilliseconds(normalizedUnit));
|
|
165
|
+
}
|