• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

atinc / ngx-tethys / 269bc1f1-a472-43fb-93e2-518e47073c10

24 Mar 2025 09:10AM UTC coverage: 90.187% (-0.04%) from 90.223%
269bc1f1-a472-43fb-93e2-518e47073c10

Pull #3313

circleci

xinglu01
feat(date-picker): support date-fns i18n and support thySeparator
Pull Request #3313: feat(date-picker): support date-fns i18n and support thySeparator

5586 of 6858 branches covered (81.45%)

Branch coverage included in aggregate %.

22 of 28 new or added lines in 7 files covered. (78.57%)

37 existing lines in 6 files now uncovered.

13347 of 14135 relevant lines covered (94.43%)

992.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.47
/src/util/date/tiny-date.ts
1
import { TZDate } from '@date-fns/tz';
2
import { FirstWeekContainsDate, Locale, setHours, setMinutes, setSeconds } from 'date-fns';
3
import { ThyLocaleType } from 'ngx-tethys/i18n';
4
import { SafeAny } from 'ngx-tethys/types';
5
import { isString } from '../helpers';
6
import {
7
    addDays,
15✔
8
    addHours,
14✔
9
    addMinutes,
14✔
10
    addMonths,
11
    addQuarters,
1✔
12
    addSeconds,
13
    addWeeks,
1✔
14
    addYears,
15
    differenceInCalendarDays,
1✔
16
    differenceInCalendarMonths,
1✔
17
    differenceInCalendarQuarters,
18
    differenceInCalendarYears,
43,667✔
19
    differenceInDays,
43,667✔
20
    differenceInHours,
40,986✔
21
    differenceInMinutes,
40,576✔
22
    differenceInSeconds,
23
    differenceInWeeks,
410✔
24
    endOfDay,
409✔
25
    endOfISOWeek,
26
    endOfMonth,
1!
27
    endOfQuarter,
1✔
28
    endOfWeek,
29
    endOfYear,
30
    format,
31
    fromUnixTime,
2,681✔
32
    getDateFnsLocale,
33
    getDaysInMonth,
34
    getQuarter,
35
    getUnixTime,
55✔
36
    getWeek,
55✔
37
    isSameDay,
38
    isSameHour,
UNCOV
39
    isSameMinute,
×
40
    isSameMonth,
41
    isSameQuarter,
42
    isSameSecond,
1!
43
    isSameYear,
44
    isToday,
UNCOV
45
    isTomorrow,
×
46
    isValid,
47
    isWeekend,
48
    setDay,
40,576!
49
    setDefaultOptions,
50
    setMonth,
51
    setQuarter,
41✔
52
    setYear,
53
    startOfDay,
54
    startOfISOWeek,
172✔
55
    startOfMonth,
56
    startOfQuarter,
57
    startOfWeek,
58
    startOfYear,
1,231✔
59
    subDays,
60
    subWeeks
61
} from './functions';
22,191✔
62

63
export type TinyDateCompareGrain = 'decade' | 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second';
64

7,085✔
65
export type WeekDayIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6;
66

67
export type TinyDateType = TinyDate | Date | null;
320✔
68

69
export function sortRangeValue(rangeValue: TinyDate[]): TinyDate[] {
70
    if (Array.isArray(rangeValue)) {
755✔
71
        const [start, end] = rangeValue;
72
        return start && end && start.isAfterSecond(end) ? [end, start] : [start, end];
73
    }
33✔
74
    return rangeValue;
75
}
2✔
76

2✔
77
export const DEFAULT_TIMEZONE = 'Asia/Shanghai';
78

