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

atinc / ngx-tethys / #96

12 Aug 2025 06:20AM UTC coverage: 90.341% (+0.02%) from 90.324%
#96

push

web-flow
refactor(date-picker): migrate to signal for date-picker #TINFR-1463 (#3513)

* refactor(date-picker): migrate to signal for calendar header

* refactor(date-picker): migrate to signal for calendar footer

* refactor(date-picker): migrate to signal for calendar table

* refactor(date-picker): migrate to signal for date table cell

* refactor(date-picker): migrate to signal for date carousel

* refactor(date-picker): migrate to signal for inner-popup and date-popup

* refactor(date-picker): migrate to signal for pickers

5531 of 6813 branches covered (81.18%)

Branch coverage included in aggregate %.

342 of 367 new or added lines in 20 files covered. (93.19%)

66 existing lines in 11 files now uncovered.

13969 of 14772 relevant lines covered (94.56%)

904.1 hits per line

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

87.45
/src/date-picker/picker.util.ts
1
import { Signal } from '@angular/core';
2
import { ThyI18nLocale } from 'ngx-tethys/i18n';
3
import { SafeAny } from 'ngx-tethys/types';
861✔
4
import { coerceArray, helpers, TinyDate } from 'ngx-tethys/util';
464✔
5
import { CompatibleValue, RangeAdvancedValue } from './inner-types';
6
import { ThyCompatibleDate, DateEntry, ThyDateGranularity, ThyDateRangeEntry, ThyPanelMode, ThyShortcutValue } from './standard-types';
7

397✔
8
export function transformDateValue(
113✔
9
    value: ThyCompatibleDate | CompatibleValue | number | DateEntry | ThyDateRangeEntry | RangeAdvancedValue
10
): {
397✔
11
    value: ThyCompatibleDate;
53✔
12
    withTime?: boolean;
32✔
13
    flexibleDateGranularity?: ThyDateGranularity;
14
} {
15
    if (!value) {
21✔
16
        return { value: null };
21✔
17
    }
18
    let withTime, flexibleDateGranularity: ThyDateGranularity;
19
    if (value && typeof value === 'number') {
397✔
20
        value = convertDate(value);
3✔
21
    }
3!
22
    if (value && instanceOfCompatibleValue(value as CompatibleValue)) {
3✔
23
        if (value instanceof TinyDate) {
24
            value = convertDate(value.nativeDate);
397✔
25
        } else {
59✔
26
            value[0] = convertDate(value[0].nativeDate);
59✔
27
            value[1] = convertDate(value[1].nativeDate);
59✔
28
        }
54✔
29
    }
54✔
30
    if (value && instanceOfDateEntry(value as DateEntry)) {
31
        const { date, with_time } = value as DateEntry;
59✔
32
        value = date ? convertDate(date) : null;
1✔
33
        withTime = !!with_time;
34
    }
35
    if (value && instanceOfRangeEntry(value as ThyDateRangeEntry)) {
397✔
36
        const rangeValue = value as ThyDateRangeEntry;
6✔
37
        value = [];
6!
38
        if (rangeValue.begin && rangeValue.end) {
6✔
39
            value[0] = convertDate(rangeValue.begin);
40
            value[1] = convertDate(rangeValue.end);
6✔
41
        }
6!
42
        if (rangeValue.granularity) {
6✔
43
            flexibleDateGranularity = rangeValue.granularity;
6✔
44
        }
45
    }
46

397✔
47
    if (value && instanceOfRangeAdvancedValue(value as RangeAdvancedValue)) {
48
        const rangeValue = value as RangeAdvancedValue;
49
        if (rangeValue.dateGranularity) {
44✔
50
            flexibleDateGranularity = rangeValue.dateGranularity;
44✔
51
        }
24✔
52
        value = [];
53
        if (rangeValue.begin && rangeValue.end) {
20✔
54
            value[0] = convertDate(rangeValue.begin.nativeDate);
55
            value[1] = convertDate(rangeValue.end.nativeDate);
1✔
56
        }
1!
57
    }
1✔
58
    return { value: value as ThyCompatibleDate, withTime, flexibleDateGranularity };
59
}
UNCOV
60

