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

atinc / ngx-tethys / 0b9df710-60cf-468d-b3fe-08c55363086b

25 Mar 2025 08:15AM UTC coverage: 90.183% (-0.04%) from 90.223%
0b9df710-60cf-468d-b3fe-08c55363086b

push

circleci

web-flow
feat(date-picker): support date-fns i18n and support thySeparator (#3313)

5588 of 6859 branches covered (81.47%)

Branch coverage included in aggregate %.

25 of 32 new or added lines in 7 files covered. (78.13%)

37 existing lines in 7 files now uncovered.

13346 of 14136 relevant lines covered (94.41%)

992.1 hits per line

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

92.27
/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 {
6
    addDays,
15✔
7
    addHours,
14✔
8
    addMinutes,
14✔
9
    addMonths,
10
    addQuarters,
1✔
11
    addSeconds,
12
    addWeeks,
1✔
13
    addYears,
14
    differenceInCalendarDays,
1✔
15
    differenceInCalendarMonths,
1✔
16
    differenceInCalendarQuarters,
17
    differenceInCalendarYears,
43,648✔
18
    differenceInDays,
43,648✔
19
    differenceInHours,
40,967✔
20
    differenceInMinutes,
40,557✔
21
    differenceInSeconds,
22
    differenceInWeeks,
410✔
23
    endOfDay,
409✔
24
    endOfISOWeek,
25
    endOfMonth,
1!
26
    endOfQuarter,
1✔
27
    endOfWeek,
28
    endOfYear,
29
    format,
30
    fromUnixTime,
2,681✔
31
    getDateFnsLocale,
32
    getDaysInMonth,
33
    getQuarter,
34
    getUnixTime,
55✔
35
    getWeek,
55✔
36
    isSameDay,
55✔
37
    isSameHour,
38
    isSameMinute,
UNCOV
39
    isSameMonth,
×
40
    isSameQuarter,
41
    isSameSecond,
42
    isSameYear,
1!
43
    isToday,
44
    isTomorrow,
UNCOV
45
    isValid,
×
46
    isWeekend,
47
    setDay,
48
    setDefaultOptions,
40,557!
49
    setMonth,
50
    setQuarter,
51
    setYear,
41✔
52
    startOfDay,
53
    startOfISOWeek,
54
    startOfMonth,
172✔
55
    startOfQuarter,
56
    startOfWeek,
57
    startOfYear,
58
    subDays,
1,231✔
59
    subWeeks
60
} from './functions';
61

22,191✔
62
export type TinyDateCompareGrain = 'decade' | 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second';
63

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

66
export type TinyDateType = TinyDate | Date | null;
67

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

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

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

81
    private useTimeZone: string;
82

66✔
83
    private static locale: string;
84

85
    protected static dateFnsLocale: Locale = getDateFnsLocale(ThyLocaleType.zhHans);
62✔
86

87
    protected static defaultTimeZone: string = DEFAULT_TIMEZONE;
88

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

102✔
108
    static setDefaultLocale(locale: string) {
102✔
109
        TinyDate.locale = locale;
110
        TinyDate.dateFnsLocale = getDateFnsLocale(locale);
111
        return setDefaultOptions({ locale: TinyDate.dateFnsLocale });
660✔
112
    }
113

114
    static getDefaultLocale(): { locale: string; dateFnsLocale: Locale } {
1,256✔
115
        return { locale: TinyDate.locale, dateFnsLocale: TinyDate.dateFnsLocale };
116
    }
117

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

122
    static getDefaultTimeZone(): string {
123
        return TinyDate.defaultTimeZone;
32✔
124
    }
125

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

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

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

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

151
    getDate(): number {
2✔
152
        return this.nativeDate.getDate();
153
    }
154

1✔
155
    getYear(): number {
156
        return this.nativeDate.getFullYear();
157
    }
×
158

159
    getQuarter(): number {
54,809✔
160
        return getQuarter(this.nativeDate);
161
    }
2✔
162

2✔
163
    getMonth(): number {
164
        return this.nativeDate.getMonth();
799✔
165
    }
799✔
166

167
    getFullYear(): number {
1,421✔
168
        return this.nativeDate.getFullYear();
1,421✔
169
    }
170

441✔
171
    getWeek(options: { locale?: Locale; weekStartsOn?: WeekDayIndex } = { weekStartsOn: 1 }): number {
441✔
172
        return getWeek(this.nativeDate, options);
173
    }
52,139✔
174

52,139✔
175
    getDay(): number {
176
        return this.nativeDate.getDay();
2✔
177
    }
2✔
178

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

2✔
183
    getMinutes(): number {
2✔
184
        return this.nativeDate.getMinutes();
185
    }
1✔
186

1✔
187
    getSeconds(): number {
188
        return this.nativeDate.getSeconds();
54,809✔
189
    }
190

191
    getMilliseconds(): number {
787✔
192
        return this.nativeDate.getMilliseconds();
193
    }
194

1,363✔
195
    getDaysInMonth() {
196
        return getDaysInMonth(this.nativeDate);
197
    }
441✔
198

199
    getDaysInQuarter() {
200
        return differenceInCalendarDays(this.endOfQuarter().addSeconds(1).nativeDate, this.startOfQuarter().nativeDate);
52,138✔
201
    }
202

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

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

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

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

224
    setQuarter(quarter: number): TinyDate {
225
        return new TinyDate(setQuarter(this.nativeDate, quarter), this.useTimeZone);
7,059✔
226
    }
227

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

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

236
    setMinutes(minutes: number): TinyDate {
237
        return new TinyDate(setMinutes(this.nativeDate, minutes), this.useTimeZone);
2✔
238
    }
239

UNCOV
240
    setSeconds(seconds: number): TinyDate {
×
241
        return new TinyDate(setSeconds(this.nativeDate, seconds), this.useTimeZone);
242
    }
243

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

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

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

257
    addWeeks(amount: number): TinyDate {
258
        return new TinyDate(addWeeks(this.nativeDate, amount), this.useTimeZone);
15✔
259
    }
260

261
    addDays(amount: number): TinyDate {
262
        return new TinyDate(addDays(this.nativeDate, amount), this.useTimeZone);
2✔
263
    }
264
    addHours(amount: number): TinyDate {
265
        return new TinyDate(addHours(this.nativeDate, amount), this.useTimeZone);
43,934✔
266
    }
267

268
    addSeconds(amount: number): TinyDate {
2✔
269
        return new TinyDate(addSeconds(this.nativeDate, amount), this.useTimeZone);
270
    }
271

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

379✔
276
    // isSame
277

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

312
    isSameYear(date: TinyDateType): boolean {
147✔
313
        return this.isSame(date, 'year');
314
    }
315

213✔
316
    isSameMonth(date: TinyDateType): boolean {
317
        return this.isSame(date, 'month');
29,365!
318
    }
51,354✔
319

2✔
320
    isSameQuarter(date: TinyDateType): boolean {
321
        return this.isSame(date, 'quarter');
322
    }
51,352!
323

324
    isSameDay(date: TinyDateType): boolean {
127✔
325
        return this.isSame(date, 'day');
127✔
326
    }
327

64✔
328
    isSameHour(date: TinyDateType): boolean {
64✔
329
        return this.isSame(date, 'hour');
330
    }
44,078✔
331

44,078✔
332
    isSameMinute(date: TinyDateType): boolean {
333
        return this.isSame(date, 'minute');
7,061✔
334
    }
7,061✔
335

336
    isSameSecond(date: TinyDateType): boolean {
2✔
337
        return this.isSame(date, 'second');
2✔
338
    }
339

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

345
    isBeforeQuarter(date: TinyDate): boolean {
16✔
346
        return this.compare(date, 'quarter');
16✔
347
    }
UNCOV
348

×
UNCOV
349
    isBeforeMonth(date: TinyDateType): boolean {
×
350
        return this.compare(date, 'month');
351
    }
51,352✔
352

353
    isBeforeWeek(date: TinyDateType): boolean {
354
        return this.compare(date, 'week');
106,161✔
355
    }
356

357
    isBeforeDay(date: TinyDateType): boolean {
×
358
        return this.compare(date, 'day');
359
    }
UNCOV
360

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

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

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

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

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

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

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

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

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

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

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

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

410
    isToday(): boolean {
411
        return isToday(this.nativeDate);
412
    }
413

414
    isTomorrow(): boolean {
415
        return isTomorrow(this.nativeDate);
416
    }
417

418
    isValid(): boolean {
419
        return isValid(this.nativeDate);
420
    }
421

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

550
    subDays(amount: number): TinyDate {
551
        return new TinyDate(subDays(this.nativeDate, amount), this.useTimeZone);
552
    }
553
}
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

© 2026 Coveralls, Inc