@superdispatch/dates 0.21.6 → 0.21.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc.js +5 -0
- package/.turbo/turbo-version.log +26 -0
- package/package.json +58 -32
- package/pkg/README.md +10 -0
- package/{dist-types → pkg/dist-types}/index.d.ts +237 -237
- package/pkg/package.json +32 -0
- package/playroom.ts +10 -0
- package/src/__tests__/index.spec.ts +48 -0
- package/src/base-date-picker/BaseDatePicker.tsx +145 -0
- package/src/calendar/Calendar.playroom.tsx +28 -0
- package/src/calendar/Calendar.spec.tsx +531 -0
- package/src/calendar/Calendar.stories.tsx +50 -0
- package/src/calendar/Calendar.tsx +534 -0
- package/src/calendar/CalendarQuickSelection.tsx +34 -0
- package/src/calendar/InternalCalendarComponents.tsx +79 -0
- package/src/date-config/DateConfig.spec.tsx +23 -0
- package/src/date-config/DateConfig.tsx +60 -0
- package/src/date-field/DateField.playroom.tsx +21 -0
- package/src/date-field/DateField.spec.tsx +350 -0
- package/src/date-field/DateField.stories.tsx +47 -0
- package/src/date-field/DateField.tsx +155 -0
- package/src/date-range-field/DateRangeField.playroom.tsx +24 -0
- package/src/date-range-field/DateRangeField.spec.tsx +318 -0
- package/src/date-range-field/DateRangeField.stories.tsx +51 -0
- package/src/date-range-field/DateRangeField.tsx +277 -0
- package/src/date-time-utils/DateTimeUtils.spec.ts +652 -0
- package/src/date-time-utils/DateTimeUtils.ts +339 -0
- package/src/date-utils/DateUtils.spec.ts +234 -0
- package/src/date-utils/DateUtils.ts +333 -0
- package/src/formatted-date/FormattedDate.spec.tsx +103 -0
- package/src/formatted-date/FormattedDate.ts +42 -0
- package/src/formatted-relative-time/FormattedRelativeTime.spec.tsx +93 -0
- package/src/formatted-relative-time/FormattedRelativeTime.ts +60 -0
- package/src/index.ts +12 -0
- package/src/time-field/TimeField.playroom.tsx +21 -0
- package/src/time-field/TimeField.stories.tsx +35 -0
- package/src/time-field/TimeField.tsx +221 -0
- package/src/use-date-time/useDateTime.spec.ts +45 -0
- package/src/use-date-time/useDateTime.ts +31 -0
- package/src/use-date-time-range/useDateTimeRange.spec.ts +53 -0
- package/src/use-date-time-range/useDateTimeRange.ts +24 -0
- package/tsconfig.json +19 -0
- package/LICENSE +0 -21
- /package/{dist-node → pkg/dist-node}/index.js +0 -0
- /package/{dist-node → pkg/dist-node}/index.js.map +0 -0
- /package/{dist-src → pkg/dist-src}/base-date-picker/BaseDatePicker.js +0 -0
- /package/{dist-src → pkg/dist-src}/calendar/Calendar.js +0 -0
- /package/{dist-src → pkg/dist-src}/calendar/CalendarQuickSelection.js +0 -0
- /package/{dist-src → pkg/dist-src}/calendar/InternalCalendarComponents.js +0 -0
- /package/{dist-src → pkg/dist-src}/date-config/DateConfig.js +0 -0
- /package/{dist-src → pkg/dist-src}/date-field/DateField.js +0 -0
- /package/{dist-src → pkg/dist-src}/date-range-field/DateRangeField.js +0 -0
- /package/{dist-src → pkg/dist-src}/date-time-utils/DateTimeUtils.js +0 -0
- /package/{dist-src → pkg/dist-src}/date-utils/DateUtils.js +0 -0
- /package/{dist-src → pkg/dist-src}/formatted-date/FormattedDate.js +0 -0
- /package/{dist-src → pkg/dist-src}/formatted-relative-time/FormattedRelativeTime.js +0 -0
- /package/{dist-src → pkg/dist-src}/index.js +0 -0
- /package/{dist-src → pkg/dist-src}/time-field/TimeField.js +0 -0
- /package/{dist-src → pkg/dist-src}/use-date-time/useDateTime.js +0 -0
- /package/{dist-src → pkg/dist-src}/use-date-time-range/useDateTimeRange.js +0 -0
- /package/{dist-web → pkg/dist-web}/index.js +0 -0
- /package/{dist-web → pkg/dist-web}/index.js.map +0 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import { DateTime, Settings, ToRelativeOptions, ToRelativeUnit } from 'luxon';
|
|
2
|
+
import {
|
|
3
|
+
DateConfig,
|
|
4
|
+
DateFormat,
|
|
5
|
+
defaultDateConfig,
|
|
6
|
+
} from '../date-config/DateConfig';
|
|
7
|
+
|
|
8
|
+
//
|
|
9
|
+
// Config
|
|
10
|
+
//
|
|
11
|
+
export type DateDisplayVariant = 'Date' | 'ShortDate' | 'Time' | 'DateTime';
|
|
12
|
+
|
|
13
|
+
const DATE_FORMATS: Readonly<Record<DateFormat, string>> = {
|
|
14
|
+
DateISO: '',
|
|
15
|
+
DateTimeISO: '',
|
|
16
|
+
JodaISO: "yyyy-MM-dd'T'HH:mm:ss.SSSZZZ",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const DATE_DISPLAY_VARIANTS: Readonly<
|
|
20
|
+
Record<DateDisplayVariant, Intl.DateTimeFormatOptions>
|
|
21
|
+
> = {
|
|
22
|
+
Date: {
|
|
23
|
+
day: '2-digit',
|
|
24
|
+
month: 'short',
|
|
25
|
+
year: 'numeric',
|
|
26
|
+
},
|
|
27
|
+
ShortDate: {
|
|
28
|
+
day: '2-digit',
|
|
29
|
+
month: 'short',
|
|
30
|
+
},
|
|
31
|
+
Time: {
|
|
32
|
+
hour: 'numeric',
|
|
33
|
+
minute: 'numeric',
|
|
34
|
+
},
|
|
35
|
+
DateTime: {
|
|
36
|
+
day: '2-digit',
|
|
37
|
+
month: 'short',
|
|
38
|
+
year: 'numeric',
|
|
39
|
+
hour: 'numeric',
|
|
40
|
+
minute: 'numeric',
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
//
|
|
45
|
+
// Date Utils
|
|
46
|
+
//
|
|
47
|
+
|
|
48
|
+
export type DateString = string;
|
|
49
|
+
export type NullableDateString = null | undefined | DateString;
|
|
50
|
+
export type PrimitiveDateInput = number | DateString;
|
|
51
|
+
export type NullableDateInput =
|
|
52
|
+
| null
|
|
53
|
+
| undefined
|
|
54
|
+
| Date
|
|
55
|
+
| DateTime
|
|
56
|
+
| PrimitiveDateInput;
|
|
57
|
+
|
|
58
|
+
export function toPrimitiveDateInput(
|
|
59
|
+
input: NullableDateInput,
|
|
60
|
+
): PrimitiveDateInput {
|
|
61
|
+
if (typeof input == 'number' || typeof input == 'string') {
|
|
62
|
+
return input;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (input instanceof Date || input instanceof DateTime) {
|
|
66
|
+
return input.valueOf();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return NaN;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function parseDate(
|
|
73
|
+
input: NullableDateInput,
|
|
74
|
+
{ format }: DateConfig = defaultDateConfig,
|
|
75
|
+
): DateTime {
|
|
76
|
+
if (input instanceof DateTime) {
|
|
77
|
+
const { defaultZone } = Settings;
|
|
78
|
+
|
|
79
|
+
if (!defaultZone.equals(input.zone)) {
|
|
80
|
+
return input.setZone(defaultZone);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return input;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (input instanceof Date) {
|
|
87
|
+
return DateTime.fromJSDate(input);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (typeof input === 'number') {
|
|
91
|
+
return DateTime.fromMillis(input);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (typeof input == 'string') {
|
|
95
|
+
switch (format) {
|
|
96
|
+
case 'DateISO':
|
|
97
|
+
case 'DateTimeISO':
|
|
98
|
+
return DateTime.fromISO(input);
|
|
99
|
+
default:
|
|
100
|
+
return DateTime.fromFormat(input, DATE_FORMATS[format]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return DateTime.invalid('invalid input');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function stringifyDate(
|
|
108
|
+
input: NullableDateInput,
|
|
109
|
+
config: DateConfig = defaultDateConfig,
|
|
110
|
+
): string | null {
|
|
111
|
+
const date = parseDate(input, config);
|
|
112
|
+
|
|
113
|
+
if (date.isValid) {
|
|
114
|
+
const { format } = config;
|
|
115
|
+
|
|
116
|
+
switch (format) {
|
|
117
|
+
case 'DateISO':
|
|
118
|
+
return date.toISODate();
|
|
119
|
+
case 'DateTimeISO':
|
|
120
|
+
return date.toISO();
|
|
121
|
+
default:
|
|
122
|
+
return date.toFormat(DATE_FORMATS[format]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface FormatDateConfig {
|
|
130
|
+
fallback?: string;
|
|
131
|
+
variant: DateDisplayVariant;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function formatDate(
|
|
135
|
+
input: NullableDateInput,
|
|
136
|
+
{ variant, fallback = 'Invalid Date' }: FormatDateConfig,
|
|
137
|
+
config: DateConfig = defaultDateConfig,
|
|
138
|
+
): string {
|
|
139
|
+
const date = parseDate(input, config);
|
|
140
|
+
|
|
141
|
+
if (!date.isValid) {
|
|
142
|
+
return fallback;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return date.toLocaleString(DATE_DISPLAY_VARIANTS[variant]);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function formatUnit(unit: ToRelativeUnit): string {
|
|
149
|
+
switch (unit) {
|
|
150
|
+
case 'months':
|
|
151
|
+
return 'mo';
|
|
152
|
+
default:
|
|
153
|
+
return unit.charAt(0);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface FormatRelativeTimeOptions
|
|
158
|
+
extends Pick<ToRelativeOptions, 'round' | 'padding'> {
|
|
159
|
+
fallback?: string;
|
|
160
|
+
unit?: ToRelativeUnit;
|
|
161
|
+
base?: NullableDateInput;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function formatRelativeTime(
|
|
165
|
+
input: NullableDateInput,
|
|
166
|
+
{
|
|
167
|
+
round = true,
|
|
168
|
+
|
|
169
|
+
unit: unitOption,
|
|
170
|
+
base: baseOption,
|
|
171
|
+
padding: paddingOption,
|
|
172
|
+
fallback = 'Invalid Date',
|
|
173
|
+
}: FormatRelativeTimeOptions = {},
|
|
174
|
+
config: DateConfig = defaultDateConfig,
|
|
175
|
+
): string {
|
|
176
|
+
const base =
|
|
177
|
+
baseOption == null ? DateTime.now() : parseDate(baseOption, config);
|
|
178
|
+
const date = parseDate(input, config);
|
|
179
|
+
const padding = paddingOption
|
|
180
|
+
? date < base
|
|
181
|
+
? -paddingOption
|
|
182
|
+
: paddingOption
|
|
183
|
+
: 0;
|
|
184
|
+
|
|
185
|
+
function format(value: number, unit: ToRelativeUnit): string {
|
|
186
|
+
const isPast = Object.is(value, -0) || value < 0;
|
|
187
|
+
const diff = Math.abs(!round ? value : Math.trunc(value));
|
|
188
|
+
const formattedUnit = formatUnit(unit);
|
|
189
|
+
|
|
190
|
+
return isPast
|
|
191
|
+
? `${diff}${formattedUnit} ago`
|
|
192
|
+
: `in ${diff}${formattedUnit}`;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function differ(unit: ToRelativeUnit): number {
|
|
196
|
+
return date.plus(padding).diff(base, unit).get(unit);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (date.isValid && base.isValid) {
|
|
200
|
+
const units: ToRelativeUnit[] = [
|
|
201
|
+
'years',
|
|
202
|
+
'months',
|
|
203
|
+
'days',
|
|
204
|
+
'hours',
|
|
205
|
+
'minutes',
|
|
206
|
+
'seconds',
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
if (unitOption) {
|
|
210
|
+
return format(differ(unitOption), unitOption);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
for (const unit of units) {
|
|
214
|
+
const diff = differ(unit);
|
|
215
|
+
|
|
216
|
+
if (Math.abs(diff) >= 1) {
|
|
217
|
+
return format(diff, unit);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return format(0, 'seconds');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return fallback;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface DatePayload {
|
|
228
|
+
readonly config: DateConfig;
|
|
229
|
+
readonly dateValue: DateTime;
|
|
230
|
+
readonly stringValue: null | DateString;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function toDatePayload(
|
|
234
|
+
input: NullableDateInput,
|
|
235
|
+
config: DateConfig = defaultDateConfig,
|
|
236
|
+
): DatePayload {
|
|
237
|
+
const dateValue = parseDate(input, config);
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
config,
|
|
241
|
+
dateValue,
|
|
242
|
+
stringValue: stringifyDate(dateValue, config),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
//
|
|
247
|
+
// Date Range Utils
|
|
248
|
+
//
|
|
249
|
+
|
|
250
|
+
export type DateTimeRange = [null | DateTime, null | DateTime];
|
|
251
|
+
export type DateStringRange = [null | DateString, null | DateString];
|
|
252
|
+
export type NullableDateRangeInput =
|
|
253
|
+
| null
|
|
254
|
+
| undefined
|
|
255
|
+
| [NullableDateInput?, NullableDateInput?];
|
|
256
|
+
export type PrimitiveDateRangeInput = [
|
|
257
|
+
undefined | PrimitiveDateInput,
|
|
258
|
+
undefined | PrimitiveDateInput,
|
|
259
|
+
];
|
|
260
|
+
|
|
261
|
+
export function toPrimitiveDateRangeInput(
|
|
262
|
+
input: NullableDateRangeInput,
|
|
263
|
+
): PrimitiveDateRangeInput {
|
|
264
|
+
if (!Array.isArray(input)) {
|
|
265
|
+
return [undefined, undefined];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return [toPrimitiveDateInput(input[0]), toPrimitiveDateInput(input[1])];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function parseDateRange(
|
|
272
|
+
input: NullableDateRangeInput,
|
|
273
|
+
config?: DateConfig,
|
|
274
|
+
): DateTimeRange {
|
|
275
|
+
let start: null | DateTime = null;
|
|
276
|
+
let finish: null | DateTime = null;
|
|
277
|
+
|
|
278
|
+
if (Array.isArray(input)) {
|
|
279
|
+
[start = null, finish = null] = input
|
|
280
|
+
.map((value) => parseDate(value, config))
|
|
281
|
+
.filter((date) => date.isValid)
|
|
282
|
+
.sort((a, b) => a.valueOf() - b.valueOf());
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return [start, finish];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function stringifyDateRange(
|
|
289
|
+
input: NullableDateRangeInput,
|
|
290
|
+
config?: DateConfig,
|
|
291
|
+
): DateStringRange {
|
|
292
|
+
const [start, finish] = parseDateRange(input, config);
|
|
293
|
+
|
|
294
|
+
return [stringifyDate(start, config), stringifyDate(finish, config)];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export interface FormatDateRangeOptions {
|
|
298
|
+
fallback?: string;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function formatDateRange(
|
|
302
|
+
input: NullableDateRangeInput,
|
|
303
|
+
{ fallback = 'Invalid Date Range' }: FormatDateRangeOptions,
|
|
304
|
+
config: DateConfig = defaultDateConfig,
|
|
305
|
+
): string {
|
|
306
|
+
const [start, finish] = parseDateRange(input, config);
|
|
307
|
+
|
|
308
|
+
if (!start) {
|
|
309
|
+
return fallback;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const startVariant: DateDisplayVariant = !finish?.hasSame(start, 'year')
|
|
313
|
+
? 'Date'
|
|
314
|
+
: 'ShortDate';
|
|
315
|
+
|
|
316
|
+
const startText = formatDate(start, { variant: startVariant }, config);
|
|
317
|
+
const finishText = !finish ? '…' : formatDate(finish, { variant: 'Date' });
|
|
318
|
+
|
|
319
|
+
return `${startText} - ${finishText}`;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export interface DateRangePayload {
|
|
323
|
+
config: DateConfig;
|
|
324
|
+
dateValue: DateTimeRange;
|
|
325
|
+
stringValue: DateStringRange;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export function toDateRangePayload(
|
|
329
|
+
input: NullableDateRangeInput,
|
|
330
|
+
config: DateConfig = defaultDateConfig,
|
|
331
|
+
): DateRangePayload {
|
|
332
|
+
const dateValue = parseDateRange(input, config);
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
config,
|
|
336
|
+
dateValue,
|
|
337
|
+
stringValue: stringifyDateRange(dateValue, config),
|
|
338
|
+
};
|
|
339
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { setDefaultTimeZone } from '../date-config/DateConfig';
|
|
2
|
+
import {
|
|
3
|
+
DateObject,
|
|
4
|
+
DateUtils,
|
|
5
|
+
isDate,
|
|
6
|
+
isDateLike,
|
|
7
|
+
isDateRange,
|
|
8
|
+
isDateRangeLike,
|
|
9
|
+
isValidDate,
|
|
10
|
+
isValidDateRange,
|
|
11
|
+
NullableDateLike,
|
|
12
|
+
toDate,
|
|
13
|
+
toDateRange,
|
|
14
|
+
} from './DateUtils';
|
|
15
|
+
|
|
16
|
+
function invalidDate() {
|
|
17
|
+
return new Date(NaN);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function stubDateObject({
|
|
21
|
+
year = 2019,
|
|
22
|
+
month = 5,
|
|
23
|
+
day = 24,
|
|
24
|
+
hour = 1,
|
|
25
|
+
minute = 2,
|
|
26
|
+
second = 3,
|
|
27
|
+
millisecond = 45,
|
|
28
|
+
}: Partial<DateObject> = {}): DateObject {
|
|
29
|
+
return {
|
|
30
|
+
year,
|
|
31
|
+
month,
|
|
32
|
+
day,
|
|
33
|
+
hour,
|
|
34
|
+
minute,
|
|
35
|
+
second,
|
|
36
|
+
millisecond,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function stubTimestamp(values?: Partial<DateObject>): number {
|
|
41
|
+
const { year, month, day, hour, minute, second, millisecond } =
|
|
42
|
+
stubDateObject(values);
|
|
43
|
+
|
|
44
|
+
return Date.UTC(year, month - 1, day, hour, minute, second, millisecond);
|
|
45
|
+
}
|
|
46
|
+
function stubDate(values?: Partial<DateObject>): Date {
|
|
47
|
+
return new Date(stubTimestamp(values));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
setDefaultTimeZone('local');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test.each`
|
|
55
|
+
input | date | dateLike | validDate
|
|
56
|
+
${null} | ${false} | ${false} | ${false}
|
|
57
|
+
${undefined} | ${false} | ${false} | ${false}
|
|
58
|
+
${''} | ${false} | ${false} | ${false}
|
|
59
|
+
${NaN} | ${false} | ${false} | ${false}
|
|
60
|
+
${Infinity} | ${false} | ${false} | ${false}
|
|
61
|
+
${0} | ${false} | ${true} | ${false}
|
|
62
|
+
${0.1} | ${false} | ${false} | ${false}
|
|
63
|
+
${Date.UTC(2020, 0)} | ${false} | ${true} | ${false}
|
|
64
|
+
${new Date(0)} | ${true} | ${true} | ${true}
|
|
65
|
+
${invalidDate()} | ${true} | ${true} | ${false}
|
|
66
|
+
${new Date(0).toISOString()} | ${false} | ${false} | ${false}
|
|
67
|
+
`('validates "$input"', ({ input, date, dateLike, validDate }) => {
|
|
68
|
+
expect(isDate(input)).toBe(date);
|
|
69
|
+
expect(isDateLike(input)).toBe(dateLike);
|
|
70
|
+
expect(isValidDate(input)).toBe(validDate);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test.each`
|
|
74
|
+
input | range | rangeLike | validRange
|
|
75
|
+
${null} | ${false} | ${false} | ${false}
|
|
76
|
+
${undefined} | ${false} | ${false} | ${false}
|
|
77
|
+
${{}} | ${false} | ${false} | ${false}
|
|
78
|
+
${''} | ${false} | ${false} | ${false}
|
|
79
|
+
${[]} | ${true} | ${true} | ${true}
|
|
80
|
+
${[stubTimestamp()]} | ${false} | ${true} | ${false}
|
|
81
|
+
${[stubTimestamp(), stubTimestamp()]} | ${false} | ${true} | ${false}
|
|
82
|
+
${[stubTimestamp(), stubTimestamp(), stubTimestamp()]} | ${false} | ${false} | ${false}
|
|
83
|
+
${[stubDate()]} | ${true} | ${true} | ${true}
|
|
84
|
+
${[stubDate(), stubDate()]} | ${true} | ${true} | ${true}
|
|
85
|
+
${[stubDate(), stubDate(), stubDate()]} | ${false} | ${false} | ${false}
|
|
86
|
+
${[invalidDate()]} | ${true} | ${true} | ${false}
|
|
87
|
+
${[invalidDate(), invalidDate()]} | ${true} | ${true} | ${false}
|
|
88
|
+
${[invalidDate(), invalidDate(), invalidDate()]} | ${false} | ${false} | ${false}
|
|
89
|
+
`('validates "$input" range', ({ input, range, rangeLike, validRange }) => {
|
|
90
|
+
expect(isDateRange(input)).toBe(range);
|
|
91
|
+
expect(isDateRangeLike(input)).toBe(rangeLike);
|
|
92
|
+
expect(isValidDateRange(input)).toBe(validRange);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test.each`
|
|
96
|
+
input | result
|
|
97
|
+
${null} | ${invalidDate()}
|
|
98
|
+
${undefined} | ${invalidDate()}
|
|
99
|
+
${'foo'} | ${invalidDate()}
|
|
100
|
+
${NaN} | ${invalidDate()}
|
|
101
|
+
${Infinity} | ${invalidDate()}
|
|
102
|
+
${stubDate()} | ${stubDate()}
|
|
103
|
+
${stubTimestamp()} | ${stubDate()}
|
|
104
|
+
`('toDate($input) => $result', ({ input, result }) => {
|
|
105
|
+
expect(toDate(input)).toBeSameDate(result);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test.each([
|
|
109
|
+
[undefined, undefined, undefined, undefined],
|
|
110
|
+
[invalidDate(), undefined, invalidDate(), undefined],
|
|
111
|
+
[undefined, invalidDate(), invalidDate(), undefined],
|
|
112
|
+
[invalidDate(), invalidDate(), invalidDate(), invalidDate()],
|
|
113
|
+
[stubTimestamp(), invalidDate(), invalidDate(), stubDate()],
|
|
114
|
+
[invalidDate(), stubTimestamp(), invalidDate(), stubDate()],
|
|
115
|
+
[stubTimestamp(), undefined, stubDate(), undefined],
|
|
116
|
+
[undefined, stubTimestamp(), stubDate(), undefined],
|
|
117
|
+
[
|
|
118
|
+
stubTimestamp({ year: 2019 }),
|
|
119
|
+
stubTimestamp({ year: 2020 }),
|
|
120
|
+
stubDate({ year: 2019 }),
|
|
121
|
+
stubDate({ year: 2020 }),
|
|
122
|
+
],
|
|
123
|
+
])(
|
|
124
|
+
'toDateRange(%p, %p)',
|
|
125
|
+
(rawStart, rawFinish, expectedStart, expectedFinish) => {
|
|
126
|
+
const [start, finish] = toDateRange([rawStart, rawFinish]);
|
|
127
|
+
|
|
128
|
+
expect(start).toBeSameDate(expectedStart);
|
|
129
|
+
expect(finish).toBeSameDate(expectedFinish);
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
test.each`
|
|
134
|
+
tz | date | shortDate | time | dateTime
|
|
135
|
+
${0} | ${'May 24, 2019'} | ${'May 24'} | ${'1:02 AM'} | ${'May 24, 2019, 1:02 AM'}
|
|
136
|
+
${+300} | ${'May 24, 2019'} | ${'May 24'} | ${'6:02 AM'} | ${'May 24, 2019, 6:02 AM'}
|
|
137
|
+
${-300} | ${'May 23, 2019'} | ${'May 23'} | ${'8:02 PM'} | ${'May 23, 2019, 8:02 PM'}
|
|
138
|
+
`(
|
|
139
|
+
'DateUtils({ zone: $tz })#format()',
|
|
140
|
+
({ tz, date, shortDate, time, dateTime }) => {
|
|
141
|
+
const utils = new DateUtils();
|
|
142
|
+
|
|
143
|
+
setDefaultTimeZone(tz);
|
|
144
|
+
|
|
145
|
+
expect(utils.format(stubDate(), 'date')).toBe(date);
|
|
146
|
+
expect(utils.format(stubTimestamp(), 'date')).toBe(date);
|
|
147
|
+
|
|
148
|
+
expect(utils.format(stubDate(), 'shortDate')).toBe(shortDate);
|
|
149
|
+
expect(utils.format(stubTimestamp(), 'shortDate')).toBe(shortDate);
|
|
150
|
+
|
|
151
|
+
expect(utils.format(stubDate(), 'time')).toBe(time);
|
|
152
|
+
expect(utils.format(stubTimestamp(), 'time')).toBe(time);
|
|
153
|
+
|
|
154
|
+
expect(utils.format(stubDate(), 'dateTime')).toBe(dateTime);
|
|
155
|
+
expect(utils.format(stubTimestamp(), 'dateTime')).toBe(dateTime);
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
test.each<[NullableDateLike, NullableDateLike, string | undefined, string]>([
|
|
160
|
+
[undefined, undefined, undefined, ''],
|
|
161
|
+
[undefined, undefined, 'Empty Text', 'Empty Text'],
|
|
162
|
+
[invalidDate(), undefined, undefined, 'Invalid Date Range'],
|
|
163
|
+
[invalidDate(), invalidDate(), undefined, 'Invalid Date Range'],
|
|
164
|
+
[stubDate(), invalidDate(), undefined, 'Invalid Date Range'],
|
|
165
|
+
[stubDate(), undefined, undefined, 'May 24, 2019 - …'],
|
|
166
|
+
[
|
|
167
|
+
stubDate({ month: 5 }),
|
|
168
|
+
stubDate({ month: 6 }),
|
|
169
|
+
undefined,
|
|
170
|
+
'May 24 - Jun 24, 2019',
|
|
171
|
+
],
|
|
172
|
+
[
|
|
173
|
+
stubDate({ year: 2020 }),
|
|
174
|
+
stubDate({ year: 2019 }),
|
|
175
|
+
undefined,
|
|
176
|
+
'May 24, 2019 - May 24, 2020',
|
|
177
|
+
],
|
|
178
|
+
])(
|
|
179
|
+
'DateUtils#formatDateRange([%p, %p]) -> %p',
|
|
180
|
+
(start, end, emptyText, result) => {
|
|
181
|
+
const utils = new DateUtils();
|
|
182
|
+
|
|
183
|
+
setDefaultTimeZone(0);
|
|
184
|
+
|
|
185
|
+
expect(utils.formatRange([start, end], emptyText)).toBe(result);
|
|
186
|
+
expect(utils.formatRange([end, start], emptyText)).toBe(result);
|
|
187
|
+
},
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
test.each([
|
|
191
|
+
[[NaN], 'in 0 sec.', 'in 0 sec.', 'in 0 seconds'],
|
|
192
|
+
[[-3], 'in 3 yr.', 'in 3 yr.', 'in 3 years'],
|
|
193
|
+
[[+3], '3 yr. ago', '3 yr. ago', '3 years ago'],
|
|
194
|
+
[[0, -3], 'in 3 mo.', 'in 3 mo.', 'in 3 months'],
|
|
195
|
+
[[0, +3], '3 mo. ago', '3 mo. ago', '3 months ago'],
|
|
196
|
+
[[0, 0, -3], 'in 3 days', 'in 3 days', 'in 3 days'],
|
|
197
|
+
[[0, 0, +3], '3 days ago', '3 days ago', '3 days ago'],
|
|
198
|
+
[[0, 0, 0, -3], 'in 3 hr.', 'in 3 hr.', 'in 3 hours'],
|
|
199
|
+
[[0, 0, 0, +3], '3 hr. ago', '3 hr. ago', '3 hours ago'],
|
|
200
|
+
[[0, 0, 0, 0, -3], 'in 3 min.', 'in 3 min.', 'in 3 minutes'],
|
|
201
|
+
[[0, 0, 0, 0, +3], '3 min. ago', '3 min. ago', '3 minutes ago'],
|
|
202
|
+
[[0, 0, 0, 0, 0, -3], 'in 3 sec.', 'in 3 sec.', 'in 3 seconds'],
|
|
203
|
+
[[0, 0, 0, 0, 0, +3], '3 sec. ago', '3 sec. ago', '3 seconds ago'],
|
|
204
|
+
])(
|
|
205
|
+
'DateUtils#formatRelativeTime($offsets)',
|
|
206
|
+
(
|
|
207
|
+
[years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds = 0],
|
|
208
|
+
short,
|
|
209
|
+
narrow,
|
|
210
|
+
long,
|
|
211
|
+
) => {
|
|
212
|
+
const utils = new DateUtils();
|
|
213
|
+
const input = new Date(Date.UTC(2019, 4, 24));
|
|
214
|
+
const base = new Date(
|
|
215
|
+
Date.UTC(years + 2019, months + 4, days + 24, hours, minutes, seconds),
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
expect(
|
|
219
|
+
utils.formatRelativeTime(input, { compare: base, style: 'narrow' }),
|
|
220
|
+
).toBe(narrow);
|
|
221
|
+
expect(
|
|
222
|
+
utils.formatRelativeTime(input, { compare: base, style: 'short' }),
|
|
223
|
+
).toBe(short);
|
|
224
|
+
expect(
|
|
225
|
+
utils.formatRelativeTime(input, { compare: base, style: 'long' }),
|
|
226
|
+
).toBe(long);
|
|
227
|
+
expect(utils.formatRelativeTime(input, { compare: base })).toBe(long);
|
|
228
|
+
|
|
229
|
+
jest.spyOn(Date, 'now').mockImplementation(() => base.getTime());
|
|
230
|
+
expect(Date.now).toHaveBeenCalledTimes(0);
|
|
231
|
+
expect(utils.formatRelativeTime(input)).toBe(long);
|
|
232
|
+
expect(Date.now).toHaveBeenCalledTimes(1);
|
|
233
|
+
},
|
|
234
|
+
);
|