Skip to content

Commit e7eb56f

Browse files
committed
refactor(material-date-fns-adapter): implement new methods
Implements the new methods in the `date-fns` adapter.
1 parent 70cc558 commit e7eb56f

File tree

3 files changed

+180
-10
lines changed

3 files changed

+180
-10
lines changed

src/material-date-fns-adapter/adapter/date-fns-adapter.spec.ts

Lines changed: 134 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import {TestBed, waitForAsync} from '@angular/core/testing';
9+
import {TestBed} from '@angular/core/testing';
1010
import {DateAdapter, MAT_DATE_LOCALE} from '@angular/material/core';
1111
import {Locale} from 'date-fns';
12-
import {ja, enUS, da, de} from 'date-fns/locale';
12+
import {ja, enUS, da, de, fi} from 'date-fns/locale';
1313
import {DateFnsModule} from './index';
1414

1515
const JAN = 0,
@@ -20,14 +20,11 @@ const JAN = 0,
2020
describe('DateFnsAdapter', () => {
2121
let adapter: DateAdapter<Date, Locale>;
2222

23-
beforeEach(waitForAsync(() => {
24-
TestBed.configureTestingModule({
25-
imports: [DateFnsModule],
26-
});
27-
23+
beforeEach(() => {
24+
TestBed.configureTestingModule({imports: [DateFnsModule]});
2825
adapter = TestBed.inject(DateAdapter);
2926
adapter.setLocale(enUS);
30-
}));
27+
});
3128

3229
it('should get year', () => {
3330
expect(adapter.getYear(new Date(2017, JAN, 1))).toBe(2017);
@@ -452,19 +449,146 @@ describe('DateFnsAdapter', () => {
452449
it('should create invalid date', () => {
453450
assertValidDate(adapter, adapter.invalid(), false);
454451
});
452+
453+
it('should get hours', () => {
454+
expect(adapter.getHours(new Date(2024, JAN, 1, 14))).toBe(14);
455+
});
456+
457+
it('should get minutes', () => {
458+
expect(adapter.getMinutes(new Date(2024, JAN, 1, 14, 53))).toBe(53);
459+
});
460+
461+
it('should get seconds', () => {
462+
expect(adapter.getSeconds(new Date(2024, JAN, 1, 14, 53, 42))).toBe(42);
463+
});
464+
465+
it('should set the time of a date', () => {
466+
const target = new Date(2024, JAN, 1, 0, 0, 0);
467+
const result = adapter.setTime(target, 14, 53, 42);
468+
expect(adapter.getHours(result)).toBe(14);
469+
expect(adapter.getMinutes(result)).toBe(53);
470+
expect(adapter.getSeconds(result)).toBe(42);
471+
});
472+
473+
it('should throw when passing in invalid hours to setTime', () => {
474+
expect(() => adapter.setTime(adapter.today(), -1, 0, 0)).toThrowError(
475+
'Invalid hours "-1". Hours value must be between 0 and 23.',
476+
);
477+
expect(() => adapter.setTime(adapter.today(), 51, 0, 0)).toThrowError(
478+
'Invalid hours "51". Hours value must be between 0 and 23.',
479+
);
480+
});
481+
482+
it('should throw when passing in invalid minutes to setTime', () => {
483+
expect(() => adapter.setTime(adapter.today(), 0, -1, 0)).toThrowError(
484+
'Invalid minutes "-1". Minutes value must be between 0 and 59.',
485+
);
486+
expect(() => adapter.setTime(adapter.today(), 0, 65, 0)).toThrowError(
487+
'Invalid minutes "65". Minutes value must be between 0 and 59.',
488+
);
489+
});
490+
491+
it('should throw when passing in invalid seconds to setTime', () => {
492+
expect(() => adapter.setTime(adapter.today(), 0, 0, -1)).toThrowError(
493+
'Invalid seconds "-1". Seconds value must be between 0 and 59.',
494+
);
495+
expect(() => adapter.setTime(adapter.today(), 0, 0, 65)).toThrowError(
496+
'Invalid seconds "65". Seconds value must be between 0 and 59.',
497+
);
498+
});
499+
500+
it('should parse a 24-hour time string', () => {
501+
adapter.setLocale(da);
502+
const result = adapter.parseTime('14:52', 'p')!;
503+
expect(result).toBeTruthy();
504+
expect(adapter.isValid(result)).toBe(true);
505+
expect(adapter.getHours(result)).toBe(14);
506+
expect(adapter.getMinutes(result)).toBe(52);
507+
expect(adapter.getSeconds(result)).toBe(0);
508+
});
509+
510+
it('should parse a 12-hour time string', () => {
511+
const result = adapter.parseTime('2:52 PM', 'p')!;
512+
expect(result).toBeTruthy();
513+
expect(adapter.isValid(result)).toBe(true);
514+
expect(adapter.getHours(result)).toBe(14);
515+
expect(adapter.getMinutes(result)).toBe(52);
516+
expect(adapter.getSeconds(result)).toBe(0);
517+
});
518+
519+
it('should parse a padded time string', () => {
520+
const result = adapter.parseTime('03:04:05 AM', 'pp')!;
521+
expect(result).toBeTruthy();
522+
expect(adapter.isValid(result)).toBe(true);
523+
expect(adapter.getHours(result)).toBe(3);
524+
expect(adapter.getMinutes(result)).toBe(4);
525+
expect(adapter.getSeconds(result)).toBe(5);
526+
});
527+
528+
it('should parse a time string that uses dot as a separator', () => {
529+
adapter.setLocale(fi);
530+
const result = adapter.parseTime('14.52', 'p')!;
531+
expect(result).toBeTruthy();
532+
expect(adapter.isValid(result)).toBe(true);
533+
expect(adapter.getHours(result)).toBe(14);
534+
expect(adapter.getMinutes(result)).toBe(52);
535+
expect(adapter.getSeconds(result)).toBe(0);
536+
});
537+
538+
it('should return an invalid date when parsing invalid time string', () => {
539+
expect(adapter.isValid(adapter.parseTime('abc', 'p')!)).toBe(false);
540+
expect(adapter.isValid(adapter.parseTime('123', 'p')!)).toBe(false);
541+
expect(adapter.isValid(adapter.parseTime('', 'p')!)).toBe(false);
542+
expect(adapter.isValid(adapter.parseTime(' ', 'p')!)).toBe(false);
543+
expect(adapter.isValid(adapter.parseTime(true, 'p')!)).toBe(false);
544+
expect(adapter.isValid(adapter.parseTime(undefined, 'p')!)).toBe(false);
545+
expect(adapter.isValid(adapter.parseTime('14:52 PM', 'p')!)).toBe(false);
546+
expect(adapter.isValid(adapter.parseTime('24:05', 'p')!)).toBe(false);
547+
expect(adapter.isValid(adapter.parseTime('00:61:05', 'p')!)).toBe(false);
548+
expect(adapter.isValid(adapter.parseTime('14:52:78', 'p')!)).toBe(false);
549+
});
550+
551+
it('should compare times', () => {
552+
const base = [2024, JAN, 1] as const;
553+
554+
expect(
555+
adapter.compareTime(new Date(...base, 12, 0, 0), new Date(...base, 13, 0, 0)),
556+
).toBeLessThan(0);
557+
expect(
558+
adapter.compareTime(new Date(...base, 12, 50, 0), new Date(...base, 12, 51, 0)),
559+
).toBeLessThan(0);
560+
expect(adapter.compareTime(new Date(...base, 1, 2, 3), new Date(...base, 1, 2, 3))).toBe(0);
561+
expect(
562+
adapter.compareTime(new Date(...base, 13, 0, 0), new Date(...base, 12, 0, 0)),
563+
).toBeGreaterThan(0);
564+
expect(
565+
adapter.compareTime(new Date(...base, 12, 50, 11), new Date(...base, 12, 50, 10)),
566+
).toBeGreaterThan(0);
567+
expect(
568+
adapter.compareTime(new Date(...base, 13, 0, 0), new Date(...base, 10, 59, 59)),
569+
).toBeGreaterThan(0);
570+
});
571+
572+
it('should add milliseconds to a date', () => {
573+
const amount = 1234567;
574+
const initial = new Date(2024, JAN, 1, 12, 34, 56);
575+
const result = adapter.addMilliseconds(initial, amount);
576+
expect(result).not.toBe(initial);
577+
expect(result.getTime() - initial.getTime()).toBe(amount);
578+
});
455579
});
456580

457581
describe('DateFnsAdapter with MAT_DATE_LOCALE override', () => {
458582
let adapter: DateAdapter<Date, Locale>;
459583

460-
beforeEach(waitForAsync(() => {
584+
beforeEach(() => {
461585
TestBed.configureTestingModule({
462586
imports: [DateFnsModule],
463587
providers: [{provide: MAT_DATE_LOCALE, useValue: da}],
464588
});
465589

466590
adapter = TestBed.inject(DateAdapter);
467-
}));
591+
});
468592

469593
it('should take the default locale id from the MAT_DATE_LOCALE injection token', () => {
470594
const date = adapter.format(new Date(2017, JAN, 2), 'PP');

src/material-date-fns-adapter/adapter/date-fns-adapter.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@ import {
1414
getYear,
1515
getDate,
1616
getDay,
17+
getHours,
18+
getMinutes,
19+
getSeconds,
20+
set,
1721
getDaysInMonth,
1822
formatISO,
1923
addYears,
2024
addMonths,
2125
addDays,
26+
addMilliseconds,
2227
isValid,
2328
isDate,
2429
format,
@@ -241,4 +246,42 @@ export class DateFnsAdapter extends DateAdapter<Date, Locale> {
241246
invalid(): Date {
242247
return new Date(NaN);
243248
}
249+
250+
override setTime(target: Date, hours: number, minutes: number, seconds: number): Date {
251+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
252+
if (hours < 0 || hours > 23) {
253+
throw Error(`Invalid hours "${hours}". Hours value must be between 0 and 23.`);
254+
}
255+
256+
if (minutes < 0 || minutes > 59) {
257+
throw Error(`Invalid minutes "${minutes}". Minutes value must be between 0 and 59.`);
258+
}
259+
260+
if (seconds < 0 || seconds > 59) {
261+
throw Error(`Invalid seconds "${seconds}". Seconds value must be between 0 and 59.`);
262+
}
263+
}
264+
265+
return set(this.clone(target), {hours, minutes, seconds});
266+
}
267+
268+
override getHours(date: Date): number {
269+
return getHours(date);
270+
}
271+
272+
override getMinutes(date: Date): number {
273+
return getMinutes(date);
274+
}
275+
276+
override getSeconds(date: Date): number {
277+
return getSeconds(date);
278+
}
279+
280+
override parseTime(value: any, parseFormat: string | string[]): Date | null {
281+
return this.parse(value, parseFormat);
282+
}
283+
284+
override addMilliseconds(date: Date, amount: number): Date {
285+
return addMilliseconds(date, amount);
286+
}
244287
}

src/material-date-fns-adapter/adapter/date-fns-formats.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ import {MatDateFormats} from '@angular/material/core';
1111
export const MAT_DATE_FNS_FORMATS: MatDateFormats = {
1212
parse: {
1313
dateInput: 'P',
14+
timeInput: 'p',
1415
},
1516
display: {
1617
dateInput: 'P',
18+
timeInput: 'p',
1719
monthYearLabel: 'LLL uuuu',
1820
dateA11yLabel: 'PP',
1921
monthYearA11yLabel: 'LLLL uuuu',
22+
timeOptionLabel: 'p',
2023
},
2124
};

0 commit comments

Comments
 (0)