×
61
export function getFlexibleAdvancedReadableValue(
62
    tinyDates: TinyDate[],
1✔
63
    flexibleDateGranularity: ThyDateGranularity,
64
    separator: string,
1✔
65
    locale: Signal<ThyI18nLocale>
1!
66
) {
1✔
67
    let value = '';
68
    if (!tinyDates[0] || !tinyDates[1]) {
UNCOV
69
        return value;
×
70
    }
71
    switch (flexibleDateGranularity) {
1✔
72
        case 'year':
73
            const yearFormatStr = locale().datePicker.yearFormat;
5✔
74
            if (tinyDates[0].isSameYear(tinyDates[1])) {
5✔
75
                value = `${tinyDates[0].format(yearFormatStr)}`;
2✔
76
            } else {
77
                value = `${tinyDates[0].format(yearFormatStr)}${separator}${tinyDates[1].format(yearFormatStr)}`;
78
            }
3✔
79
            break;
80
        case 'quarter':
5✔
81
            const quarterFormatStr = locale().datePicker.yearQuarterFormat;
82
            if (tinyDates[0].isSameQuarter(tinyDates[1])) {
20✔
83
                value = `${tinyDates[0].format(quarterFormatStr)}`;
84
            } else {
85
                value = `${tinyDates[0].format(quarterFormatStr)}${separator}${tinyDates[1].format(quarterFormatStr)}`;
310✔
86
            }
189✔
87
            break;
165✔
88
        case 'month':
89
            const monthFormatStr = locale().datePicker.yearMonthFormat;
90
            if (tinyDates[0].isSameMonth(tinyDates[1])) {
24✔
91
                value = `${tinyDates[0].format(monthFormatStr)}`;
92
            } else {
93
                value = `${tinyDates[0].format(monthFormatStr)}${separator}${tinyDates[1].format(monthFormatStr)}`;
121!
UNCOV
94
            }
×
95
            break;
96
    }
97
    return value;
121✔
98
}
99

100
export function convertDate(date: Date | number | TinyDate): Date {
101
    if (typeof date === 'number') {
2!
UNCOV
102
        if (date.toString().length < 13) {
×
103
            return TinyDate.fromUnixTime(date)?.nativeDate;
104
        } else {
105
            return new TinyDate(date)?.nativeDate;
2✔
106
        }
107
    } else if (date instanceof TinyDate) {
108
        return date?.nativeDate;
38✔
109
    } else {
604✔
110
        return new TinyDate(date)?.nativeDate;
176✔
111
    }
112
}
113

428✔
114
export function hasValue(value: CompatibleValue): boolean {
115
    if (Array.isArray(value)) {
116
        return !!value[0] && !!value[1];
117
    } else {
118
        return !!value;
214✔
119
    }
120
}
10✔
121

10✔
122
export function makeValue(value: ThyCompatibleDate | null, isRange: boolean = false, timeZone?: string): CompatibleValue {
123
    if (isRange) {
23✔
124
        return Array.isArray(value) ? (value as Date[]).map(val => new TinyDate(val, timeZone)) : [];
23✔
125
    } else {
126
        return value ? new TinyDate(value as Date, timeZone) : null;
175✔
127
    }
175✔
128
}
129

6✔
130
export function dateAddAmount(value: TinyDate, amount: number, mode: ThyPanelMode): TinyDate {
6✔
131
    let date: TinyDate;
132
    switch (mode) {
214✔
133
        case 'decade':
134
            date = value.addYears(amount * 10);
135
            break;
136
        case 'year':
200!
137
            date = value.addYears(amount);
200!
138
            break;
200!
UNCOV
139
        case 'month':
×
140
            date = value.addMonths(amount);
141
            break;
200✔
142
        default:
156✔
143
            date = value.addMonths(amount);
144
            break;
145
    }
44!
146
    return date;
147
}
148

149
// rightDate 超过 leftDate 一个月
200!
150
export function isAfterMoreThanOneMonth(rightDate: TinyDate, leftDate: TinyDate) {
200!
151
    rightDate = rightDate ? rightDate : leftDate ? leftDate : new TinyDate();
200✔
152
    leftDate = leftDate ? leftDate : rightDate;
182✔
153
    if (rightDate.getYear() < leftDate.getYear()) {
154
        return false;
155
    }
18✔
156

157
    if (rightDate.getYear() === leftDate.getYear() && leftDate.getMonth() + 1 >= rightDate.getMonth()) {
158
        return false;
159
    }
34!
160

34!
161
    // 处理rightDate(2020,1,1) 为leftDate(2020,12,1)后一年1月,同时leftDate日期为12月的特殊情况
34✔
162
    return !(rightDate.getYear() - leftDate.getYear() === 1 && rightDate.getMonth() === 0 && leftDate.getMonth() === 11);
30✔
163
}
164

165
// rightDate 超过 leftDate 不到一年
4✔
166
export function isAfterMoreThanLessOneYear(rightDate: TinyDate, leftDate: TinyDate) {
167
    rightDate = rightDate ? rightDate : leftDate ? leftDate : new TinyDate();
168
    leftDate = leftDate ? leftDate : rightDate;
169
    if (rightDate.getYear() <= leftDate.getYear()) {
13!
170
        return false;
13!
171
    }
13✔
172
    // 处理rightDate(2021,1,1)日期比leftDate(2020,12,1)日期大1年,同时rightDate日期月份小于leftDate日期月份的情况
173
    return !(rightDate.getYear() - leftDate.getYear() === 1 && rightDate.getMonth() <= leftDate.getMonth());
174
}
397✔
175

