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

atinc / ngx-tethys / #55

30 Jul 2025 07:08AM UTC coverage: 9.866% (-80.4%) from 90.297%
#55

push

why520crazy
feat(empty): add setMessage for update display text #TINFR-2616

92 of 6794 branches covered (1.35%)

Branch coverage included in aggregate %.

2014 of 14552 relevant lines covered (13.84%)

6.15 hits per line

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

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

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

8!
65
export type WeekDayIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6;
66

67
export type TinyDateType = TinyDate | Date | null;
×
68

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

77
export const DEFAULT_TIMEZONE = 'Asia/Shanghai';
×
78

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

82
    private useTimeZone: string;
83

×
84
    private static locale: string = getDefaultLocaleId();
85

86
    protected static dateFnsLocale: Locale = getDateFnsLocale(TinyDate.locale);
×
87

88
    protected static defaultTimeZone: string = DEFAULT_TIMEZONE;
89

×
90
    constructor(date?: Date | string | number, zone?: string) {
91
        setDefaultOptions({ locale: TinyDate.dateFnsLocale });
×
92
        this.useTimeZone = zone || TinyDate.defaultTimeZone;
×
93
        if (date) {
94
            if (date instanceof Date) {
95
                this.nativeDate = TinyDate.utcToZonedTime(date, this.useTimeZone);
×
96
            } else if (typeof date === 'string') {
97
                if (hasTimeInStringDate(date)) {
98
                    // If the string contains time, you need to convert the time to UTC time before passing it to TZDate
×
99
                    const originTime = new Date(date);
100
                    const zoneTime = TZDate.tz(this.useTimeZone, originTime);
101
                    const utcDate = fromZonedTime(zoneTime, this.useTimeZone).toISOString();
×
102
                    this.nativeDate = new TZDate(utcDate, this.useTimeZone);
103
                } else {
104
                    this.nativeDate = new TZDate(date, this.useTimeZone);
×
105
                }
106
            } else if (typeof date === 'number') {
107
                this.nativeDate = new TZDate(date, this.useTimeZone);
×
108
            } else if (typeof ngDevMode === 'undefined' || ngDevMode) {
109
                throw new Error(
110
                    `The input date type is not supported expect Date | string | number | { date: number; with_time: 0 | 1}, actual ${JSON.stringify(
×
111
                        date
112
                    )}`
113
                );
×
114
            }
115
        } else {
116
            this.nativeDate = new TZDate(Date.now(), this.useTimeZone);
117
        }
×
118
    }
119

120
    static setDefaultLocale(locale: string) {
×
121
        TinyDate.locale = locale;
122
        TinyDate.dateFnsLocale = getDateFnsLocale(locale);
123
        return setDefaultOptions({ locale: TinyDate.dateFnsLocale });
×
124
    }
125

126
    static getDefaultLocale(): { locale: string; dateFnsLocale: Locale } {
×
127
        return { locale: TinyDate.locale, dateFnsLocale: TinyDate.dateFnsLocale };
128
    }
129

×
130
    static setDefaultTimeZone(zone: string) {
131
        TinyDate.defaultTimeZone = zone ?? DEFAULT_TIMEZONE;
132
    }
×
133

134
    static getDefaultTimeZone(): string {
135
        return TinyDate.defaultTimeZone;
×
136
    }
137

138
    static utcToZonedTime(value: Date | number, timeZone?: string): Date {
×
139
        return TZDate.tz(timeZone || TinyDate.defaultTimeZone, value as any);
140
    }
141

×
142
    static createDateInTimeZone(
143
        year: number,
144
        month: number,
145
        day: number,
×
146
        hours: number,
147
        minutes: number,
148
        seconds: number,
×
149
        timeZone?: string
150
    ): Date {
151
        return new TZDate(year, month, day, hours, minutes, seconds, timeZone || TinyDate.defaultTimeZone);
×
152
    }
153

154
    static fromUnixTime(unixTime: number, timeZone?: string): TinyDate {
×
155
        return new TinyDate(fromUnixTime(unixTime), timeZone || TinyDate.defaultTimeZone);
156
    }
157

×
158
    // get
159
    getTime(): number {
160
        return this.nativeDate.getTime();
×
161
    }
162

163
    getDate(): number {
×
164
        return this.nativeDate.getDate();
165
    }
166

×
167
    getYear(): number {
168
        return this.nativeDate.getFullYear();
169
    }
×
170

171
    getQuarter(): number {
×
172
        return getQuarter(this.nativeDate);
173
    }
×
174

×
175
    getMonth(): number {
176
        return this.nativeDate.getMonth();
×
177
    }
×
178

179
    getFullYear(): number {
×
180
        return this.nativeDate.getFullYear();
×
181
    }
182

×
183
    getWeek(options: { locale?: Locale; weekStartsOn?: WeekDayIndex } = { weekStartsOn: 1 }): number {
×
184
        return getWeek(this.nativeDate, options);
185
    }
×
186