79
export class TinyDate implements Record<string, any> {
3✔
80
    nativeDate: Date;
81

82
    private useTimeZone: string;
66✔
83

84
    protected static defaultLocale: Locale = getDateFnsLocale(ThyLocaleType.zhHans);
85

62✔
86
    protected static defaultTimeZone: string = DEFAULT_TIMEZONE;
87

88
    constructor(date?: Date | string | number, zone?: string) {
62✔
89
        this.useTimeZone = zone || TinyDate.defaultTimeZone;
90
        if (date) {
91
            if (date instanceof Date) {
1✔
92
                this.nativeDate = TinyDate.utcToZonedTime(date, this.useTimeZone);
93
            } else if (typeof date === 'string' || typeof date === 'number') {
94
                this.nativeDate = new TZDate(date as SafeAny, this.useTimeZone);
1✔
95
            } else if (typeof ngDevMode === 'undefined' || ngDevMode) {
96
                throw new Error(
97
                    `The input date type is not supported expect Date | string | number | { date: number; with_time: 0 | 1}, actual ${JSON.stringify(
1✔
98
                        date
99
                    )}`
100
                );
101
            }
1✔
102
        } else {
1✔
103
            this.nativeDate = new TZDate(Date.now(), this.useTimeZone);
1✔
104
        }
105
    }
106

102✔
107
    static setDefaultLocale(locale: string) {
102✔
108
        TinyDate.defaultLocale = getDateFnsLocale(locale);
102✔
109
        return setDefaultOptions({ locale: TinyDate.defaultLocale });
110
    }
111

660✔
112
    static getDefaultLocale() {
113
        return TinyDate.defaultLocale;
114
    }
1,256✔
115

116
    static setDefaultTimeZone(zone: string) {
117
        TinyDate.defaultTimeZone = zone ?? DEFAULT_TIMEZONE;
172✔
118
    }
119

120
    static getDefaultTimeZone() {
1✔
121
        return TinyDate.defaultTimeZone;
122
    }
123

32✔
124
    static utcToZonedTime(value: Date | number, timeZone?: string): Date {
125
        return TZDate.tz(timeZone || TinyDate.defaultTimeZone, value as any);
126
    }
32✔
127

128
    static createDateInTimeZone(
129
        year: number,
32✔
130
        month: number,
131
        day: number,
132
        hours: number,
133
        minutes: number,
398✔
134
        seconds: number,
135
        timeZone?: string
136
    ): Date {
450✔
137
        return new TZDate(year, month, day, hours, minutes, seconds, timeZone || TinyDate.defaultTimeZone);
138
    }
139

644✔
140
    static fromUnixTime(unixTime: number, timeZone?: string): TinyDate {
141
        return new TinyDate(fromUnixTime(unixTime), timeZone || TinyDate.defaultTimeZone);
142
    }
1✔
143

144
    // get
145
    getTime(): number {
28,767✔
146
        return this.nativeDate.getTime();
147
    }
148

1✔
149
    getDate(): number {
150
        return this.nativeDate.getDate();
151
    }
2✔
152

153
    getYear(): number {
154
        return this.nativeDate.getFullYear();
1✔
155
    }
156

157
    getQuarter(): number {
×
158
        return getQuarter(this.nativeDate);
159
    }
54,809✔
160

161
    getMonth(): number {
2✔
162
        return this.nativeDate.getMonth();
2✔
163
    }
164

799✔
165
    getFullYear(): number {
799✔
166
        return this.nativeDate.getFullYear();
167
    }
1,421✔
168

1,421✔
169
    getWeek(options: { locale?: Locale; weekStartsOn?: WeekDayIndex } = { weekStartsOn: 1 }): number {
170
        return getWeek(this.nativeDate, options);
441✔
171
    }
441✔
172

173
    getDay(): number {
52,139✔
174
        return this.nativeDate.getDay();
52,139✔
175
    }
176

2✔
177
    getHours(): number {
2✔
178
        return this.nativeDate.getHours();
179
    }
2✔
180

2✔
181
    getMinutes(): number {
182
        return this.nativeDate.getMinutes();
2✔
183
    }
2✔
184

185
    getSeconds(): number {
1✔
186
        return this.nativeDate.getSeconds();
1✔
187
    }
188

54,809✔
189
    getMilliseconds(): number {
190
        return this.nativeDate.getMilliseconds();
191
    }
787✔
192

193
    getDaysInMonth() {
194
        return getDaysInMonth(this.nativeDate);
1,363✔
195
    }
196

197
    getDaysInQuarter() {
441✔
198
        return differenceInCalendarDays(this.endOfQuarter().addSeconds(1).nativeDate, this.startOfQuarter().nativeDate);
199
    }
200

52,138✔
201
    // set
202
    setDate(amount: number): TinyDate {
203
        const date = new Date(this.nativeDate);
1✔
204
        date.setDate(amount);
205
        return new TinyDate(date, this.useTimeZone);
206
    }
1✔
207

208
    setHms(hour: number, minute: number, second: number): TinyDate {
209
        const date = new Date(this.nativeDate);
1✔
210
        date.setHours(hour, minute, second);
211
        return new TinyDate(date, this.useTimeZone);
212
    }
213

127✔
214
    setYear(year: number): TinyDate {
215
        return new TinyDate(setYear(this.nativeDate, year), this.useTimeZone);
216
    }
64✔
217

218
    setMonth(month: number): TinyDate {
219
        return new TinyDate(setMonth(this.nativeDate, month), this.useTimeZone);
22,111✔
220
    }
221

222
    setQuarter(quarter: number): TinyDate {
1✔
223
        return new TinyDate(setQuarter(this.nativeDate, quarter), this.useTimeZone);
224
    }
225

7,067✔
226
    setDay(day: number, options?: { weekStartsOn: WeekDayIndex }): TinyDate {
227
        return new TinyDate(setDay(this.nativeDate, day, options), this.useTimeZone);
228
    }
1✔
229

230
    setHours(hours: number): TinyDate {
231
        return new TinyDate(setHours(this.nativeDate, hours), this.useTimeZone);
1✔
232
    }
233

234
    setMinutes(minutes: number): TinyDate {
1✔
235
        return new TinyDate(setMinutes(this.nativeDate, minutes), this.useTimeZone);
236
    }
237

2✔
238
    setSeconds(seconds: number): TinyDate {
239
        return new TinyDate(setSeconds(this.nativeDate, seconds), this.useTimeZone);
UNCOV
240
    }
×
241

242
    // add
243
    addYears(amount: number): TinyDate {
21,967✔
244
        return new TinyDate(addYears(this.nativeDate, amount), this.useTimeZone);
245
    }
246

1✔
247
    addQuarters(amount: number): TinyDate {
248
        return new TinyDate(addQuarters(this.nativeDate, amount), this.useTimeZone);
249
    }
2✔
250

251
    addMonths(amount: number): TinyDate {
252
        return new TinyDate(addMonths(this.nativeDate, amount), this.useTimeZone);
1✔
253
    }
254

255
    addWeeks(amount: number): TinyDate {
1✔
256
        return new TinyDate(addWeeks(this.nativeDate, amount), this.useTimeZone);
257
    }
258

15✔
259
    addDays(amount: number): TinyDate {
260
        return new TinyDate(addDays(this.nativeDate, amount), this.useTimeZone);
261
    }
262
    addHours(amount: number): TinyDate {
2✔
263
        return new TinyDate(addHours(this.nativeDate, amount), this.useTimeZone);
264
    }
265

43,934✔
266
    addSeconds(amount: number): TinyDate {
267
        return new TinyDate(addSeconds(this.nativeDate, amount), this.useTimeZone);
268
    }
2✔
269

270
    addMinutes(amount: number): TinyDate {
271
        return new TinyDate(addMinutes(this.nativeDate, amount), this.useTimeZone);
2✔
272
    }
273

274
    // isSame
275

379✔
276
    isSame(date: TinyDateType, grain: TinyDateCompareGrain = 'day'): boolean {
277
        let fn;
278
        switch (grain) {
206✔
279
            case 'decade':
280
                fn = (pre: Date, next: Date) => Math.abs(pre.getFullYear() - next.getFullYear()) < 11;
281
                break;
236✔
282
            case 'year':
283
                fn = isSameYear;
284
                break;
32✔
285
            case 'month':
286
                fn = isSameMonth;
287
                break;
2,376✔
288
            case 'quarter':
289
                fn = isSameQuarter;
290
                break;
113✔
291
            case 'day':
292
                fn = isSameDay;
293
                break;
151✔
294
            case 'hour':
295
                fn = isSameHour;
296
                break;
180✔
297
            case 'minute':
298
                fn = isSameMinute;
299
                break;
33✔
300
            case 'second':
301
                fn = isSameSecond;
302
                break;
1,814✔
303
            default:
304
                fn = isSameDay;
305
                break;
306
        }
255!
307
        return fn(this.nativeDate, this.toNativeDate(date));
255✔
308
    }
309

310
    isSameYear(date: TinyDateType): boolean {
1,047✔
311
        return this.isSame(date, 'year');
312
    }
313

147✔
314
    isSameMonth(date: TinyDateType): boolean {
315
        return this.isSame(date, 'month');
316
    }
213✔
317

318
    isSameQuarter(date: TinyDateType): boolean {
29,373!
319
        return this.isSame(date, 'quarter');
51,362✔
320
    }
2✔
321

322
    isSameDay(date: TinyDateType): boolean {
323
        return this.isSame(date, 'day');
51,360!
324
    }
325

127✔
326
    isSameHour(date: TinyDateType): boolean {
127✔
327
        return this.isSame(date, 'hour');
328
    }
64✔
329

64✔
330
    isSameMinute(date: TinyDateType): boolean {
331
        return this.isSame(date, 'minute');
44,078✔
332
    }
44,078✔
333

334
    isSameSecond(date: TinyDateType): boolean {
7,069✔
335
        return this.isSame(date, 'second');
7,069✔
336
    }
337

2✔
338
    // isBefore and isAfter
2✔
339
    isBeforeYear(date: TinyDateType): boolean {
340
        return this.compare(date, 'year');
2✔
341
    }
2✔
342

343
    isBeforeQuarter(date: TinyDate): boolean {
2✔
344
        return this.compare(date, 'quarter');
2✔
345
    }
346

16✔
347
    isBeforeMonth(date: TinyDateType): boolean {
16✔
348
        return this.compare(date, 'month');
UNCOV
349
    }
×
UNCOV
350

×
351
    isBeforeWeek(date: TinyDateType): boolean {
352
        return this.compare(date, 'week');
51,360✔
353
    }
354

355
    isBeforeDay(date: TinyDateType): boolean {
106,169✔
356
        return this.compare(date, 'day');
357
    }
UNCOV
358

×
359
    isBeforeHour(date: TinyDateType): boolean {
360
        return this.compare(date, 'hour');
UNCOV
361
    }
×
362

363
    isBeforeMinute(date: TinyDateType): boolean {
UNCOV
364
        return this.compare(date, 'minute');
×
365
    }
366

UNCOV
367
    isBeforeSecond(date: TinyDateType): boolean {
×
368
        return this.compare(date, 'second');
369
    }
UNCOV
370

×
371
    isAfterYear(date: TinyDateType): boolean {
372
        return this.compare(date, 'year', false);
373
    }
6✔
374

375
    isAfterQuarter(date: TinyDate): boolean {
376
        return this.compare(date, 'quarter', false);
377
    }
378

379
    isAfterMonth(date: TinyDateType): boolean {
380
        return this.compare(date, 'month', false);
381
    }
382

383
    isAfterWeek(date: TinyDateType): boolean {
384
        return this.compare(date, 'week', false);
385
    }
386

387
    isAfterDay(date: TinyDateType): boolean {
388
        return this.compare(date, 'day', false);
389
    }
390

391
    isAfterHour(date: TinyDateType): boolean {
392
        return this.compare(date, 'hour', false);
393
    }
394

395
    isAfterMinute(date: TinyDateType): boolean {
396
        return this.compare(date, 'minute', false);
397
    }
398

399
    isAfterSecond(date: TinyDateType): boolean {
400
        return this.compare(date, 'second', false);
401
    }
402

403
    // is
404
    isWeekend(): boolean {
405
        return isWeekend(this.nativeDate);
406
    }
407

408
    isToday(): boolean {
409
        return isToday(this.nativeDate);
410
    }
411

412
    isTomorrow(): boolean {
413
        return isTomorrow(this.nativeDate);
414
    }
415

416
    isValid(): boolean {
417
        return isValid(this.nativeDate);
418
    }
419

420
    // startOf and endOf
421
    startOfYear(): TinyDate {
422
        return new TinyDate(startOfYear(this.nativeDate), this.useTimeZone);
423
    }
424

425
    startOfQuarter(): TinyDate {
426
        return new TinyDate(startOfQuarter(this.nativeDate), this.useTimeZone);
427
    }
428

429
    startOfMonth(): TinyDate {
430
        return new TinyDate(startOfMonth(this.nativeDate), this.useTimeZone);
431
    }
432

433
    startOfWeek(options?: { locale?: Locale; weekStartsOn?: WeekDayIndex }): TinyDate {
434
        return new TinyDate(startOfWeek(this.nativeDate, options), this.useTimeZone);
435
    }
436

437
    startOfDay(): TinyDate {
438
        return new TinyDate(startOfDay(this.nativeDate), this.useTimeZone);
439
    }
440

441
    endOfYear(): TinyDate {
442
        return new TinyDate(endOfYear(this.nativeDate), this.useTimeZone);
443
    }
444

445
    endOfQuarter(): TinyDate {
446
        return new TinyDate(endOfQuarter(this.nativeDate), this.useTimeZone);
447
    }
448

449
    endOfMonth(): TinyDate {
450
        return new TinyDate(endOfMonth(this.nativeDate), this.useTimeZone);
451
    }
452

453
    endOfWeek(options?: { locale?: Locale; weekStartsOn?: WeekDayIndex }): TinyDate {
454
        return new TinyDate(endOfWeek(this.nativeDate, options), this.useTimeZone);
455
    }
456

457
    endOfDay(): TinyDate {
458
        return new TinyDate(endOfDay(this.nativeDate), this.useTimeZone);
459
    }
460

461
    // other
462
    format(
463
        mat: string,
464
        options?: {
465
            locale?: Locale | string;
466
            weekStartsOn?: WeekDayIndex;
467
            firstWeekContainsDate?: FirstWeekContainsDate;
468
            useAdditionalWeekYearTokens?: boolean;
469
            useAdditionalDayOfYearTokens?: boolean;
470
        }
471
    ) {
472
        const locale = options?.locale && isString(options?.locale) ? getDateFnsLocale(options.locale) : (options?.locale as Locale);
473
        return format(this.nativeDate, mat, { ...options, locale: locale });
474
    }
475

476
    calendarStart(options?: { weekStartsOn: WeekDayIndex | undefined }): TinyDate {
477
        return new TinyDate(startOfWeek(startOfMonth(this.nativeDate), options), this.useTimeZone);
478
    }
479

480
    clone(): TinyDate {
481
        return new TinyDate(new Date(this.nativeDate), this.useTimeZone);
482
    }
483

484
    getUnixTime(): number {
485
        return getUnixTime(this.nativeDate);
486
    }
487

488
    compare(date: TinyDateType, grain: TinyDateCompareGrain = 'day', isBefore: boolean = true): boolean {
489
        if (date === null) {
490
            return false;
491
        }
492
        let fn;
493
        switch (grain) {
494
            case 'year':
495
                fn = differenceInCalendarYears;
496
                break;
497
            case 'quarter':
498
                fn = differenceInCalendarQuarters;
499
                break;
500
            case 'month':
501
                fn = differenceInCalendarMonths;
502
                break;
503
            case 'day':
504
                fn = differenceInCalendarDays;
505
                break;
506
            case 'week':
507
                fn = differenceInWeeks;
508
                break;
509
            case 'hour':
510
                fn = differenceInHours;
511
                break;
512
            case 'minute':
513
                fn = differenceInMinutes;
514
                break;
515
            case 'second':
516
                fn = differenceInSeconds;
517
                break;
518
            default:
519
                fn = differenceInCalendarDays;
520
                break;
521
        }
522
        return isBefore ? fn(this.nativeDate, this.toNativeDate(date)) < 0 : fn(this.nativeDate, this.toNativeDate(date)) > 0;
523
    }
524

525
    private toNativeDate(date: any): Date {
526
        return date instanceof TinyDate ? date.nativeDate : date;
527
    }
528

529
    startOfISOWeek(): TinyDate {
530
        return new TinyDate(startOfISOWeek(this.nativeDate), this.useTimeZone);
531
    }
532

533
    endOfISOWeek(): TinyDate {
534
        return new TinyDate(endOfISOWeek(this.nativeDate), this.useTimeZone);
535
    }
536

537
    differenceInDays(date: Date): number {
538
        return differenceInDays(this.nativeDate, date);
539
    }
540

541
    differenceInHours(date: Date): number {
542
        return differenceInHours(this.nativeDate, date);
543
    }
544

545
    subWeeks(amount: number): TinyDate {
546
        return new TinyDate(subWeeks(this.nativeDate, amount), this.useTimeZone);
547
    }
548

549
    subDays(amount: number): TinyDate {
550
        return new TinyDate(subDays(this.nativeDate, amount), this.useTimeZone);
551
    }
552
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc