@taiga-ui/cdk 2.26.0 → 2.27.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/abstract/control.d.ts +1 -1
- package/abstract/interactive.d.ts +4 -4
- package/abstract/taiga-ui-cdk-abstract.metadata.json +1 -1
- package/bundles/taiga-ui-cdk-abstract.umd.js +3 -3
- package/bundles/taiga-ui-cdk-abstract.umd.js.map +1 -1
- package/bundles/taiga-ui-cdk-abstract.umd.min.js +1 -1
- package/bundles/taiga-ui-cdk-abstract.umd.min.js.map +1 -1
- package/bundles/taiga-ui-cdk-date-time.umd.js +331 -331
- package/bundles/taiga-ui-cdk-date-time.umd.js.map +1 -1
- package/bundles/taiga-ui-cdk-date-time.umd.min.js +1 -1
- package/bundles/taiga-ui-cdk-date-time.umd.min.js.map +1 -1
- package/bundles/taiga-ui-cdk-directives-copy-processor.umd.js +276 -0
- package/bundles/taiga-ui-cdk-directives-copy-processor.umd.js.map +1 -0
- package/bundles/taiga-ui-cdk-directives-copy-processor.umd.min.js +16 -0
- package/bundles/taiga-ui-cdk-directives-copy-processor.umd.min.js.map +1 -0
- package/bundles/taiga-ui-cdk-directives-for.umd.js.map +1 -1
- package/bundles/taiga-ui-cdk-directives-for.umd.min.js.map +1 -1
- package/bundles/taiga-ui-cdk-directives-media.umd.js +1 -1
- package/bundles/taiga-ui-cdk-directives-media.umd.js.map +1 -1
- package/bundles/taiga-ui-cdk-directives-media.umd.min.js +1 -1
- package/bundles/taiga-ui-cdk-directives-media.umd.min.js.map +1 -1
- package/bundles/taiga-ui-cdk-services.umd.js +2 -2
- package/bundles/taiga-ui-cdk-services.umd.js.map +1 -1
- package/bundles/taiga-ui-cdk-services.umd.min.js +1 -1
- package/bundles/taiga-ui-cdk-services.umd.min.js.map +1 -1
- package/date-time/day-range.d.ts +14 -14
- package/date-time/day.d.ts +57 -57
- package/date-time/month-range.d.ts +1 -1
- package/date-time/month.d.ts +26 -26
- package/date-time/taiga-ui-cdk-date-time.metadata.json +1 -1
- package/date-time/time.d.ts +12 -12
- package/date-time/year.d.ts +27 -27
- package/directives/copy-processor/copy-processor.directive.d.ts +7 -0
- package/directives/copy-processor/copy-processor.module.d.ts +2 -0
- package/directives/copy-processor/index.d.ts +2 -0
- package/directives/copy-processor/package.json +13 -0
- package/directives/copy-processor/taiga-ui-cdk-directives-copy-processor.d.ts +4 -0
- package/directives/copy-processor/taiga-ui-cdk-directives-copy-processor.metadata.json +1 -0
- package/directives/for/for.directive.d.ts +1 -1
- package/directives/for/taiga-ui-cdk-directives-for.metadata.json +1 -1
- package/directives/media/media.directive.d.ts +1 -1
- package/directives/media/taiga-ui-cdk-directives-media.metadata.json +1 -1
- package/esm2015/abstract/control.js +1 -1
- package/esm2015/abstract/interactive.js +4 -4
- package/esm2015/date-time/day-range.js +26 -26
- package/esm2015/date-time/day.js +120 -120
- package/esm2015/date-time/month-range.js +6 -6
- package/esm2015/date-time/month.js +56 -56
- package/esm2015/date-time/time.js +40 -40
- package/esm2015/date-time/year.js +49 -49
- package/esm2015/directives/copy-processor/copy-processor.directive.js +37 -0
- package/esm2015/directives/copy-processor/copy-processor.module.js +13 -0
- package/esm2015/directives/copy-processor/index.js +3 -0
- package/esm2015/directives/copy-processor/taiga-ui-cdk-directives-copy-processor.js +5 -0
- package/esm2015/directives/for/for.directive.js +1 -1
- package/esm2015/directives/media/media.directive.js +2 -2
- package/esm2015/services/pan.service.js +3 -3
- package/esm5/abstract/control.js +1 -1
- package/esm5/abstract/interactive.js +4 -4
- package/esm5/date-time/day-range.js +26 -26
- package/esm5/date-time/day.js +120 -120
- package/esm5/date-time/month-range.js +6 -6
- package/esm5/date-time/month.js +56 -56
- package/esm5/date-time/time.js +41 -41
- package/esm5/date-time/year.js +49 -49
- package/esm5/directives/copy-processor/copy-processor.directive.js +38 -0
- package/esm5/directives/copy-processor/copy-processor.module.js +16 -0
- package/esm5/directives/copy-processor/index.js +3 -0
- package/esm5/directives/copy-processor/taiga-ui-cdk-directives-copy-processor.js +5 -0
- package/esm5/directives/for/for.directive.js +1 -1
- package/esm5/directives/media/media.directive.js +2 -2
- package/esm5/services/pan.service.js +3 -3
- package/fesm2015/taiga-ui-cdk-abstract.js +3 -3
- package/fesm2015/taiga-ui-cdk-abstract.js.map +1 -1
- package/fesm2015/taiga-ui-cdk-date-time.js +322 -322
- package/fesm2015/taiga-ui-cdk-date-time.js.map +1 -1
- package/fesm2015/taiga-ui-cdk-directives-copy-processor.js +52 -0
- package/fesm2015/taiga-ui-cdk-directives-copy-processor.js.map +1 -0
- package/fesm2015/taiga-ui-cdk-directives-for.js.map +1 -1
- package/fesm2015/taiga-ui-cdk-directives-media.js +1 -1
- package/fesm2015/taiga-ui-cdk-directives-media.js.map +1 -1
- package/fesm2015/taiga-ui-cdk-services.js +2 -2
- package/fesm2015/taiga-ui-cdk-services.js.map +1 -1
- package/fesm5/taiga-ui-cdk-abstract.js +3 -3
- package/fesm5/taiga-ui-cdk-abstract.js.map +1 -1
- package/fesm5/taiga-ui-cdk-date-time.js +331 -331
- package/fesm5/taiga-ui-cdk-date-time.js.map +1 -1
- package/fesm5/taiga-ui-cdk-directives-copy-processor.js +56 -0
- package/fesm5/taiga-ui-cdk-directives-copy-processor.js.map +1 -0
- package/fesm5/taiga-ui-cdk-directives-for.js.map +1 -1
- package/fesm5/taiga-ui-cdk-directives-media.js +1 -1
- package/fesm5/taiga-ui-cdk-directives-media.js.map +1 -1
- package/fesm5/taiga-ui-cdk-services.js +2 -2
- package/fesm5/taiga-ui-cdk-services.js.map +1 -1
- package/package.json +2 -2
|
@@ -69,6 +69,54 @@ class TuiYear {
|
|
|
69
69
|
this.year = year;
|
|
70
70
|
tuiAssert.assert(TuiYear.isValidYear(year));
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Checks year for validity
|
|
74
|
+
*/
|
|
75
|
+
static isValidYear(year) {
|
|
76
|
+
return Number.isInteger(year) && inRange(year, MIN_YEAR, MAX_YEAR + 1);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Check if passed year is a leap year
|
|
80
|
+
*/
|
|
81
|
+
static isLeapYear(year) {
|
|
82
|
+
tuiAssert.assert(TuiYear.isValidYear(year));
|
|
83
|
+
return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Returns amount of leap years from year 0 to the passed one
|
|
87
|
+
*/
|
|
88
|
+
static getAbsoluteLeapYears(year) {
|
|
89
|
+
tuiAssert.assert(TuiYear.isValidYear(year));
|
|
90
|
+
return Math.ceil(year / 400) + (Math.ceil(year / 4) - Math.ceil(year / 100));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* @deprecated DONT USE IT (will be deleted soon)
|
|
94
|
+
*
|
|
95
|
+
* Returns day of week offset of the beginning of the passed year
|
|
96
|
+
*
|
|
97
|
+
* @param year
|
|
98
|
+
* @param absoluteLeapYears amount of leap years prior to the passed one
|
|
99
|
+
* @return offset in days
|
|
100
|
+
*/
|
|
101
|
+
static getYearStartDaysOffset(year, absoluteLeapYears) {
|
|
102
|
+
tuiAssert.assert(TuiYear.isValidYear(year));
|
|
103
|
+
tuiAssert.assert(Number.isInteger(absoluteLeapYears));
|
|
104
|
+
tuiAssert.assert(year >= absoluteLeapYears);
|
|
105
|
+
tuiAssert.assert(absoluteLeapYears >= 0);
|
|
106
|
+
return ((absoluteLeapYears * DAYS_IN_LEAP_YEAR +
|
|
107
|
+
(year - absoluteLeapYears) * DAYS_IN_NORMAL_YEAR +
|
|
108
|
+
5) %
|
|
109
|
+
DAYS_IN_WEEK);
|
|
110
|
+
}
|
|
111
|
+
static lengthBetween(from, to) {
|
|
112
|
+
return to.year - from.year;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Normalizes year by clamping it between min and max years
|
|
116
|
+
*/
|
|
117
|
+
static normalizeYearPart(year) {
|
|
118
|
+
return normalizeToIntNumber(year, MIN_YEAR, MAX_YEAR);
|
|
119
|
+
}
|
|
72
120
|
get formattedYear() {
|
|
73
121
|
return padStart(this.year.toString(), 4, '0');
|
|
74
122
|
}
|
|
@@ -138,68 +186,75 @@ class TuiYear {
|
|
|
138
186
|
toJSON() {
|
|
139
187
|
return this.formattedYear;
|
|
140
188
|
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Immutable object consisting of year and month
|
|
193
|
+
*/
|
|
194
|
+
class TuiMonth extends TuiYear {
|
|
141
195
|
/**
|
|
142
|
-
*
|
|
196
|
+
* @param year
|
|
197
|
+
* @param month (starting with 0)
|
|
143
198
|
*/
|
|
144
|
-
|
|
145
|
-
|
|
199
|
+
constructor(year, month) {
|
|
200
|
+
super(year);
|
|
201
|
+
this.month = month;
|
|
202
|
+
tuiAssert.assert(TuiMonth.isValidMonth(year, month));
|
|
146
203
|
}
|
|
147
204
|
/**
|
|
148
|
-
*
|
|
205
|
+
* Tests month and year for validity
|
|
149
206
|
*/
|
|
150
|
-
static
|
|
151
|
-
|
|
152
|
-
return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0);
|
|
207
|
+
static isValidMonth(year, month) {
|
|
208
|
+
return TuiYear.isValidYear(year) && TuiMonth.isValidMonthPart(month);
|
|
153
209
|
}
|
|
154
210
|
/**
|
|
155
|
-
* Returns
|
|
211
|
+
* Returns number of days in a month
|
|
156
212
|
*/
|
|
157
|
-
static
|
|
158
|
-
tuiAssert.assert(
|
|
159
|
-
|
|
213
|
+
static getMonthDaysCount(month, isLeapYear) {
|
|
214
|
+
tuiAssert.assert(TuiMonth.isValidMonthPart(month));
|
|
215
|
+
switch (month) {
|
|
216
|
+
case 1 /* February */:
|
|
217
|
+
return isLeapYear ? 29 : 28;
|
|
218
|
+
case 3 /* April */:
|
|
219
|
+
case 5 /* June */:
|
|
220
|
+
case 8 /* September */:
|
|
221
|
+
case 10 /* November */:
|
|
222
|
+
return 30;
|
|
223
|
+
default:
|
|
224
|
+
return 31;
|
|
225
|
+
}
|
|
160
226
|
}
|
|
161
227
|
/**
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
* Returns day of week offset of the beginning of the passed year
|
|
165
|
-
*
|
|
166
|
-
* @param year
|
|
167
|
-
* @param absoluteLeapYears amount of leap years prior to the passed one
|
|
168
|
-
* @return offset in days
|
|
228
|
+
* Returns current month and year based on local time zone
|
|
229
|
+
* @nosideeffects
|
|
169
230
|
*/
|
|
170
|
-
static
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
231
|
+
static currentLocal() {
|
|
232
|
+
const nativeDate = new Date();
|
|
233
|
+
return new TuiMonth(nativeDate.getFullYear(), nativeDate.getMonth());
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Returns current month and year based on UTC
|
|
237
|
+
*/
|
|
238
|
+
static currentUtc() {
|
|
239
|
+
const nativeDate = new Date();
|
|
240
|
+
return new TuiMonth(nativeDate.getUTCFullYear(), nativeDate.getUTCMonth());
|
|
179
241
|
}
|
|
180
242
|
static lengthBetween(from, to) {
|
|
181
|
-
|
|
243
|
+
const absoluteFrom = from.month + from.year * 12;
|
|
244
|
+
const absoluteTo = to.month + to.year * 12;
|
|
245
|
+
return absoluteTo - absoluteFrom;
|
|
182
246
|
}
|
|
183
247
|
/**
|
|
184
|
-
* Normalizes
|
|
248
|
+
* Normalizes number by clamping it between min and max month
|
|
185
249
|
*/
|
|
186
|
-
static
|
|
187
|
-
return normalizeToIntNumber(
|
|
250
|
+
static normalizeMonthPart(month) {
|
|
251
|
+
return normalizeToIntNumber(month, MIN_MONTH, MAX_MONTH);
|
|
188
252
|
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Immutable object consisting of year and month
|
|
193
|
-
*/
|
|
194
|
-
class TuiMonth extends TuiYear {
|
|
195
253
|
/**
|
|
196
|
-
*
|
|
197
|
-
* @param month (starting with 0)
|
|
254
|
+
* Tests month for validity
|
|
198
255
|
*/
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
this.month = month;
|
|
202
|
-
tuiAssert.assert(TuiMonth.isValidMonth(year, month));
|
|
256
|
+
static isValidMonthPart(month) {
|
|
257
|
+
return Number.isInteger(month) && inRange(month, MIN_MONTH, MAX_MONTH + 1);
|
|
203
258
|
}
|
|
204
259
|
get formattedMonthPart() {
|
|
205
260
|
return padStart((this.month + 1).toString(), 2, '0');
|
|
@@ -304,61 +359,6 @@ class TuiMonth extends TuiYear {
|
|
|
304
359
|
toUtcNativeDate() {
|
|
305
360
|
return new Date(Date.UTC(this.year, this.month));
|
|
306
361
|
}
|
|
307
|
-
/**
|
|
308
|
-
* Tests month and year for validity
|
|
309
|
-
*/
|
|
310
|
-
static isValidMonth(year, month) {
|
|
311
|
-
return TuiYear.isValidYear(year) && TuiMonth.isValidMonthPart(month);
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* Returns number of days in a month
|
|
315
|
-
*/
|
|
316
|
-
static getMonthDaysCount(month, isLeapYear) {
|
|
317
|
-
tuiAssert.assert(TuiMonth.isValidMonthPart(month));
|
|
318
|
-
switch (month) {
|
|
319
|
-
case 1 /* February */:
|
|
320
|
-
return isLeapYear ? 29 : 28;
|
|
321
|
-
case 3 /* April */:
|
|
322
|
-
case 5 /* June */:
|
|
323
|
-
case 8 /* September */:
|
|
324
|
-
case 10 /* November */:
|
|
325
|
-
return 30;
|
|
326
|
-
default:
|
|
327
|
-
return 31;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* Returns current month and year based on local time zone
|
|
332
|
-
* @nosideeffects
|
|
333
|
-
*/
|
|
334
|
-
static currentLocal() {
|
|
335
|
-
const nativeDate = new Date();
|
|
336
|
-
return new TuiMonth(nativeDate.getFullYear(), nativeDate.getMonth());
|
|
337
|
-
}
|
|
338
|
-
/**
|
|
339
|
-
* Returns current month and year based on UTC
|
|
340
|
-
*/
|
|
341
|
-
static currentUtc() {
|
|
342
|
-
const nativeDate = new Date();
|
|
343
|
-
return new TuiMonth(nativeDate.getUTCFullYear(), nativeDate.getUTCMonth());
|
|
344
|
-
}
|
|
345
|
-
static lengthBetween(from, to) {
|
|
346
|
-
const absoluteFrom = from.month + from.year * 12;
|
|
347
|
-
const absoluteTo = to.month + to.year * 12;
|
|
348
|
-
return absoluteTo - absoluteFrom;
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* Normalizes number by clamping it between min and max month
|
|
352
|
-
*/
|
|
353
|
-
static normalizeMonthPart(month) {
|
|
354
|
-
return normalizeToIntNumber(month, MIN_MONTH, MAX_MONTH);
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* Tests month for validity
|
|
358
|
-
*/
|
|
359
|
-
static isValidMonthPart(month) {
|
|
360
|
-
return Number.isInteger(month) && inRange(month, MIN_MONTH, MAX_MONTH + 1);
|
|
361
|
-
}
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
// TODO: Localized formatting
|
|
@@ -371,53 +371,172 @@ class TuiDay extends TuiMonth {
|
|
|
371
371
|
this.day = day;
|
|
372
372
|
tuiAssert.assert(TuiDay.isValidDay(year, month, day));
|
|
373
373
|
}
|
|
374
|
-
get formattedDayPart() {
|
|
375
|
-
return padStart(this.day.toString(), 2, '0');
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* Formatted whole date
|
|
379
|
-
*/
|
|
380
|
-
get formattedDay() {
|
|
381
|
-
return `${this.formattedDayPart}.${this.formattedMonth}`;
|
|
382
|
-
}
|
|
383
|
-
get isWeekend() {
|
|
384
|
-
const dayOfWeek = this.dayOfWeek(false);
|
|
385
|
-
return dayOfWeek === 6 /* Saturday */ || dayOfWeek === 0 /* Sunday */;
|
|
386
|
-
}
|
|
387
374
|
/**
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
* @param startFromMonday whether week starts from Monday and not from Sunday
|
|
391
|
-
* @return day of week (from 0 to 6)
|
|
375
|
+
* Creates {@link TuiDay} from native {@link Date} based on local time zone
|
|
392
376
|
*/
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
? this.toLocalNativeDate().getDay() - 1
|
|
396
|
-
: this.toLocalNativeDate().getDay();
|
|
397
|
-
return dayOfWeek < 0 ? 6 : dayOfWeek;
|
|
377
|
+
static fromLocalNativeDate(date) {
|
|
378
|
+
return new TuiDay(date.getFullYear(), date.getMonth(), date.getDate());
|
|
398
379
|
}
|
|
399
380
|
/**
|
|
400
|
-
*
|
|
381
|
+
* Creates {@link TuiDay} from native {@link Date} using UTC
|
|
401
382
|
*/
|
|
402
|
-
|
|
403
|
-
return (
|
|
404
|
-
(this.monthSame(another) && this.day < another.day));
|
|
383
|
+
static fromUtcNativeDate(date) {
|
|
384
|
+
return new TuiDay(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
|
|
405
385
|
}
|
|
406
386
|
/**
|
|
407
|
-
*
|
|
387
|
+
* Check validity of year, month and day
|
|
388
|
+
*
|
|
389
|
+
* @param year
|
|
390
|
+
* @param month
|
|
391
|
+
* @param day
|
|
392
|
+
* @return boolean validity
|
|
408
393
|
*/
|
|
409
|
-
|
|
410
|
-
return (
|
|
411
|
-
|
|
394
|
+
static isValidDay(year, month, day) {
|
|
395
|
+
return (TuiMonth.isValidMonth(year, month) &&
|
|
396
|
+
Number.isInteger(day) &&
|
|
397
|
+
inRange(day, MIN_DAY, TuiMonth.getMonthDaysCount(month, TuiYear.isLeapYear(year)) + 1));
|
|
412
398
|
}
|
|
413
399
|
/**
|
|
414
|
-
*
|
|
400
|
+
* @deprecated DONT USE IT (will be deleted soon)
|
|
401
|
+
*
|
|
402
|
+
* Calculated day on a calendar grid
|
|
403
|
+
*
|
|
404
|
+
* @param month
|
|
405
|
+
* @param row row in a calendar
|
|
406
|
+
* @param col column in a calendar
|
|
407
|
+
* @return resulting day on these coordinates (could exceed passed month)
|
|
415
408
|
*/
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
409
|
+
static getDayFromMonthRowCol(month, row, col) {
|
|
410
|
+
tuiAssert.assert(Number.isInteger(row));
|
|
411
|
+
tuiAssert.assert(inRange(row, 0, 6));
|
|
412
|
+
tuiAssert.assert(Number.isInteger(col));
|
|
413
|
+
tuiAssert.assert(inRange(col, 0, DAYS_IN_WEEK));
|
|
414
|
+
let day = row * DAYS_IN_WEEK + col - month.monthStartDaysOffset + 1;
|
|
415
|
+
if (day > month.daysCount) {
|
|
416
|
+
day = day - month.daysCount;
|
|
417
|
+
month = month.append({ month: 1 });
|
|
418
|
+
}
|
|
419
|
+
if (day <= 0) {
|
|
420
|
+
month = month.append({ month: -1 });
|
|
421
|
+
day = month.daysCount + day;
|
|
422
|
+
}
|
|
423
|
+
return new TuiDay(month.year, month.month, day);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Current day based on local time zone
|
|
427
|
+
*/
|
|
428
|
+
static currentLocal() {
|
|
429
|
+
const nativeDate = new Date();
|
|
430
|
+
const year = nativeDate.getFullYear();
|
|
431
|
+
const month = nativeDate.getMonth();
|
|
432
|
+
const day = nativeDate.getDate();
|
|
433
|
+
return new TuiDay(year, month, day);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Calculates {@link TuiDay} normalizing year, month and day. {@link NaN} is turned into minimal value.
|
|
437
|
+
*
|
|
438
|
+
* @param year any year value, including invalid
|
|
439
|
+
* @param month any month value, including invalid (months start with 0)
|
|
440
|
+
* @param day any day value, including invalid
|
|
441
|
+
* @return normalized date
|
|
442
|
+
*/
|
|
443
|
+
static normalizeOf(year, month, day) {
|
|
444
|
+
const normalizedYear = TuiYear.normalizeYearPart(year);
|
|
445
|
+
const normalizedMonth = TuiMonth.normalizeMonthPart(month);
|
|
446
|
+
const normalizedDay = TuiDay.normalizeDayPart(day, normalizedMonth, normalizedYear);
|
|
447
|
+
return new TuiDay(normalizedYear, normalizedMonth, normalizedDay);
|
|
448
|
+
}
|
|
449
|
+
static lengthBetween(from, to) {
|
|
450
|
+
return Math.round((to.toLocalNativeDate().getTime() - from.toLocalNativeDate().getTime()) /
|
|
451
|
+
(1000 * 60 * 60 * 24));
|
|
452
|
+
}
|
|
453
|
+
// TODO: Move month and year related code corresponding classes
|
|
454
|
+
/**
|
|
455
|
+
* Parsing a string with date with normalization
|
|
456
|
+
*
|
|
457
|
+
* @param yearMonthDayString date string in format of DD.MM.Yyyy
|
|
458
|
+
* @return normalized date
|
|
459
|
+
*/
|
|
460
|
+
static normalizeParse(yearMonthDayString) {
|
|
461
|
+
const day = parseInt(yearMonthDayString.slice(0, 2), 10);
|
|
462
|
+
const month = parseInt(yearMonthDayString.slice(3, 5), 10) - 1;
|
|
463
|
+
const year = parseInt(yearMonthDayString.slice(6, 10), 10);
|
|
464
|
+
return TuiDay.normalizeOf(year, month, day);
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Parsing a date stringified in a toJSON format
|
|
468
|
+
* @param yearMonthDayString date string in format of YYYY-MM-DD
|
|
469
|
+
* @return date
|
|
470
|
+
* @throws exceptions if any part of the date is invalid
|
|
471
|
+
*/
|
|
472
|
+
static jsonParse(yearMonthDayString) {
|
|
473
|
+
const day = parseInt(yearMonthDayString.slice(8, 10), 10);
|
|
474
|
+
const month = parseInt(yearMonthDayString.slice(5, 7), 10) - 1;
|
|
475
|
+
const year = parseInt(yearMonthDayString.slice(0, 4), 10);
|
|
476
|
+
if (!TuiYear.isValidYear(year)) {
|
|
477
|
+
throw new Error('Invalid year: ' + year);
|
|
478
|
+
}
|
|
479
|
+
if (!TuiMonth.isValidMonth(year, month)) {
|
|
480
|
+
throw new Error('Invalid month: ' + month);
|
|
481
|
+
}
|
|
482
|
+
if (!Number.isInteger(day) ||
|
|
483
|
+
!inRange(day, MIN_DAY, TuiMonth.getMonthDaysCount(month, TuiYear.isLeapYear(year)) + 1)) {
|
|
484
|
+
throw new Error('Invalid day: ' + day);
|
|
485
|
+
}
|
|
486
|
+
return new TuiDay(year, month, day);
|
|
487
|
+
}
|
|
488
|
+
static normalizeDayPart(day, month, year) {
|
|
489
|
+
tuiAssert.assert(TuiMonth.isValidMonth(year, month));
|
|
490
|
+
const monthDaysCount = TuiMonth.getMonthDaysCount(month, TuiYear.isLeapYear(year));
|
|
491
|
+
return normalizeToIntNumber(day, 1, monthDaysCount);
|
|
492
|
+
}
|
|
493
|
+
get formattedDayPart() {
|
|
494
|
+
return padStart(this.day.toString(), 2, '0');
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Formatted whole date
|
|
498
|
+
*/
|
|
499
|
+
get formattedDay() {
|
|
500
|
+
return `${this.formattedDayPart}.${this.formattedMonth}`;
|
|
501
|
+
}
|
|
502
|
+
get isWeekend() {
|
|
503
|
+
const dayOfWeek = this.dayOfWeek(false);
|
|
504
|
+
return dayOfWeek === 6 /* Saturday */ || dayOfWeek === 0 /* Sunday */;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Returns day of week
|
|
508
|
+
*
|
|
509
|
+
* @param startFromMonday whether week starts from Monday and not from Sunday
|
|
510
|
+
* @return day of week (from 0 to 6)
|
|
511
|
+
*/
|
|
512
|
+
dayOfWeek(startFromMonday = true) {
|
|
513
|
+
const dayOfWeek = startFromMonday
|
|
514
|
+
? this.toLocalNativeDate().getDay() - 1
|
|
515
|
+
: this.toLocalNativeDate().getDay();
|
|
516
|
+
return dayOfWeek < 0 ? 6 : dayOfWeek;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Passed date is after current
|
|
520
|
+
*/
|
|
521
|
+
dayBefore(another) {
|
|
522
|
+
return (this.monthBefore(another) ||
|
|
523
|
+
(this.monthSame(another) && this.day < another.day));
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Passed date is after or equals to current
|
|
527
|
+
*/
|
|
528
|
+
daySameOrBefore(another) {
|
|
529
|
+
return (this.monthBefore(another) ||
|
|
530
|
+
(this.monthSame(another) && this.day <= another.day));
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Passed date is the same as current
|
|
534
|
+
*/
|
|
535
|
+
daySame(another) {
|
|
536
|
+
return this.monthSame(another) && this.day === another.day;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Passed date is either before or the same as current
|
|
421
540
|
*/
|
|
422
541
|
daySameOrAfter(another) {
|
|
423
542
|
return (this.monthAfter(another) ||
|
|
@@ -508,125 +627,6 @@ class TuiDay extends TuiMonth {
|
|
|
508
627
|
toUtcNativeDate() {
|
|
509
628
|
return new Date(Date.UTC(this.year, this.month, this.day));
|
|
510
629
|
}
|
|
511
|
-
/**
|
|
512
|
-
* Creates {@link TuiDay} from native {@link Date} based on local time zone
|
|
513
|
-
*/
|
|
514
|
-
static fromLocalNativeDate(date) {
|
|
515
|
-
return new TuiDay(date.getFullYear(), date.getMonth(), date.getDate());
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* Creates {@link TuiDay} from native {@link Date} using UTC
|
|
519
|
-
*/
|
|
520
|
-
static fromUtcNativeDate(date) {
|
|
521
|
-
return new TuiDay(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
|
|
522
|
-
}
|
|
523
|
-
/**
|
|
524
|
-
* Check validity of year, month and day
|
|
525
|
-
*
|
|
526
|
-
* @param year
|
|
527
|
-
* @param month
|
|
528
|
-
* @param day
|
|
529
|
-
* @return boolean validity
|
|
530
|
-
*/
|
|
531
|
-
static isValidDay(year, month, day) {
|
|
532
|
-
return (TuiMonth.isValidMonth(year, month) &&
|
|
533
|
-
Number.isInteger(day) &&
|
|
534
|
-
inRange(day, MIN_DAY, TuiMonth.getMonthDaysCount(month, TuiYear.isLeapYear(year)) + 1));
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* @deprecated DONT USE IT (will be deleted soon)
|
|
538
|
-
*
|
|
539
|
-
* Calculated day on a calendar grid
|
|
540
|
-
*
|
|
541
|
-
* @param month
|
|
542
|
-
* @param row row in a calendar
|
|
543
|
-
* @param col column in a calendar
|
|
544
|
-
* @return resulting day on these coordinates (could exceed passed month)
|
|
545
|
-
*/
|
|
546
|
-
static getDayFromMonthRowCol(month, row, col) {
|
|
547
|
-
tuiAssert.assert(Number.isInteger(row));
|
|
548
|
-
tuiAssert.assert(inRange(row, 0, 6));
|
|
549
|
-
tuiAssert.assert(Number.isInteger(col));
|
|
550
|
-
tuiAssert.assert(inRange(col, 0, DAYS_IN_WEEK));
|
|
551
|
-
let day = row * DAYS_IN_WEEK + col - month.monthStartDaysOffset + 1;
|
|
552
|
-
if (day > month.daysCount) {
|
|
553
|
-
day = day - month.daysCount;
|
|
554
|
-
month = month.append({ month: 1 });
|
|
555
|
-
}
|
|
556
|
-
if (day <= 0) {
|
|
557
|
-
month = month.append({ month: -1 });
|
|
558
|
-
day = month.daysCount + day;
|
|
559
|
-
}
|
|
560
|
-
return new TuiDay(month.year, month.month, day);
|
|
561
|
-
}
|
|
562
|
-
/**
|
|
563
|
-
* Current day based on local time zone
|
|
564
|
-
*/
|
|
565
|
-
static currentLocal() {
|
|
566
|
-
const nativeDate = new Date();
|
|
567
|
-
const year = nativeDate.getFullYear();
|
|
568
|
-
const month = nativeDate.getMonth();
|
|
569
|
-
const day = nativeDate.getDate();
|
|
570
|
-
return new TuiDay(year, month, day);
|
|
571
|
-
}
|
|
572
|
-
/**
|
|
573
|
-
* Calculates {@link TuiDay} normalizing year, month and day. {@link NaN} is turned into minimal value.
|
|
574
|
-
*
|
|
575
|
-
* @param year any year value, including invalid
|
|
576
|
-
* @param month any month value, including invalid (months start with 0)
|
|
577
|
-
* @param day any day value, including invalid
|
|
578
|
-
* @return normalized date
|
|
579
|
-
*/
|
|
580
|
-
static normalizeOf(year, month, day) {
|
|
581
|
-
const normalizedYear = TuiYear.normalizeYearPart(year);
|
|
582
|
-
const normalizedMonth = TuiMonth.normalizeMonthPart(month);
|
|
583
|
-
const normalizedDay = TuiDay.normalizeDayPart(day, normalizedMonth, normalizedYear);
|
|
584
|
-
return new TuiDay(normalizedYear, normalizedMonth, normalizedDay);
|
|
585
|
-
}
|
|
586
|
-
static lengthBetween(from, to) {
|
|
587
|
-
return Math.round((to.toLocalNativeDate().getTime() - from.toLocalNativeDate().getTime()) /
|
|
588
|
-
(1000 * 60 * 60 * 24));
|
|
589
|
-
}
|
|
590
|
-
// TODO: Move month and year related code corresponding classes
|
|
591
|
-
/**
|
|
592
|
-
* Parsing a string with date with normalization
|
|
593
|
-
*
|
|
594
|
-
* @param yearMonthDayString date string in format of DD.MM.Yyyy
|
|
595
|
-
* @return normalized date
|
|
596
|
-
*/
|
|
597
|
-
static normalizeParse(yearMonthDayString) {
|
|
598
|
-
const day = parseInt(yearMonthDayString.slice(0, 2), 10);
|
|
599
|
-
const month = parseInt(yearMonthDayString.slice(3, 5), 10) - 1;
|
|
600
|
-
const year = parseInt(yearMonthDayString.slice(6, 10), 10);
|
|
601
|
-
return TuiDay.normalizeOf(year, month, day);
|
|
602
|
-
}
|
|
603
|
-
/**
|
|
604
|
-
* Parsing a date stringified in a toJSON format
|
|
605
|
-
* @param yearMonthDayString date string in format of YYYY-MM-DD
|
|
606
|
-
* @return date
|
|
607
|
-
* @throws exceptions if any part of the date is invalid
|
|
608
|
-
*/
|
|
609
|
-
static jsonParse(yearMonthDayString) {
|
|
610
|
-
const day = parseInt(yearMonthDayString.slice(8, 10), 10);
|
|
611
|
-
const month = parseInt(yearMonthDayString.slice(5, 7), 10) - 1;
|
|
612
|
-
const year = parseInt(yearMonthDayString.slice(0, 4), 10);
|
|
613
|
-
if (!TuiYear.isValidYear(year)) {
|
|
614
|
-
throw new Error('Invalid year: ' + year);
|
|
615
|
-
}
|
|
616
|
-
if (!TuiMonth.isValidMonth(year, month)) {
|
|
617
|
-
throw new Error('Invalid month: ' + month);
|
|
618
|
-
}
|
|
619
|
-
if (!Number.isInteger(day) ||
|
|
620
|
-
!inRange(day, MIN_DAY, TuiMonth.getMonthDaysCount(month, TuiYear.isLeapYear(year)) + 1)) {
|
|
621
|
-
throw new Error('Invalid day: ' + day);
|
|
622
|
-
}
|
|
623
|
-
return new TuiDay(year, month, day);
|
|
624
|
-
}
|
|
625
|
-
static normalizeDayPart(day, month, year) {
|
|
626
|
-
tuiAssert.assert(TuiMonth.isValidMonth(year, month));
|
|
627
|
-
const monthDaysCount = TuiMonth.getMonthDaysCount(month, TuiYear.isLeapYear(year));
|
|
628
|
-
return normalizeToIntNumber(day, 1, monthDaysCount);
|
|
629
|
-
}
|
|
630
630
|
}
|
|
631
631
|
|
|
632
632
|
/**
|
|
@@ -638,6 +638,11 @@ class TuiMonthRange {
|
|
|
638
638
|
this.to = to;
|
|
639
639
|
tuiAssert.assert(from.monthSameOrBefore(to));
|
|
640
640
|
}
|
|
641
|
+
static sort(month1, month2) {
|
|
642
|
+
return month1.monthSameOrBefore(month2)
|
|
643
|
+
? new TuiMonthRange(month1, month2)
|
|
644
|
+
: new TuiMonthRange(month2, month1);
|
|
645
|
+
}
|
|
641
646
|
get isSingleMonth() {
|
|
642
647
|
return this.from.monthSame(this.to);
|
|
643
648
|
}
|
|
@@ -650,11 +655,6 @@ class TuiMonthRange {
|
|
|
650
655
|
toString() {
|
|
651
656
|
return `${this.from}${RANGE_SEPARATOR_CHAR}${this.to}`;
|
|
652
657
|
}
|
|
653
|
-
static sort(month1, month2) {
|
|
654
|
-
return month1.monthSameOrBefore(month2)
|
|
655
|
-
? new TuiMonthRange(month1, month2)
|
|
656
|
-
: new TuiMonthRange(month2, month1);
|
|
657
|
-
}
|
|
658
658
|
}
|
|
659
659
|
|
|
660
660
|
/**
|
|
@@ -667,6 +667,31 @@ class TuiDayRange extends TuiMonthRange {
|
|
|
667
667
|
this.to = to;
|
|
668
668
|
tuiAssert.assert(from.daySameOrBefore(to));
|
|
669
669
|
}
|
|
670
|
+
/**
|
|
671
|
+
* Creates range from two days after sorting them
|
|
672
|
+
*
|
|
673
|
+
* @param day1
|
|
674
|
+
* @param day2
|
|
675
|
+
* @return new range with sorted days
|
|
676
|
+
*/
|
|
677
|
+
static sort(day1, day2) {
|
|
678
|
+
return day1.daySameOrBefore(day2)
|
|
679
|
+
? new TuiDayRange(day1, day2)
|
|
680
|
+
: new TuiDayRange(day2, day1);
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Parse and correct a day range in string format
|
|
684
|
+
*
|
|
685
|
+
* @param rangeString a string of dates in a format dd.mm.yyyy - dd.mm.yyyy
|
|
686
|
+
* @return normalized day range object
|
|
687
|
+
*/
|
|
688
|
+
static normalizeParse(rangeString) {
|
|
689
|
+
const leftDay = TuiDay.normalizeParse(rangeString.slice(0, DATE_FILLER_LENGTH));
|
|
690
|
+
if (rangeString.length < DATE_RANGE_FILLER_LENGTH) {
|
|
691
|
+
return new TuiDayRange(leftDay, leftDay);
|
|
692
|
+
}
|
|
693
|
+
return TuiDayRange.sort(leftDay, TuiDay.normalizeParse(rangeString.slice(DATE_FILLER_LENGTH + RANGE_SEPARATOR_CHAR.length)));
|
|
694
|
+
}
|
|
670
695
|
get isSingleDay() {
|
|
671
696
|
return this.from.daySame(this.to);
|
|
672
697
|
}
|
|
@@ -695,31 +720,6 @@ class TuiDayRange extends TuiMonthRange {
|
|
|
695
720
|
dayLimit(min, max) {
|
|
696
721
|
return new TuiDayRange(this.from.dayLimit(min, max), this.to.dayLimit(min, max));
|
|
697
722
|
}
|
|
698
|
-
/**
|
|
699
|
-
* Creates range from two days after sorting them
|
|
700
|
-
*
|
|
701
|
-
* @param day1
|
|
702
|
-
* @param day2
|
|
703
|
-
* @return new range with sorted days
|
|
704
|
-
*/
|
|
705
|
-
static sort(day1, day2) {
|
|
706
|
-
return day1.daySameOrBefore(day2)
|
|
707
|
-
? new TuiDayRange(day1, day2)
|
|
708
|
-
: new TuiDayRange(day2, day1);
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* Parse and correct a day range in string format
|
|
712
|
-
*
|
|
713
|
-
* @param rangeString a string of dates in a format dd.mm.yyyy - dd.mm.yyyy
|
|
714
|
-
* @return normalized day range object
|
|
715
|
-
*/
|
|
716
|
-
static normalizeParse(rangeString) {
|
|
717
|
-
const leftDay = TuiDay.normalizeParse(rangeString.slice(0, DATE_FILLER_LENGTH));
|
|
718
|
-
if (rangeString.length < DATE_RANGE_FILLER_LENGTH) {
|
|
719
|
-
return new TuiDayRange(leftDay, leftDay);
|
|
720
|
-
}
|
|
721
|
-
return TuiDayRange.sort(leftDay, TuiDay.normalizeParse(rangeString.slice(DATE_FILLER_LENGTH + RANGE_SEPARATOR_CHAR.length)));
|
|
722
|
-
}
|
|
723
723
|
}
|
|
724
724
|
|
|
725
725
|
const TUI_FIRST_DAY = new TuiDay(MIN_YEAR, MIN_MONTH, MIN_DAY);
|
|
@@ -736,45 +736,6 @@ class TuiTime {
|
|
|
736
736
|
this.ms = ms;
|
|
737
737
|
tuiAssert.assert(TuiTime.isValidTime(hours, minutes, seconds, ms), 'Time must be real, but got:', hours, minutes, seconds, ms);
|
|
738
738
|
}
|
|
739
|
-
/**
|
|
740
|
-
* Shifts time by hours and minutes
|
|
741
|
-
*/
|
|
742
|
-
shift({ hours = 0, minutes = 0, seconds = 0, ms = 0 }) {
|
|
743
|
-
const newMs = (1000 + this.ms + (ms % 1000)) % 1000;
|
|
744
|
-
const secondsInMs = ms < 0 ? Math.ceil(ms / 1000) : Math.floor(ms / 1000);
|
|
745
|
-
const secondsToAdd = secondsInMs + seconds;
|
|
746
|
-
const newSeconds = (60 + this.seconds + (secondsToAdd % 60)) % 60;
|
|
747
|
-
const minutesInSeconds = secondsToAdd < 0
|
|
748
|
-
? Math.ceil(secondsToAdd / 60)
|
|
749
|
-
: Math.floor(secondsToAdd / 60);
|
|
750
|
-
const minutesToAdd = minutesInSeconds + minutes;
|
|
751
|
-
const newMinutes = (60 + this.minutes + (minutesToAdd % 60)) % 60;
|
|
752
|
-
const hoursInMinutes = minutesToAdd < 0
|
|
753
|
-
? Math.ceil(minutesToAdd / 60)
|
|
754
|
-
: Math.floor(minutesToAdd / 60);
|
|
755
|
-
const hoursToAdd = hoursInMinutes + hours;
|
|
756
|
-
const newHours = (24 + this.hours + (hoursToAdd % 24)) % 24;
|
|
757
|
-
return new TuiTime(newHours, newMinutes, newSeconds, newMs);
|
|
758
|
-
}
|
|
759
|
-
/**
|
|
760
|
-
* Converts TuiTime to string
|
|
761
|
-
*/
|
|
762
|
-
toString(mode) {
|
|
763
|
-
const needAddMs = mode === 'HH:MM:SS.MSS' || (!mode && this.ms > 0);
|
|
764
|
-
const needAddSeconds = needAddMs || mode === 'HH:MM:SS' || (!mode && this.seconds > 0);
|
|
765
|
-
return (`${this.formatTime(this.hours)}:${this.formatTime(this.minutes)}` +
|
|
766
|
-
`${needAddSeconds ? ':' + this.formatTime(this.seconds) : ''}` +
|
|
767
|
-
`${needAddMs ? '.' + this.formatTime(this.ms, 3) : ''}`);
|
|
768
|
-
}
|
|
769
|
-
/**
|
|
770
|
-
* Converts TuiTime to milliseconds
|
|
771
|
-
*/
|
|
772
|
-
toAbsoluteMilliseconds() {
|
|
773
|
-
return (this.hours * MILLISECONDS_IN_HOUR +
|
|
774
|
-
this.minutes * MILLISECONDS_IN_MINUTE +
|
|
775
|
-
this.seconds * 1000 +
|
|
776
|
-
this.ms);
|
|
777
|
-
}
|
|
778
739
|
/**
|
|
779
740
|
* Checks if time is valid
|
|
780
741
|
*/
|
|
@@ -831,6 +792,45 @@ class TuiTime {
|
|
|
831
792
|
static fromLocalNativeDate(date) {
|
|
832
793
|
return new TuiTime(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
|
|
833
794
|
}
|
|
795
|
+
/**
|
|
796
|
+
* Shifts time by hours and minutes
|
|
797
|
+
*/
|
|
798
|
+
shift({ hours = 0, minutes = 0, seconds = 0, ms = 0 }) {
|
|
799
|
+
const newMs = (1000 + this.ms + (ms % 1000)) % 1000;
|
|
800
|
+
const secondsInMs = ms < 0 ? Math.ceil(ms / 1000) : Math.floor(ms / 1000);
|
|
801
|
+
const secondsToAdd = secondsInMs + seconds;
|
|
802
|
+
const newSeconds = (60 + this.seconds + (secondsToAdd % 60)) % 60;
|
|
803
|
+
const minutesInSeconds = secondsToAdd < 0
|
|
804
|
+
? Math.ceil(secondsToAdd / 60)
|
|
805
|
+
: Math.floor(secondsToAdd / 60);
|
|
806
|
+
const minutesToAdd = minutesInSeconds + minutes;
|
|
807
|
+
const newMinutes = (60 + this.minutes + (minutesToAdd % 60)) % 60;
|
|
808
|
+
const hoursInMinutes = minutesToAdd < 0
|
|
809
|
+
? Math.ceil(minutesToAdd / 60)
|
|
810
|
+
: Math.floor(minutesToAdd / 60);
|
|
811
|
+
const hoursToAdd = hoursInMinutes + hours;
|
|
812
|
+
const newHours = (24 + this.hours + (hoursToAdd % 24)) % 24;
|
|
813
|
+
return new TuiTime(newHours, newMinutes, newSeconds, newMs);
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Converts TuiTime to string
|
|
817
|
+
*/
|
|
818
|
+
toString(mode) {
|
|
819
|
+
const needAddMs = mode === 'HH:MM:SS.MSS' || (!mode && this.ms > 0);
|
|
820
|
+
const needAddSeconds = needAddMs || mode === 'HH:MM:SS' || (!mode && this.seconds > 0);
|
|
821
|
+
return (`${this.formatTime(this.hours)}:${this.formatTime(this.minutes)}` +
|
|
822
|
+
`${needAddSeconds ? ':' + this.formatTime(this.seconds) : ''}` +
|
|
823
|
+
`${needAddMs ? '.' + this.formatTime(this.ms, 3) : ''}`);
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Converts TuiTime to milliseconds
|
|
827
|
+
*/
|
|
828
|
+
toAbsoluteMilliseconds() {
|
|
829
|
+
return (this.hours * MILLISECONDS_IN_HOUR +
|
|
830
|
+
this.minutes * MILLISECONDS_IN_MINUTE +
|
|
831
|
+
this.seconds * 1000 +
|
|
832
|
+
this.ms);
|
|
833
|
+
}
|
|
834
834
|
formatTime(time, digits = 2) {
|
|
835
835
|
return padStart(time.toString(), digits, '0');
|
|
836
836
|
}
|