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

atinc / ngx-tethys / e479b10a-d5b5-4901-a22d-cb1d6e7afdf4

14 Apr 2025 03:08AM UTC coverage: 90.233% (-0.003%) from 90.236%
e479b10a-d5b5-4901-a22d-cb1d6e7afdf4

Pull #3331

circleci

xinglu01
Merge branch 'master' of github.com:atinc/ngx-tethys into xl/tiny-date
Pull Request #3331: fix(util): handle i18n and tinyDate set default locale

5602 of 6871 branches covered (81.53%)

Branch coverage included in aggregate %.

9 of 9 new or added lines in 3 files covered. (100.0%)

9 existing lines in 1 file now uncovered.

13364 of 14148 relevant lines covered (94.46%)

995.67 hits per line

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

92.34
/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 { getDefaultLocaleId } 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,
1✔
17
    differenceInCalendarYears,
18
    differenceInDays,
44,141✔
19
    differenceInHours,
44,141✔
20
    differenceInMinutes,
44,141✔
21
    differenceInSeconds,
41,464✔
22
    differenceInWeeks,
41,054✔
23
    endOfDay,
24
    endOfISOWeek,
410✔
25
    endOfMonth,
409✔
26
    endOfQuarter,
27
    endOfWeek,
1!
28
    endOfYear,
1✔
29
    format,
30
    fromUnixTime,
31
    getDateFnsLocale,
32
    getDaysInMonth,
2,677✔
33
    getQuarter,
34
    getUnixTime,
35
    getWeek,
36
    isSameDay,
67✔
37
    isSameHour,
67✔
38
    isSameMinute,
67✔
39
    isSameMonth,
40
    isSameQuarter,
UNCOV
41
    isSameSecond,
×
42
    isSameYear,
43
    isToday,
44
    isTomorrow,
1!
45
    isValid,
46
    isWeekend,
UNCOV
47
    setDay,
×
48
    setDefaultOptions,
49
    setMonth,
50
    setQuarter,
41,054!
51
    setYear,
52
    startOfDay,
53
    startOfISOWeek,
41✔
54
    startOfMonth,
55
    startOfQuarter,
56
    startOfWeek,
172✔
57
    startOfYear,
58
    subDays,
59
    subWeeks
60
} from './functions';
1,222✔
61

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

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

66
export type TinyDateType = TinyDate | Date | null;
7,102✔
67

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

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

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

81
    private useTimeZone: string;
3✔
82

83
    private static locale: string = getDefaultLocaleId();
84

66✔
85
    protected static dateFnsLocale: Locale = getDateFnsLocale(TinyDate.locale);
86

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

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

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

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

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

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

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

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

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

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

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

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

×
160
    getQuarter(): number {
161
        return getQuarter(this.nativeDate);
55,147✔
162
    }
163

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

799✔
168
    getFullYear(): number {
169
        return this.nativeDate.getFullYear();
1,425✔
170
    }
1,425✔
171

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

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

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

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

1✔
188
    getSeconds(): number {
1✔
189
        return this.nativeDate.getSeconds();
190
    }
55,147✔
191

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

277
    // isSame
379✔
278

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

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

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

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

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

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

44,414✔
333
    isSameMinute(date: TinyDateType): boolean {
44,414✔
334
        return this.isSame(date, 'minute');
335
    }
7,118✔
336

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

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

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

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

51,745✔
354
    isBeforeWeek(date: TinyDateType): boolean {
355
        return this.compare(date, 'week');
356
    }
106,892✔
357

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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