176
// rightDate 超过 leftDate 一年
177
export function isAfterMoreThanOneYear(rightDate: TinyDate, leftDate: TinyDate) {
397✔
178
    rightDate = rightDate ? rightDate : leftDate ? leftDate : new TinyDate();
179
    leftDate = leftDate ? leftDate : rightDate;
180
    if (leftDate.getYear() + 1 >= rightDate.getYear()) {
397✔
181
        return false;
182
    } else {
183
        return true;
397✔
184
    }
185
}
186

853✔
187
export function isAfterMoreThanOneDecade(rightDate: TinyDate, leftDate: TinyDate) {
188
    rightDate = rightDate ? rightDate : leftDate ? leftDate : new TinyDate();
189
    leftDate = leftDate ? leftDate : rightDate;
392!
190
    return rightDate.getYear() - leftDate.getYear() >= 20;
191
}
192

21✔
193
export function instanceOfDateEntry(object: DateEntry): object is DateEntry {
21✔
194
    return isSupportDateType(object, 'date') && typeof object.with_time === 'number';
195
}
196

47✔
197
export function instanceOfRangeEntry(object: ThyDateRangeEntry): object is ThyDateRangeEntry {
198
    return isSupportDateType(object, 'begin') && isSupportDateType(object, 'end');
199
}
200

201
export function instanceOfCompatibleValue(object: CompatibleValue): object is CompatibleValue {
51✔
202
    return object instanceof TinyDate || object[0] instanceof TinyDate;
51✔
203
}
51✔
204

205
export function instanceOfRangeAdvancedValue(object: RangeAdvancedValue): object is RangeAdvancedValue {
206
    return object['begin'] instanceof TinyDate && object['end'] instanceof TinyDate;
98✔
207
}
98✔
208

98✔
209
export function isSupportDateType(object: DateEntry | ThyDateRangeEntry, key: string) {
4✔
210
    return typeof object[key] === 'number' || object[key] === null || object[key] instanceof Date;
211
}
98✔
212

213
export function getShortcutValue(value: ThyShortcutValue): number | Date {
214
    return helpers.isFunction(value) ? value() : value;
93✔
215
}
93✔
216

13✔
217
export function isValidStringDate(dateStr: string, timeZone?: string): boolean {
218
    const parseDate = parseStringDate(dateStr, timeZone).nativeDate.getTime();
80✔
219
    return !(parseDate < 0 || isNaN(parseDate));
6✔
220
}
4✔
221

222
export function parseStringDate(dateStr: string, timeZone?: string): TinyDate {
223
    return hasTimeInStringDate(dateStr, timeZone)
224
        ? new TinyDate(fixStringDate(dateStr, timeZone), timeZone)
74✔
225
        : new TinyDate(fixStringDate(dateStr, timeZone), timeZone).startOfDay();
70✔
226
}
227

228
export function hasTimeInStringDate(dateStr: string, timeZone?: string): boolean {
229
    const formatDate = fixStringDate(dateStr, timeZone);
230
    const timeRegex = /(\d{1,2}:\d{1,2}(:\d{1,2})?)|(^\d{1,2}时\d{1,2}分(\d{1,2}秒)?)$/;
231
    return timeRegex.test(formatDate);
232
}
233

234
function fixStringDate(dateStr: string, timeZone?: string) {
235
    let replacedStr = dateStr.replace(/[^0-9\s.,:]/g, '-').replace('- ', ' ');
236
    const hasYear = /\d{4}/.test(replacedStr);
237
    if (!hasYear || replacedStr.length < 'yyyy.M.d'.length) {
238
        replacedStr = `${new TinyDate(undefined, timeZone).getYear()}-${replacedStr}`;
239
    }
240
    return replacedStr;
241
}
242

243
export function setValueByTimestampPrecision(
244
    date: ThyCompatibleDate | number | Date | DateEntry | ThyDateRangeEntry | SafeAny,
245
    isRange: boolean,
246
    timestampPrecision: 'seconds' | 'milliseconds',
247
    timeZone?: string
248
): number | number[] {
249
    const { value } = transformDateValue(date);
250
    if (!value || (helpers.isArray(value) && !value?.length)) {
251
        return helpers.isArray(value) ? [null, null] : null;
252
    }
253
    if (timestampPrecision === 'milliseconds') {
254
        return isRange
255
            ? coerceArray(value).map(val => new TinyDate(val, timeZone).getTime())
256
            : new TinyDate(value as Date, timeZone).getTime();
257
    } else {
258
        return isRange
259
            ? coerceArray(value).map(val => new TinyDate(val, timeZone).getUnixTime())
260
            : new TinyDate(value as Date, timeZone)?.getUnixTime();
261
    }
262
}
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