×
187
    getDay(): number {
188
        return this.nativeDate.getDay();
×
189
    }
×
190

191
    getHours(): number {
×
192
        return this.nativeDate.getHours();
×
193
    }
194

×
195
    getMinutes(): number {
×
196
        return this.nativeDate.getMinutes();
197
    }
×
198

×
199
    getSeconds(): number {
200
        return this.nativeDate.getSeconds();
×
201
    }
202

203
    getMilliseconds(): number {
×
204
        return this.nativeDate.getMilliseconds();
205
    }
206

×
207
    getDaysInMonth() {
208
        return getDaysInMonth(this.nativeDate);
209
    }
×
210

211
    getDaysInQuarter() {
212
        return differenceInCalendarDays(this.endOfQuarter().addSeconds(1).nativeDate, this.startOfQuarter().nativeDate);
×
213
    }
214

215
    // set
×
216
    setDate(amount: number): TinyDate {
217
        return new TinyDate(this.nativeDate.setDate(amount), this.useTimeZone);
218
    }
×
219

220
    setHms(hour: number, minute: number, second: number): TinyDate {
221
        return new TinyDate(this.nativeDate?.setHours(hour, minute, second), this.useTimeZone);
×
222
    }
223

224
    setYear(year: number): TinyDate {
225
        return new TinyDate(setYear(this.nativeDate, year), this.useTimeZone);
×
226
    }
227

228
    setMonth(month: number): TinyDate {
×
229
        return new TinyDate(setMonth(this.nativeDate, month), this.useTimeZone);
230
    }
231

×
232
    setQuarter(quarter: number): TinyDate {
233
        return new TinyDate(setQuarter(this.nativeDate, quarter), this.useTimeZone);
234
    }
×
235

236
    setDay(day: number, options?: { weekStartsOn: WeekDayIndex }): TinyDate {
237
        return new TinyDate(setDay(this.nativeDate, day, options), this.useTimeZone);
×
238
    }
239

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

×
244
    setMinutes(minutes: number): TinyDate {
245
        return new TinyDate(setMinutes(this.nativeDate, minutes), this.useTimeZone);
246
    }
×
247

248
    setSeconds(seconds: number): TinyDate {
249
        return new TinyDate(setSeconds(this.nativeDate, seconds), this.useTimeZone);
×
250
    }
251

252
    // add
×
253
    addYears(amount: number): TinyDate {
254
        return new TinyDate(addYears(this.nativeDate, amount), this.useTimeZone);
255
    }
×
256

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

261
    addMonths(amount: number): TinyDate {
×
262
        return new TinyDate(addMonths(this.nativeDate, amount), this.useTimeZone);
263
    }
264

×
265
    addWeeks(amount: number): TinyDate {
266
        return new TinyDate(addWeeks(this.nativeDate, amount), this.useTimeZone);
267
    }
×
268

269
    addDays(amount: number): TinyDate {
270
        return new TinyDate(addDays(this.nativeDate, amount), this.useTimeZone);
×
271
    }
272
    addHours(amount: number): TinyDate {
273
        return new TinyDate(addHours(this.nativeDate, amount), this.useTimeZone);
274
    }
×
275

276
    addSeconds(amount: number): TinyDate {
277
        return new TinyDate(addSeconds(this.nativeDate, amount), this.useTimeZone);
×
278
    }
279

280
    addMinutes(amount: number): TinyDate {
×
281
        return new TinyDate(addMinutes(this.nativeDate, amount), this.useTimeZone);
282
    }
283

×
284
    // isSame
285

286
    isSame(date: TinyDateType, grain: TinyDateCompareGrain = 'day'): boolean {
287
        let fn;
×
288
        switch (grain) {
289
            case 'decade':
290
                fn = (pre: Date, next: Date) => Math.abs(pre.getFullYear() - next.getFullYear()) < 11;
×
291
                break;
292
            case 'year':
293
                fn = isSameYear;
1✔
294
                break;
295
            case 'month':
296
                fn = isSameMonth;
1✔
297
                break;
298
            case 'quarter':
299
                fn = isSameQuarter;
×
300
                break;
301
            case 'day':
302
                fn = isSameDay;
×
303
                break;
304
            case 'hour':
305
                fn = isSameHour;
×
306
                break;
307
            case 'minute':
308
                fn = isSameMinute;
1✔
309
                break;
310
            case 'second':
311
                fn = isSameSecond;
1✔
312
                break;
313
            default:
314
                fn = isSameDay;
2✔
315
                break;
316
        }
317
        return fn(this.nativeDate, this.toNativeDate(date));
318
    }
×
319

320
    isSameYear(date: TinyDateType): boolean {
321
        return this.isSame(date, 'year');
×
322
    }
323

324
    isSameMonth(date: TinyDateType): boolean {
×
325
        return this.isSame(date, 'month');
326
    }
327

×
328
    isSameQuarter(date: TinyDateType): boolean {
329
        return this.isSame(date, 'quarter');
×
330
    }
