@alexstukovnikov/oz-time 1.0.0 → 1.0.1

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.
@@ -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
+ }
@@ -0,0 +1,162 @@
1
+ import { OzTime } from '../core/core.js';
2
+
3
+ /**
4
+ * Модуль форматирования экземпляров {@link OzTime}.
5
+ *
6
+ * @module modules/format
7
+ */
8
+
9
+ /**
10
+ * Проверяет, является ли значение экземпляром OzTime.
11
+ *
12
+ * @private
13
+ * @param {*} value - Проверяемое значение.
14
+ * @throws {TypeError} Выбрасывается, если значение не является экземпляром OzTime.
15
+ * @returns {void}
16
+ */
17
+ function assertOzTime(value) {
18
+ if (!(value instanceof OzTime)) {
19
+ throw new TypeError('format: first argument must be OzTime');
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Дополняет значение ведущими нулями до нужной длины.
25
+ *
26
+ * @private
27
+ * @param {string|number} value - Исходное значение.
28
+ * @param {number} [length=2] - Итоговая длина строки.
29
+ * @returns {string} Строка, дополненная ведущими нулями.
30
+ */
31
+ function pad(value, length = 2) {
32
+ return String(value).padStart(length, '0');
33
+ }
34
+
35
+ /**
36
+ * Возвращает числовые части даты для форматирования.
37
+ *
38
+ * @private
39
+ * @param {OzTime} time - Экземпляр времени.
40
+ * @param {string} locale - Локаль форматирования.
41
+ * @returns {Object.<string, string>} Объект с числовыми частями даты и времени.
42
+ */
43
+ function getNumericParts(time, locale) {
44
+ const formatter = new Intl.DateTimeFormat(locale, {
45
+ timeZone: time.getTimezone(),
46
+ year: 'numeric',
47
+ month: 'numeric',
48
+ day: 'numeric',
49
+ hour: 'numeric',
50
+ minute: '2-digit',
51
+ second: '2-digit',
52
+ hour12: false,
53
+ });
54
+
55
+ const parts = formatter.formatToParts(new Date(time.getTimestamp()));
56
+ return Object.fromEntries(parts.map((part) => [part.type, part.value]));
57
+ }
58
+
59
+ /**
60
+ * Возвращает название месяца в нужном формате.
61
+ *
62
+ * @private
63
+ * @param {OzTime} time - Экземпляр времени.
64
+ * @param {string} locale - Локаль форматирования.
65
+ * @param {'long'|'short'|'narrow'} length - Длина названия месяца.
66
+ * @returns {string} Название месяца.
67
+ */
68
+ function getMonthName(time, locale, length) {
69
+ return new Intl.DateTimeFormat(locale, {
70
+ timeZone: time.getTimezone(),
71
+ month: length,
72
+ }).format(new Date(time.getTimestamp()));
73
+ }
74
+
75
+ /**
76
+ * Возвращает название дня недели в нужном формате.
77
+ *
78
+ * @private
79
+ * @param {OzTime} time - Экземпляр времени.
80
+ * @param {string} locale - Локаль форматирования.
81
+ * @param {'long'|'short'|'narrow'} length - Длина названия дня недели.
82
+ * @returns {string} Название дня недели.
83
+ */
84
+ function getWeekdayName(time, locale, length) {
85
+ return new Intl.DateTimeFormat(locale, {
86
+ timeZone: time.getTimezone(),
87
+ weekday: length,
88
+ }).format(new Date(time.getTimestamp()));
89
+ }
90
+
91
+ /**
92
+ * Возвращает строковое представление экземпляра {@link OzTime}
93
+ * по заданному шаблону с токенами.
94
+ *
95
+ * Поддерживаются токены `YYYY`, `YY`, `MMMM`, `MMM`, `MM`, `M`, `dddd`, `ddd`,
96
+ * `DD`, `D`, `HH`, `H`, `hh`, `h`, `mm`, `ss`, `SSS` и `A`.
97
+ *
98
+ * @param {OzTime} time - Экземпляр времени для форматирования.
99
+ * @param {string} template - Шаблон форматирования.
100
+ * @param {string} [locale] - Необязательное переопределение локали.
101
+ * @throws {TypeError} Выбрасывается, если первый аргумент не является экземпляром OzTime или template некорректен.
102
+ * @returns {string} Отформатированная строка.
103
+ * @example
104
+ * import { format, fromISO } from '@alexstukovnikov/oz-time';
105
+ *
106
+ * const time = fromISO('2024-05-25T12:00:00Z', 'UTC', 'ru-RU');
107
+ * console.log(format(time, 'DD.MM.YYYY HH:mm')); // ожидаемый результат: 25.05.2024 12:00
108
+ */
109
+ export function format(time, template, locale) {
110
+ assertOzTime(time);
111
+
112
+ if (typeof template !== 'string' || template.trim() === '') {
113
+ throw new TypeError('format: template must be a non-empty string');
114
+ }
115
+
116
+ const usedLocale = locale ?? time.getLocale();
117
+ const parts = getNumericParts(time, usedLocale);
118
+
119
+ const year = Number(parts.year);
120
+ const month = Number(parts.month);
121
+ const day = Number(parts.day);
122
+ const hour24 = Number(parts.hour);
123
+ const minute = Number(parts.minute);
124
+ const second = Number(parts.second);
125
+ const millisecond = new Date(time.getTimestamp()).getUTCMilliseconds();
126
+
127
+ const hour12base = hour24 % 12;
128
+ const hour12 = hour12base === 0 ? 12 : hour12base;
129
+ const meridiem = hour24 >= 12 ? 'PM' : 'AM';
130
+
131
+ const tokens = {
132
+ YYYY: String(year),
133
+ YY: String(year).slice(-2),
134
+
135
+ MMMM: getMonthName(time, usedLocale, 'long'),
136
+ MMM: getMonthName(time, usedLocale, 'short'),
137
+ MM: pad(month),
138
+ M: String(month),
139
+
140
+ dddd: getWeekdayName(time, usedLocale, 'long'),
141
+ ddd: getWeekdayName(time, usedLocale, 'short'),
142
+
143
+ DD: pad(day),
144
+ D: String(day),
145
+
146
+ HH: pad(hour24),
147
+ H: String(hour24),
148
+
149
+ hh: pad(hour12),
150
+ h: String(hour12),
151
+
152
+ mm: pad(minute),
153
+ ss: pad(second),
154
+ SSS: pad(millisecond, 3),
155
+
156
+ A: meridiem,
157
+ };
158
+
159
+ const tokenPattern = /YYYY|MMMM|MMM|MM|M|dddd|ddd|DD|D|HH|H|hh|h|mm|ss|SSS|YY|A/g;
160
+
161
+ return template.replace(tokenPattern, (token) => tokens[token] ?? token);
162
+ }