@central-design-system/components 3.0.0-alpha.3 → 3.0.0-alpha.4

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,1100 @@
1
+ import { isDate, pad } from '@components/utils';
2
+ import defaultLang from '@components/locale/ru';
3
+
4
+ import type { ILocale } from '@components/locale';
5
+
6
+ export interface IRawDate {
7
+ year?: number;
8
+ month?: number;
9
+ day?: number;
10
+ hour?: number;
11
+ minute?: number;
12
+ second?: number;
13
+ millisecond?: number;
14
+ timezoneOffset?: number;
15
+ dateHash?: string;
16
+ timeHash?: string;
17
+ date?: number;
18
+ }
19
+
20
+ export type TDateLocale = ILocale['date'];
21
+
22
+ const MILLISECONDS_IN_DAY: number = 86400000;
23
+ const MILLISECONDS_IN_HOUR: number = 3600000;
24
+ const MILLISECONDS_IN_MINUTE: number = 60000;
25
+ const defaultMask: string = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
26
+ const token: RegExp =
27
+ /\[((?:[^\]\\]|\\]|\\)*)\]|d{1,4}|M{1,4}|m{1,2}|w{1,2}|Qo|Do|D{1,4}|YY(?:YY)?|H{1,2}|h{1,2}|s{1,2}|S{1,3}|Z{1,2}|a{1,2}|[AQExX]/g;
28
+ const reverseToken: RegExp =
29
+ /(\[[^\]]*\])|d{1,4}|M{1,4}|m{1,2}|w{1,2}|Qo|Do|D{1,4}|YY(?:YY)?|H{1,2}|h{1,2}|s{1,2}|S{1,3}|Z{1,2}|a{1,2}|[AQExX]|([.*+:?^,\s${}()|\\]+)/g;
30
+ const regexStore: Record<string, any> = {};
31
+
32
+ const formatter: Record<string, Function> = {
33
+ // Year: 00, 01, ..., 99
34
+ YY(date: Date, dateLocale: TDateLocale, forcedYear: number) {
35
+ // workaround for < 1900 with new Date()
36
+ const y = this.YYYY(date, dateLocale, forcedYear) % 100;
37
+ return y >= 0 ? pad(y) : '-' + pad(Math.abs(y));
38
+ },
39
+
40
+ // Year: 1900, 1901, ..., 2099
41
+ YYYY(date: Date, _dateLocale: TDateLocale, forcedYear: number) {
42
+ // workaround for < 1900 with new Date()
43
+ return forcedYear !== void 0 && forcedYear !== null ? forcedYear : date.getFullYear();
44
+ },
45
+
46
+ // Month: 1, 2, ..., 12
47
+ M(date: Date) {
48
+ return date.getMonth() + 1;
49
+ },
50
+
51
+ // Month: 01, 02, ..., 12
52
+ MM(date: Date) {
53
+ return pad(date.getMonth() + 1);
54
+ },
55
+
56
+ // Month Short Name: Jan, Feb, ...
57
+ MMM(date: Date, dateLocale: TDateLocale) {
58
+ return dateLocale.monthsShort[date.getMonth()];
59
+ },
60
+
61
+ // Month Name: January, February, ...
62
+ MMMM(date: Date, dateLocale: TDateLocale) {
63
+ return dateLocale.months[date.getMonth()];
64
+ },
65
+
66
+ // Quarter: 1, 2, 3, 4
67
+ Q(date: Date) {
68
+ return Math.ceil((date.getMonth() + 1) / 3);
69
+ },
70
+
71
+ // Quarter: 1st, 2nd, 3rd, 4th
72
+ Qo(date: Date) {
73
+ return getOrdinal(this.Q(date));
74
+ },
75
+
76
+ // Day of month: 1, 2, ..., 31
77
+ D(date: Date) {
78
+ return date.getDate();
79
+ },
80
+
81
+ // Day of month: 1st, 2nd, ..., 31st
82
+ Do(date: Date) {
83
+ return getOrdinal(date.getDate());
84
+ },
85
+
86
+ // Day of month: 01, 02, ..., 31
87
+ DD(date: Date) {
88
+ return pad(date.getDate());
89
+ },
90
+
91
+ // Day of year: 1, 2, ..., 366
92
+ DDD(date: Date) {
93
+ return getDayOfYear(date);
94
+ },
95
+
96
+ // Day of year: 001, 002, ..., 366
97
+ DDDD(date: Date) {
98
+ return pad(getDayOfYear(date) as number, 3);
99
+ },
100
+
101
+ // Day of week: 0, 1, ..., 6
102
+ d(date: Date) {
103
+ return date.getDay();
104
+ },
105
+
106
+ // Day of week: Su, Mo, ...
107
+ dd(date: Date, dateLocale: TDateLocale) {
108
+ return this.dddd(date, dateLocale).slice(0, 2);
109
+ },
110
+
111
+ // Day of week: Sun, Mon, ...
112
+ ddd(date: Date, dateLocale: TDateLocale) {
113
+ return dateLocale.daysShort[date.getDay()];
114
+ },
115
+
116
+ // Day of week: Sunday, Monday, ...
117
+ dddd(date: Date, dateLocale: TDateLocale) {
118
+ return dateLocale.days[date.getDay()];
119
+ },
120
+
121
+ // Day of ISO week: 1, 2, ..., 7
122
+ E(date: Date) {
123
+ return date.getDay() || 7;
124
+ },
125
+
126
+ // Week of Year: 1 2 ... 52 53
127
+ w(date: Date) {
128
+ return getWeekOfYear(date);
129
+ },
130
+
131
+ // Week of Year: 01 02 ... 52 53
132
+ ww(date: Date) {
133
+ return pad(getWeekOfYear(date));
134
+ },
135
+
136
+ // Hour: 0, 1, ... 23
137
+ H(date: Date) {
138
+ return date.getHours();
139
+ },
140
+
141
+ // Hour: 00, 01, ..., 23
142
+ HH(date: Date) {
143
+ return pad(date.getHours());
144
+ },
145
+
146
+ // Hour: 1, 2, ..., 12
147
+ h(date: Date) {
148
+ const hours = date.getHours();
149
+ return hours === 0 ? 12 : hours > 12 ? hours % 12 : hours;
150
+ },
151
+
152
+ // Hour: 01, 02, ..., 12
153
+ hh(date: Date) {
154
+ return pad(this.h(date));
155
+ },
156
+
157
+ // Minute: 0, 1, ..., 59
158
+ m(date: Date) {
159
+ return date.getMinutes();
160
+ },
161
+
162
+ // Minute: 00, 01, ..., 59
163
+ mm(date: Date) {
164
+ return pad(date.getMinutes());
165
+ },
166
+
167
+ // Second: 0, 1, ..., 59
168
+ s(date: Date) {
169
+ return date.getSeconds();
170
+ },
171
+
172
+ // Second: 00, 01, ..., 59
173
+ ss(date: Date) {
174
+ return pad(date.getSeconds());
175
+ },
176
+
177
+ // 1/10 of second: 0, 1, ..., 9
178
+ S(date: Date) {
179
+ return Math.floor(date.getMilliseconds() / 100);
180
+ },
181
+
182
+ // 1/100 of second: 00, 01, ..., 99
183
+ SS(date: Date) {
184
+ return pad(Math.floor(date.getMilliseconds() / 10));
185
+ },
186
+
187
+ // Millisecond: 000, 001, ..., 999
188
+ SSS(date: Date) {
189
+ return pad(date.getMilliseconds(), 3);
190
+ },
191
+
192
+ // Meridiem: AM, PM
193
+ A(date: Date) {
194
+ return this.H(date) < 12 ? 'AM' : 'PM';
195
+ },
196
+
197
+ // Meridiem: am, pm
198
+ a(date: Date) {
199
+ return this.H(date) < 12 ? 'am' : 'pm';
200
+ },
201
+
202
+ // Meridiem: a.m., p.m.
203
+ aa(date: Date) {
204
+ return this.H(date) < 12 ? 'a.m.' : 'p.m.';
205
+ },
206
+
207
+ // Timezone: -01:00, +00:00, ... +12:00
208
+ Z(date: Date, _dateLocale: TDateLocale, _forcedYear: number, forcedTimezoneOffset: number) {
209
+ const tzOffset =
210
+ forcedTimezoneOffset === void 0 || forcedTimezoneOffset === null
211
+ ? date.getTimezoneOffset()
212
+ : forcedTimezoneOffset;
213
+
214
+ return formatTimezone(tzOffset, ':');
215
+ },
216
+
217
+ // Timezone: -0100, +0000, ... +1200
218
+ ZZ(date: Date, _dateLocale: TDateLocale, _forcedYear: number, forcedTimezoneOffset: number) {
219
+ const tzOffset =
220
+ forcedTimezoneOffset === void 0 || forcedTimezoneOffset === null
221
+ ? date.getTimezoneOffset()
222
+ : forcedTimezoneOffset;
223
+
224
+ return formatTimezone(tzOffset);
225
+ },
226
+
227
+ // Seconds timestamp: 512969520
228
+ X(date: Date) {
229
+ return Math.floor(date.getTime() / 1000);
230
+ },
231
+
232
+ // Milliseconds timestamp: 512969520900
233
+ x(date: Date) {
234
+ return date.getTime();
235
+ }
236
+ };
237
+
238
+ /**
239
+ * Format date by mask
240
+ * @param value
241
+ * @param mask
242
+ * @param dateLocale
243
+ * @param __forcedYear
244
+ * @param __forcedTimezoneOffset
245
+ */
246
+ export function formatDate(
247
+ value: any,
248
+ mask: string,
249
+ dateLocale: TDateLocale,
250
+ __forcedYear: number,
251
+ __forcedTimezoneOffset: number
252
+ ): string | undefined {
253
+ if ((value !== 0 && !value) || value === Infinity || value === -Infinity) {
254
+ return;
255
+ }
256
+
257
+ const date = new Date(value) as unknown as number;
258
+
259
+ if (isNaN(date)) {
260
+ return;
261
+ }
262
+
263
+ if (mask === void 0) {
264
+ mask = defaultMask;
265
+ }
266
+
267
+ const locale = getDateLocale(dateLocale, defaultLang);
268
+
269
+ return mask.replace(token, (match, text) =>
270
+ match in formatter
271
+ ? formatter[match](date, locale, __forcedYear, __forcedTimezoneOffset)
272
+ : text === void 0
273
+ ? match
274
+ : text.split('\\]').join(']')
275
+ );
276
+ }
277
+
278
+ /**
279
+ * Set a specific year, month and day to a date.
280
+ * @param date
281
+ * @param rawModified
282
+ * @param utc
283
+ */
284
+ export function adjustDate(date: Date, rawModified: IRawDate, utc: boolean) {
285
+ const modified = normalizeModified(rawModified);
286
+ const middle = utc ? 'UTC' : '';
287
+ const _date = new Date(date);
288
+ const adjustedDate =
289
+ modified.year || modified.month || modified.date ? applyYearMonthDay(_date, modified, middle) : _date;
290
+
291
+ for (const key in modified) {
292
+ const op = key.charAt(0).toUpperCase() + key.slice(1);
293
+ (adjustedDate as Date & Record<string, any>)[`set${middle}${op}`](modified[key]);
294
+ }
295
+
296
+ return adjustedDate;
297
+ }
298
+
299
+ /**
300
+ * Parse any string into a date object based on the format passed.
301
+ * @param str
302
+ * @param mask
303
+ * @param dateLocale
304
+ */
305
+ export function extractDate(str: string, mask: string, dateLocale: TDateLocale) {
306
+ const sDate = splitDate(str, mask, dateLocale);
307
+
308
+ const date = new Date(
309
+ sDate.year as number,
310
+ (sDate.month ? sDate.month - 1 : null) as number,
311
+ sDate.day ? sDate.day : 1,
312
+ sDate.hour,
313
+ sDate.minute,
314
+ sDate.second,
315
+ sDate.millisecond
316
+ );
317
+
318
+ const tzOffset = date.getTimezoneOffset();
319
+
320
+ return !sDate.timezoneOffset || sDate.timezoneOffset === tzOffset
321
+ ? date
322
+ : getChange(date, { minutes: sDate.timezoneOffset - tzOffset }, 1);
323
+ }
324
+
325
+ /**
326
+ * Split date by mask.
327
+ * @param str
328
+ * @param mask
329
+ * @param dateLocale
330
+ * @param defaultModel
331
+ */
332
+ export function splitDate(str: string, mask: string, dateLocale: TDateLocale, defaultModel?: IRawDate): IRawDate {
333
+ const date: IRawDate | Record<string, number | string | null> = {
334
+ year: null,
335
+ month: null,
336
+ day: null,
337
+ hour: null,
338
+ minute: null,
339
+ second: null,
340
+ millisecond: null,
341
+ timezoneOffset: null,
342
+ dateHash: null,
343
+ timeHash: null
344
+ };
345
+
346
+ defaultModel !== undefined && Object.assign(date, defaultModel);
347
+
348
+ if (str === undefined || str === null || str === '') return date;
349
+
350
+ if (!mask) mask = defaultMask;
351
+
352
+ const langOpts = getDateLocale(dateLocale, defaultLang);
353
+ const months = langOpts.months;
354
+ const monthsShort = langOpts.monthsShort;
355
+
356
+ const { regex, map } = getRegexData(mask, langOpts);
357
+ const match = str.match(regex);
358
+
359
+ if (match === null) return date;
360
+
361
+ let tzString = '';
362
+
363
+ if (map.X || map.x) {
364
+ const stamp = parseInt(match[map.X ? map.X : map.x], 10);
365
+
366
+ if (isNaN(stamp) || stamp < 0) return date;
367
+
368
+ const _date = new Date(stamp * (map.X ? 1000 : 0));
369
+
370
+ date.year = _date.getFullYear();
371
+ date.month = _date.getMonth() + 1;
372
+ date.date = _date.getDate();
373
+ date.hour = _date.getHours();
374
+ date.minute = _date.getMinutes();
375
+ date.second = _date.getSeconds();
376
+ date.millisecond = _date.getMilliseconds();
377
+ } else {
378
+ if (map.YYYY) {
379
+ date.year = parseInt(match[map.YYYY], 10);
380
+ } else if (map.YY) {
381
+ const year = parseInt(match[map.YY], 10);
382
+ date.year = year < 0 ? year : 2000 + year;
383
+ }
384
+
385
+ if (map.M) {
386
+ date.month = parseInt(match[map.M], 10);
387
+ if (date.month < 1 || date.month > 12) return date;
388
+ } else if (map.MMM) {
389
+ date.month = monthsShort.indexOf(match[map.MMM]) + 1;
390
+ } else if (map.MMMM) {
391
+ date.month = months.indexOf(match[map.MMMM]) + 1;
392
+ }
393
+
394
+ if (map.D) {
395
+ date.day = parseInt(match[map.D], 10);
396
+ if (date.year === null || date.month === null || date.day < 1) return date;
397
+
398
+ const maxDay = new Date(date.year as number, date.month as number, 0).getDate();
399
+ if (date.day > maxDay) return date;
400
+ }
401
+
402
+ if (map.H) {
403
+ date.hour = parseInt(match[map.H], 10) % 24;
404
+ } else if (map.h) {
405
+ date.hour = parseInt(match[map.h], 10) % 12;
406
+
407
+ if (
408
+ (map.A && match[map.A] === 'PM') ||
409
+ (map.a && match[map.a] === 'pm') ||
410
+ (map.aa && match[map.aa] === 'p.m.')
411
+ ) {
412
+ date.hour += 12;
413
+ }
414
+
415
+ date.hour = date.hour % 24;
416
+ }
417
+
418
+ if (map.m) {
419
+ date.minute = parseInt(match[map.m], 10) % 60;
420
+ }
421
+
422
+ if (map.s) {
423
+ date.second = parseInt(match[map.s], 10) % 60;
424
+ }
425
+
426
+ if (map.S) {
427
+ date.millisecond = parseInt(match[map.S], 10) * 10 ** (3 - match[map.S].length);
428
+ }
429
+
430
+ if (map.Z || map.ZZ) {
431
+ tzString = map.Z ? match[map.Z].replace(':', '') : match[map.ZZ];
432
+ date.timezoneOffset = (tzString[0] === '+' ? -1 : 1) * (60 * +tzString.slice(1, 3) + +tzString.slice(3, 5));
433
+ }
434
+ }
435
+
436
+ date.dateHash = pad(date.year as number, 6) + '/' + pad(date.month as number) + '/' + pad(date.day as number);
437
+ date.timeHash =
438
+ pad(date.hour as number) + ':' + pad(date.minute as number) + ':' + pad(date.second as number) + tzString;
439
+
440
+ return date;
441
+ }
442
+
443
+ /**
444
+ * Check a date string for validity.
445
+ * @param date
446
+ */
447
+ export function isValid(date: any): boolean {
448
+ return typeof date === 'number' ? true : !isNaN(Date.parse(date));
449
+ }
450
+
451
+ /**
452
+ * The following method is just a wrapper to help you in cases where you just need current time but with a different year, or month, or second etc.
453
+ * @param modified
454
+ * @param utc
455
+ */
456
+ export function buildDate(modified: IRawDate, utc: boolean) {
457
+ return adjustDate(new Date(), modified, utc);
458
+ }
459
+
460
+ /**
461
+ * Get the day number in week for a given date.
462
+ * @param date
463
+ */
464
+ export function getDayOfWeek(date: Date): number {
465
+ const dow = new Date(date).getDay();
466
+ return dow === 0 ? 7 : dow;
467
+ }
468
+
469
+ /**
470
+ * Get the week number in year for a given date.
471
+ * @param date
472
+ */
473
+ export function getWeekOfYear(date: Date): number {
474
+ const thursday = new Date(date.getFullYear(), date.getMonth(), date.getDate());
475
+ thursday.setDate(thursday.getDate() - ((thursday.getDay() + 6) % 7) + 3);
476
+
477
+ const firstThursday = new Date(thursday.getFullYear(), 0, 4);
478
+ firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3);
479
+
480
+ const ds = thursday.getTimezoneOffset() - firstThursday.getTimezoneOffset();
481
+ thursday.setHours(thursday.getHours() - ds);
482
+
483
+ const weekDiff = (+thursday - +firstThursday) / (MILLISECONDS_IN_DAY * 7);
484
+ return 1 + Math.floor(weekDiff);
485
+ }
486
+
487
+ /**
488
+ * Get the day of the year from a given date.
489
+ * @param date
490
+ */
491
+ export function getDayOfYear(date: Date): number | undefined {
492
+ const dayOfYear = getDateDiff(date, startOfDate(date, 'year'), 'days');
493
+ return dayOfYear ? dayOfYear + 1 : undefined;
494
+ }
495
+
496
+ /**
497
+ * Check a date is in a given date/time range.
498
+ * @param date
499
+ * @param from
500
+ * @param to
501
+ * @param opts
502
+ */
503
+ export function isBetweenDates(
504
+ date: Date,
505
+ from: Date,
506
+ to: Date,
507
+ opts: { onlyDate: boolean; inclusiveFrom: boolean; inclusiveTo: boolean }
508
+ ): boolean {
509
+ const date1 = getDateIdentifier(from, opts.onlyDate);
510
+ const date2 = getDateIdentifier(to, opts.onlyDate);
511
+ const current = getDateIdentifier(date, opts.onlyDate);
512
+
513
+ return (
514
+ (current > date1 || (opts.inclusiveFrom && current === date1)) &&
515
+ (current < date2 || (opts.inclusiveTo && current === date2))
516
+ );
517
+ }
518
+
519
+ /**
520
+ * Add a given date/time unit from a given date.
521
+ * @param date
522
+ * @param modified
523
+ */
524
+ export function addToDate(date: Date, modified: IRawDate): Date {
525
+ return getChange(date, modified, 1);
526
+ }
527
+
528
+ /**
529
+ * Subtract a given date/time unit from a given date.
530
+ * @param date
531
+ * @param modified
532
+ */
533
+ export function subtractFromDate(date: Date, modified: IRawDate): Date {
534
+ return getChange(date, modified, -1);
535
+ }
536
+
537
+ /**
538
+ * Beginning of a given date/time unit for a given date.
539
+ * @param date
540
+ * @param unit
541
+ * @param utc
542
+ */
543
+ export function startOfDate(date: Date, unit: string, utc?: boolean): Date {
544
+ const _date = new Date(date);
545
+ const prefix = `set${utc ? 'UTC' : ''}`;
546
+
547
+ switch (unit) {
548
+ case 'year':
549
+ case 'years':
550
+ (_date as Date & Record<string, any>)[`${prefix}Month`](0);
551
+ // eslint-disable-next-line no-fallthrough
552
+ case 'month':
553
+ case 'months':
554
+ (_date as Date & Record<string, any>)[`${prefix}Date`](1);
555
+ // eslint-disable-next-line no-fallthrough
556
+ case 'day':
557
+ case 'days':
558
+ case 'date':
559
+ (_date as Date & Record<string, any>)[`${prefix}Hours`](0);
560
+ // eslint-disable-next-line no-fallthrough
561
+ case 'hour':
562
+ case 'hours':
563
+ (_date as Date & Record<string, any>)[`${prefix}Minutes`](0);
564
+ // eslint-disable-next-line no-fallthrough
565
+ case 'minute':
566
+ case 'minutes':
567
+ (_date as Date & Record<string, any>)[`${prefix}Seconds`](0);
568
+ // eslint-disable-next-line no-fallthrough
569
+ case 'second':
570
+ case 'seconds':
571
+ (_date as Date & Record<string, any>)[`${prefix}Milliseconds`](0);
572
+ }
573
+
574
+ return _date;
575
+ }
576
+
577
+ /**
578
+ * End of a given date/time unit for a given date.
579
+ * @param date
580
+ * @param unit
581
+ * @param utc
582
+ */
583
+ export function endOfDate(date: Date, unit: string, utc: boolean): Date {
584
+ const _date = new Date(date),
585
+ prefix = `set${utc ? 'UTC' : ''}`;
586
+
587
+ switch (unit) {
588
+ case 'year':
589
+ case 'years':
590
+ (_date as Date & Record<string, any>)[`${prefix}Month`](11);
591
+ // eslint-disable-next-line no-fallthrough
592
+ case 'month':
593
+ case 'months':
594
+ (_date as Date & Record<string, any>)[`${prefix}Date`](daysInMonth(_date));
595
+ // eslint-disable-next-line no-fallthrough
596
+ case 'day':
597
+ case 'days':
598
+ case 'date':
599
+ (_date as Date & Record<string, any>)[`${prefix}Hours`](23);
600
+ // eslint-disable-next-line no-fallthrough
601
+ case 'hour':
602
+ case 'hours':
603
+ (_date as Date & Record<string, any>)[`${prefix}Minutes`](59);
604
+ // eslint-disable-next-line no-fallthrough
605
+ case 'minute':
606
+ case 'minutes':
607
+ (_date as Date & Record<string, any>)[`${prefix}Seconds`](59);
608
+ // eslint-disable-next-line no-fallthrough
609
+ case 'second':
610
+ case 'seconds':
611
+ (_date as Date & Record<string, any>)[`${prefix}Milliseconds`](999);
612
+ }
613
+ return _date;
614
+ }
615
+
616
+ /**
617
+ * Get the maximum date from a given date and other dates.
618
+ * @param date
619
+ * @param args
620
+ */
621
+ export function getMaxDate(date: Date, ...args: Date[]): Date {
622
+ let _date = new Date(date) as unknown as number;
623
+ Array.prototype.slice.call(args, 1).forEach((date) => {
624
+ _date = Math.max(_date, new Date(date) as unknown as number);
625
+ });
626
+ return _date as unknown as Date;
627
+ }
628
+
629
+ /**
630
+ * Get the minimum date from a given date and other dates.
631
+ * @param date
632
+ * @param args
633
+ */
634
+ export function getMinDate(date: Date, ...args: Date[]): Date {
635
+ let _date = new Date(date) as unknown as number;
636
+ Array.prototype.slice.call(args, 1).forEach((date) => {
637
+ _date = Math.min(_date, new Date(date) as unknown as number);
638
+ });
639
+ return _date as unknown as Date;
640
+ }
641
+
642
+ /**
643
+ * Get the difference between two dates.
644
+ * @param date
645
+ * @param subtract
646
+ * @param unit
647
+ */
648
+ export function getDateDiff(date: Date, subtract: Date, unit = 'days'): number {
649
+ const date1 = new Date(date);
650
+ const date2 = new Date(subtract);
651
+
652
+ switch (unit) {
653
+ case 'years':
654
+ case 'year':
655
+ return date1.getFullYear() - date2.getFullYear();
656
+
657
+ case 'months':
658
+ case 'month':
659
+ return (date1.getFullYear() - date2.getFullYear()) * 12 + date1.getMonth() - date2.getMonth();
660
+
661
+ case 'days':
662
+ case 'day':
663
+ case 'date':
664
+ return getDiff(startOfDate(date1, 'day'), startOfDate(date2, 'day'), MILLISECONDS_IN_DAY);
665
+
666
+ case 'hours':
667
+ case 'hour':
668
+ return getDiff(startOfDate(date1, 'hour'), startOfDate(date2, 'hour'), MILLISECONDS_IN_HOUR);
669
+
670
+ case 'minutes':
671
+ case 'minute':
672
+ return getDiff(startOfDate(date1, 'minute'), startOfDate(date2, 'minute'), MILLISECONDS_IN_MINUTE);
673
+
674
+ case 'seconds':
675
+ case 'second':
676
+ return getDiff(startOfDate(date1, 'second'), startOfDate(date2, 'second'), 1000);
677
+
678
+ default:
679
+ return 0;
680
+ }
681
+ }
682
+
683
+ /**
684
+ * Get date format
685
+ * @param date
686
+ */
687
+ export function inferDateFormat(date: Date | any): 'date' | 'number' | 'string' {
688
+ return isDate(date) ? 'date' : typeof date === 'number' ? 'number' : 'string';
689
+ }
690
+
691
+ /**
692
+ * Normalize a date in a given date/time range.
693
+ * @param date
694
+ * @param min
695
+ * @param max
696
+ */
697
+ export function getDateBetween(date: Date, min: Date, max: Date): Date {
698
+ const t = new Date(date);
699
+
700
+ if (min) {
701
+ const low = new Date(min);
702
+ if (t < low) {
703
+ return low;
704
+ }
705
+ }
706
+
707
+ if (max) {
708
+ const high = new Date(max);
709
+ if (t > high) {
710
+ return high;
711
+ }
712
+ }
713
+
714
+ return t;
715
+ }
716
+
717
+ /**
718
+ * Check of two dates unit are equal.
719
+ * @param date1
720
+ * @param date2
721
+ * @param unit
722
+ */
723
+ export function isSameDate(date1: Date, date2: Date, unit: string): boolean {
724
+ const _date1 = new Date(date1);
725
+ const _date2 = new Date(date2);
726
+
727
+ if (unit === void 0) {
728
+ return _date1.getTime() === _date2.getTime();
729
+ }
730
+
731
+ switch (unit) {
732
+ case 'second':
733
+ case 'seconds':
734
+ if (_date1.getSeconds() !== _date2.getSeconds()) {
735
+ return false;
736
+ }
737
+ // eslint-disable-next-line no-fallthrough
738
+ case 'minute':
739
+ case 'minutes':
740
+ if (_date1.getMinutes() !== _date2.getMinutes()) {
741
+ return false;
742
+ }
743
+ // eslint-disable-next-line no-fallthrough
744
+ case 'hour':
745
+ case 'hours':
746
+ if (_date1.getHours() !== _date2.getHours()) {
747
+ return false;
748
+ }
749
+ // eslint-disable-next-line no-fallthrough
750
+ case 'day':
751
+ case 'days':
752
+ case 'date':
753
+ if (_date1.getDate() !== _date2.getDate()) {
754
+ return false;
755
+ }
756
+ // eslint-disable-next-line no-fallthrough
757
+ case 'month':
758
+ case 'months':
759
+ if (_date1.getMonth() !== _date2.getMonth()) {
760
+ return false;
761
+ }
762
+ // eslint-disable-next-line no-fallthrough
763
+ case 'year':
764
+ case 'years':
765
+ if (_date1.getFullYear() !== _date2.getFullYear()) {
766
+ return false;
767
+ }
768
+ break;
769
+ default:
770
+ throw new Error(`date isSameDate unknown unit ${unit}`);
771
+ }
772
+
773
+ return true;
774
+ }
775
+
776
+ /**
777
+ * Get the number of days in a month.
778
+ * @param date
779
+ */
780
+ export function daysInMonth(date: Date): number {
781
+ return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
782
+ }
783
+
784
+ /**
785
+ * Cloning date object.
786
+ * @param date
787
+ */
788
+ export function clone(date: Date): Date {
789
+ return isDate(date) ? new Date(date.getTime()) : date;
790
+ }
791
+
792
+ function getRegexData(mask: string, dateLocale: TDateLocale): { map: Record<string, any>; regex: RegExp } {
793
+ const days = '(' + dateLocale.days.join('|') + ')';
794
+ const key = mask + days;
795
+
796
+ if (regexStore[key]) return regexStore[key];
797
+
798
+ const daysShort = '(' + dateLocale.daysShort.join('|') + ')';
799
+ const months = '(' + dateLocale.months.join('|') + ')';
800
+ const monthsShort = '(' + dateLocale.monthsShort.join('|') + ')';
801
+
802
+ const map: Record<string, any> = {};
803
+ let index = 0;
804
+
805
+ const regexText = mask.replace(reverseToken, (match) => {
806
+ index++;
807
+ switch (match) {
808
+ case 'YY':
809
+ map.YY = index;
810
+ return '(-?\\d{1,2})';
811
+ case 'YYYY':
812
+ map.YYYY = index;
813
+ return '(-?\\d{1,4})';
814
+ case 'M':
815
+ map.M = index;
816
+ return '(\\d{1,2})';
817
+ case 'MM':
818
+ map.M = index; // bumping to M
819
+ return '(\\d{2})';
820
+ case 'MMM':
821
+ map.MMM = index;
822
+ return monthsShort;
823
+ case 'MMMM':
824
+ map.MMMM = index;
825
+ return months;
826
+ case 'D':
827
+ map.D = index;
828
+ return '(\\d{1,2})';
829
+ case 'Do':
830
+ map.D = index++; // bumping to D
831
+ return '(\\d{1,2}(st|nd|rd|th))';
832
+ case 'DD':
833
+ map.D = index; // bumping to D
834
+ return '(\\d{2})';
835
+ case 'H':
836
+ map.H = index;
837
+ return '(\\d{1,2})';
838
+ case 'HH':
839
+ map.H = index; // bumping to H
840
+ return '(\\d{2})';
841
+ case 'h':
842
+ map.h = index;
843
+ return '(\\d{1,2})';
844
+ case 'hh':
845
+ map.h = index; // bumping to h
846
+ return '(\\d{2})';
847
+ case 'm':
848
+ map.m = index;
849
+ return '(\\d{1,2})';
850
+ case 'mm':
851
+ map.m = index; // bumping to m
852
+ return '(\\d{2})';
853
+ case 's':
854
+ map.s = index;
855
+ return '(\\d{1,2})';
856
+ case 'ss':
857
+ map.s = index; // bumping to s
858
+ return '(\\d{2})';
859
+ case 'S':
860
+ map.S = index;
861
+ return '(\\d{1})';
862
+ case 'SS':
863
+ map.S = index; // bump to S
864
+ return '(\\d{2})';
865
+ case 'SSS':
866
+ map.S = index; // bump to S
867
+ return '(\\d{3})';
868
+ case 'A':
869
+ map.A = index;
870
+ return '(AM|PM)';
871
+ case 'a':
872
+ map.a = index;
873
+ return '(am|pm)';
874
+ case 'aa':
875
+ map.aa = index;
876
+ return '(a\\.m\\.|p\\.m\\.)';
877
+
878
+ case 'ddd':
879
+ return daysShort;
880
+ case 'dddd':
881
+ return days;
882
+ case 'Q':
883
+ case 'd':
884
+ case 'E':
885
+ return '(\\d{1})';
886
+ case 'Qo':
887
+ return '(1st|2nd|3rd|4th)';
888
+ case 'DDD':
889
+ case 'DDDD':
890
+ return '(\\d{1,3})';
891
+ case 'w':
892
+ return '(\\d{1,2})';
893
+ case 'ww':
894
+ return '(\\d{2})';
895
+
896
+ case 'Z': // to split: (?:(Z)()()|([+-])?(\\d{2}):?(\\d{2}))
897
+ map.Z = index;
898
+ return '(Z|[+-]\\d{2}:\\d{2})';
899
+ case 'ZZ':
900
+ map.ZZ = index;
901
+ return '(Z|[+-]\\d{2}\\d{2})';
902
+
903
+ case 'X':
904
+ map.X = index;
905
+ return '(-?\\d+)';
906
+ case 'x':
907
+ map.x = index;
908
+ return '(-?\\d{4,})';
909
+
910
+ default:
911
+ index--;
912
+ if (match[0] === '[') {
913
+ match = match.substring(1, match.length - 1);
914
+ }
915
+ return match.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
916
+ }
917
+ });
918
+
919
+ const result = { map, regex: new RegExp('^' + regexText) };
920
+ regexStore[key] = result;
921
+
922
+ return result;
923
+ }
924
+
925
+ function getDateLocale(paramDateLocale: TDateLocale, locale: ILocale): TDateLocale {
926
+ return paramDateLocale ? paramDateLocale : locale ? locale.date : defaultLang.date;
927
+ }
928
+
929
+ function formatTimezone(offset: number, delimiter: string = ''): string {
930
+ const sign = offset > 0 ? '-' : '+';
931
+ const absOffset = Math.abs(offset);
932
+ const hours = Math.floor(absOffset / 60);
933
+ const minutes = absOffset % 60;
934
+
935
+ return sign + pad(hours) + delimiter + pad(minutes);
936
+ }
937
+
938
+ function applyYearMonthDayChange(date: Date, modified: IRawDate, sign: number): Date {
939
+ let year = date.getFullYear();
940
+ let month = date.getMonth();
941
+ const day = date.getMonth();
942
+
943
+ if (modified.year) {
944
+ year += sign * modified.year;
945
+ delete modified.year;
946
+ }
947
+
948
+ if (modified.month) {
949
+ month += sign * modified.month;
950
+ delete modified.month;
951
+ }
952
+
953
+ date.setDate(1);
954
+ date.setMonth(2);
955
+ date.setFullYear(year);
956
+ date.setMonth(month);
957
+ date.setDate(Math.min(day, daysInMonth(date)));
958
+
959
+ if (modified.date) {
960
+ date.setDate(date.getDate() + sign * modified.date);
961
+ delete modified.date;
962
+ }
963
+
964
+ return date;
965
+ }
966
+
967
+ function applyYearMonthDay(date: Date, modified: IRawDate, middle: string): Date {
968
+ const year = modified.year ? modified.year : (date as any)[`get${middle}FullYear`]();
969
+ const month = modified.month ? modified.month - 1 : (date as any)[`get${middle}Month`]();
970
+ const maxDay = new Date(year, month + 1, 0).getDate();
971
+ const day = Math.min(maxDay, modified.date ? modified.date : (date as any)[`get${middle}Date`]());
972
+
973
+ (date as Date & Record<string, any>)[`set${middle}Date`](1);
974
+ (date as Date & Record<string, any>)[`set${middle}Month`](2);
975
+ (date as Date & Record<string, any>)[`set${middle}FullYear`](year);
976
+ (date as Date & Record<string, any>)[`set${middle}Month`](month);
977
+ (date as Date & Record<string, any>)[`set${middle}Date`](day);
978
+
979
+ delete modified.year;
980
+ delete modified.month;
981
+ delete modified.date;
982
+
983
+ return date;
984
+ }
985
+
986
+ function getChange(date: Date, rawModified: IRawDate & Record<string, any>, sign: number): Date {
987
+ const modified = normalizeModified(rawModified);
988
+ const _date = new Date(date);
989
+ const changedDate =
990
+ modified.year || modified.month || modified.date ? applyYearMonthDayChange(date, modified, sign) : _date;
991
+
992
+ for (const key in modified) {
993
+ const op = key.charAt(0).toUpperCase() + key.slice(1);
994
+ (changedDate as Date & Record<string, any>)[`set${op}`]((changedDate as Date & Record<string, any>)[`get${op}`])() +
995
+ sign * modified[key];
996
+ }
997
+
998
+ return changedDate;
999
+ }
1000
+
1001
+ function normalizeModified(modified: IRawDate & Record<string, any>) {
1002
+ const acc = { ...modified };
1003
+
1004
+ if (modified.years) {
1005
+ acc.year = modified.years;
1006
+ delete acc.years;
1007
+ }
1008
+
1009
+ if (modified.months) {
1010
+ acc.month = modified.months;
1011
+ delete acc.months;
1012
+ }
1013
+
1014
+ if (modified.days) {
1015
+ acc.date = modified.days;
1016
+ delete acc.days;
1017
+ }
1018
+ if (modified.day) {
1019
+ acc.date = modified.day;
1020
+ delete acc.day;
1021
+ }
1022
+
1023
+ if (modified.hour) {
1024
+ acc.hours = modified.hour;
1025
+ delete acc.hour;
1026
+ }
1027
+
1028
+ if (modified.minute) {
1029
+ acc.minutes = modified.minute;
1030
+ delete acc.minute;
1031
+ }
1032
+
1033
+ if (modified.second) {
1034
+ acc.seconds = modified.second;
1035
+ delete acc.second;
1036
+ }
1037
+
1038
+ if (modified.millisecond) {
1039
+ acc.milliseconds = modified.millisecond;
1040
+ delete acc.millisecond;
1041
+ }
1042
+
1043
+ return acc;
1044
+ }
1045
+
1046
+ function getDayIdentifier(date: Date): number {
1047
+ return date.getFullYear() * 10000 + date.getMonth() * 100 + date.getDate();
1048
+ }
1049
+
1050
+ function getDateIdentifier(date: Date, onlyDate?: boolean): number {
1051
+ const _date = new Date(date);
1052
+ return onlyDate ? getDayIdentifier(_date) : _date.getTime();
1053
+ }
1054
+
1055
+ function getDiff(date1: Date, date2: Date, interval: number): number {
1056
+ return (
1057
+ (date1.getTime() -
1058
+ date1.getTimezoneOffset() * MILLISECONDS_IN_MINUTE -
1059
+ (date2.getTime() - date2.getTimezoneOffset() * MILLISECONDS_IN_MINUTE)) /
1060
+ interval
1061
+ );
1062
+ }
1063
+
1064
+ function getOrdinal(n: number) {
1065
+ if (n >= 11 && n <= 13) {
1066
+ return `${n}th`;
1067
+ }
1068
+ switch (n % 10) {
1069
+ case 1:
1070
+ return `${n}st`;
1071
+ case 2:
1072
+ return `${n}nd`;
1073
+ case 3:
1074
+ return `${n}rd`;
1075
+ }
1076
+ return `${n}th`;
1077
+ }
1078
+
1079
+ export default {
1080
+ formatDate,
1081
+ clone,
1082
+ isValid,
1083
+ buildDate,
1084
+ getDayOfWeek,
1085
+ getWeekOfYear,
1086
+ isBetweenDates,
1087
+ addToDate,
1088
+ subtractFromDate,
1089
+ adjustDate,
1090
+ startOfDate,
1091
+ endOfDate,
1092
+ getMaxDate,
1093
+ getMinDate,
1094
+ getDateDiff,
1095
+ getDayOfYear,
1096
+ inferDateFormat,
1097
+ getDateBetween,
1098
+ isSameDate,
1099
+ daysInMonth
1100
+ };