×
331

×
332
    isSameDay(date: TinyDateType): boolean {
333
        return this.isSame(date, 'day');
334
    }
×
335

336
    isSameHour(date: TinyDateType): boolean {
×
337
        return this.isSame(date, 'hour');
×
338
    }
339

×
340
    isSameMinute(date: TinyDateType): boolean {
×
341
        return this.isSame(date, 'minute');
342
    }
×
343

×
344
    isSameSecond(date: TinyDateType): boolean {
345
        return this.isSame(date, 'second');
×
346
    }
×
347

348
    // isBefore and isAfter
×
349
    isBeforeYear(date: TinyDateType): boolean {
×
350
        return this.compare(date, 'year');
351
    }
×
352

×
353
    isBeforeQuarter(date: TinyDate): boolean {
354
        return this.compare(date, 'quarter');
×
355
    }
×
356

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

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

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

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

×
373
    isBeforeMinute(date: TinyDateType): boolean {
374
        return this.compare(date, 'minute');
375
    }
×
376

377
    isBeforeSecond(date: TinyDateType): boolean {
378
        return this.compare(date, 'second');
×
379
    }
380

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

×
385
    isAfterQuarter(date: TinyDate): boolean {
386
        return this.compare(date, 'quarter', false);
387
    }
388

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

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

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

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

405
    isAfterMinute(date: TinyDateType): boolean {
406
        return this.compare(date, 'minute', false);
407
    }
408

409
    isAfterSecond(date: TinyDateType): boolean {
410
        return this.compare(date, 'second', false);
411
    }
412

413
    // is
414
    isWeekend(): boolean {
415
        return isWeekend(this.nativeDate);
416
    }
417

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

422
    isTomorrow(): boolean {
423
        return isTomorrow(this.nativeDate);
424
    }
425

426
    isValid(): boolean {
427
        return isValid(this.nativeDate);
428
    }
429

430
    // startOf and endOf
431
    startOfYear(): TinyDate {
432
        return new TinyDate(startOfYear(this.nativeDate), this.useTimeZone);
433
    }
434

435
    startOfQuarter(): TinyDate {
436
        return new TinyDate(startOfQuarter(this.nativeDate), this.useTimeZone);
437
    }
438

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

443
    startOfWeek(options?: { locale?: Locale; weekStartsOn?: WeekDayIndex }): TinyDate {
444
        return new TinyDate(startOfWeek(this.nativeDate, options), this.useTimeZone);
445
    }
446

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

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

455
    endOfQuarter(): TinyDate {
456
        return new TinyDate(endOfQuarter(this.nativeDate), this.useTimeZone);
457
    }
458

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

463
    endOfWeek(options?: { locale?: Locale; weekStartsOn?: WeekDayIndex }): TinyDate {
464
        return new TinyDate(endOfWeek(this.nativeDate, options), this.useTimeZone);
465
    }
466

467
    endOfDay(): TinyDate {
468
        return new TinyDate(endOfDay(this.nativeDate), this.useTimeZone);
469
    }
470

471
    // other
472
    format(
473
        mat: string,
474
        options?: {
475
            locale?: Locale;
476
            weekStartsOn?: WeekDayIndex;
477
            firstWeekContainsDate?: FirstWeekContainsDate;
478
            useAdditionalWeekYearTokens?: boolean;
479
            useAdditionalDayOfYearTokens?: boolean;
480
        }
481
    ) {
482
        return format(this.nativeDate, mat, options);
483
    }
484

485
    calendarStart(options?: { weekStartsOn: WeekDayIndex | undefined }): TinyDate {
486
        return new TinyDate(startOfWeek(startOfMonth(this.nativeDate), options), this.useTimeZone);
487
    }
488

489
    clone(): TinyDate {
490
        return new TinyDate(this.nativeDate, this.useTimeZone);
491
    }
492

493
    getUnixTime(): number {
494
        return getUnixTime(this.nativeDate);
495
    }
496

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

534
    private toNativeDate(date: any): Date {
535
        return date instanceof TinyDate ? date.nativeDate : date;
536
    }
537

538
    startOfISOWeek(): TinyDate {
539
        return new TinyDate(startOfISOWeek(this.nativeDate), this.useTimeZone);
540
    }
541

542
    endOfISOWeek(): TinyDate {
543
        return new TinyDate(endOfISOWeek(this.nativeDate), this.useTimeZone);
544
    }
545

546
    differenceInDays(date: Date): number {
547
        return differenceInDays(this.nativeDate, date);
548
    }
549

550
    differenceInHours(date: Date): number {
551
        return differenceInHours(this.nativeDate, date);
552
    }
553

554
    subWeeks(amount: number): TinyDate {
555
        return new TinyDate(subWeeks(this.nativeDate, amount), this.useTimeZone);
556
    }
557

558
    subDays(amount: number): TinyDate {
559
        return new TinyDate(subDays(this.nativeDate, amount), this.useTimeZone);
560
    }
561
}
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