Skip to content

Commit 0e812f2

Browse files
committed
refactor(material-moment-adapter): implement new methods
Implements the new methods in the Moment adapter.
1 parent 78ed8b8 commit 0e812f2

File tree

3 files changed

+195
-30
lines changed

3 files changed

+195
-30
lines changed

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

Lines changed: 154 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {LOCALE_ID} from '@angular/core';
10-
import {waitForAsync, inject, TestBed} from '@angular/core/testing';
10+
import {TestBed} from '@angular/core/testing';
1111
import {DateAdapter, MAT_DATE_LOCALE} from '@angular/material/core';
1212
import {DEC, FEB, JAN, MAR} from '../../material/testing';
1313
import {MomentDateModule} from './index';
@@ -22,15 +22,10 @@ describe('MomentDateAdapter', () => {
2222
let adapter: MomentDateAdapter;
2323
let assertValidDate: (d: moment.Moment | null, valid: boolean) => void;
2424

25-
beforeEach(waitForAsync(() => {
26-
TestBed.configureTestingModule({
27-
imports: [MomentDateModule],
28-
});
29-
}));
30-
31-
beforeEach(inject([DateAdapter], (dateAdapter: MomentDateAdapter) => {
25+
beforeEach(() => {
26+
TestBed.configureTestingModule({imports: [MomentDateModule]});
3227
moment.locale('en');
33-
adapter = dateAdapter;
28+
adapter = TestBed.inject(DateAdapter) as MomentDateAdapter;
3429
adapter.setLocale('en');
3530

3631
assertValidDate = (d: moment.Moment | null, valid: boolean) => {
@@ -44,7 +39,7 @@ describe('MomentDateAdapter', () => {
4439
)
4540
.toBe(valid);
4641
};
47-
}));
42+
});
4843

4944
it('should get year', () => {
5045
expect(adapter.getYear(moment([2017, JAN, 1]))).toBe(2017);
@@ -534,21 +529,156 @@ describe('MomentDateAdapter', () => {
534529
it('should create invalid date', () => {
535530
assertValidDate(adapter.invalid(), false);
536531
});
532+
533+
it('should get hours', () => {
534+
expect(adapter.getHours(moment([2024, JAN, 1, 14]))).toBe(14);
535+
});
536+
537+
it('should get minutes', () => {
538+
expect(adapter.getMinutes(moment([2024, JAN, 1, 14, 53]))).toBe(53);
539+
});
540+
541+
it('should get seconds', () => {
542+
expect(adapter.getSeconds(moment([2024, JAN, 1, 14, 53, 42]))).toBe(42);
543+
});
544+
545+
it('should set the time of a date', () => {
546+
const target = moment([2024, JAN, 1, 0, 0, 0]);
547+
const result = adapter.setTime(target, 14, 53, 42);
548+
expect(adapter.getHours(result)).toBe(14);
549+
expect(adapter.getMinutes(result)).toBe(53);
550+
expect(adapter.getSeconds(result)).toBe(42);
551+
});
552+
553+
it('should throw when passing in invalid hours to setTime', () => {
554+
expect(() => adapter.setTime(adapter.today(), -1, 0, 0)).toThrowError(
555+
'Invalid hours "-1". Hours value must be between 0 and 23.',
556+
);
557+
expect(() => adapter.setTime(adapter.today(), 51, 0, 0)).toThrowError(
558+
'Invalid hours "51". Hours value must be between 0 and 23.',
559+
);
560+
});
561+
562+
it('should throw when passing in invalid minutes to setTime', () => {
563+
expect(() => adapter.setTime(adapter.today(), 0, -1, 0)).toThrowError(
564+
'Invalid minutes "-1". Minutes value must be between 0 and 59.',
565+
);
566+
expect(() => adapter.setTime(adapter.today(), 0, 65, 0)).toThrowError(
567+
'Invalid minutes "65". Minutes value must be between 0 and 59.',
568+
);
569+
});
570+
571+
it('should throw when passing in invalid seconds to setTime', () => {
572+
expect(() => adapter.setTime(adapter.today(), 0, 0, -1)).toThrowError(
573+
'Invalid seconds "-1". Seconds value must be between 0 and 59.',
574+
);
575+
expect(() => adapter.setTime(adapter.today(), 0, 0, 65)).toThrowError(
576+
'Invalid seconds "65". Seconds value must be between 0 and 59.',
577+
);
578+
});
579+
580+
it('should parse a 24-hour time string', () => {
581+
const result = adapter.parseTime('14:52', 'LT')!;
582+
expect(result).toBeTruthy();
583+
expect(adapter.isValid(result)).toBe(true);
584+
expect(adapter.getHours(result)).toBe(14);
585+
expect(adapter.getMinutes(result)).toBe(52);
586+
expect(adapter.getSeconds(result)).toBe(0);
587+
});
588+
589+
it('should parse a 12-hour time string', () => {
590+
const result = adapter.parseTime('2:52 PM', 'LT')!;
591+
expect(result).toBeTruthy();
592+
expect(adapter.isValid(result)).toBe(true);
593+
expect(adapter.getHours(result)).toBe(14);
594+
expect(adapter.getMinutes(result)).toBe(52);
595+
expect(adapter.getSeconds(result)).toBe(0);
596+
});
597+
598+
it('should parse a padded time string', () => {
599+
const result = adapter.parseTime('03:04:05', 'LTS')!;
600+
expect(result).toBeTruthy();
601+
expect(adapter.isValid(result)).toBe(true);
602+
expect(adapter.getHours(result)).toBe(3);
603+
expect(adapter.getMinutes(result)).toBe(4);
604+
expect(adapter.getSeconds(result)).toBe(5);
605+
});
606+
607+
it('should parse a time string that uses dot as a separator', () => {
608+
adapter.setLocale('fi-FI');
609+
const result = adapter.parseTime('14.52', 'LT')!;
610+
expect(result).toBeTruthy();
611+
expect(adapter.isValid(result)).toBe(true);
612+
expect(adapter.getHours(result)).toBe(14);
613+
expect(adapter.getMinutes(result)).toBe(52);
614+
expect(adapter.getSeconds(result)).toBe(0);
615+
});
616+
617+
it('should parse a time string with characters around the time', () => {
618+
adapter.setLocale('bg-BG');
619+
const result = adapter.parseTime('14:52 ч.', 'LT')!;
620+
expect(result).toBeTruthy();
621+
expect(adapter.isValid(result)).toBe(true);
622+
expect(adapter.getHours(result)).toBe(14);
623+
expect(adapter.getMinutes(result)).toBe(52);
624+
expect(adapter.getSeconds(result)).toBe(0);
625+
});
626+
627+
it('should return an invalid date when parsing invalid time string', () => {
628+
expect(adapter.isValid(adapter.parseTime('abc', 'LT')!)).toBeFalse();
629+
expect(adapter.isValid(adapter.parseTime(' ', 'LT')!)).toBeFalse();
630+
expect(adapter.isValid(adapter.parseTime(true, 'LT')!)).toBeFalse();
631+
expect(adapter.isValid(adapter.parseTime('24:05', 'LT')!)).toBeFalse();
632+
expect(adapter.isValid(adapter.parseTime('00:61:05', 'LTS')!)).toBeFalse();
633+
expect(adapter.isValid(adapter.parseTime('14:52:78', 'LTS')!)).toBeFalse();
634+
});
635+
636+
it('should return null when parsing unsupported time values', () => {
637+
expect(adapter.parseTime(undefined, 'LT')).toBeNull();
638+
expect(adapter.parseTime('', 'LT')).toBeNull();
639+
});
640+
641+
it('should compare times', () => {
642+
const base = [2024, JAN, 1] as const;
643+
644+
expect(
645+
adapter.compareTime(moment([...base, 12, 0, 0]), moment([...base, 13, 0, 0])),
646+
).toBeLessThan(0);
647+
expect(
648+
adapter.compareTime(moment([...base, 12, 50, 0]), moment([...base, 12, 51, 0])),
649+
).toBeLessThan(0);
650+
expect(adapter.compareTime(moment([...base, 1, 2, 3]), moment([...base, 1, 2, 3]))).toBe(0);
651+
expect(
652+
adapter.compareTime(moment([...base, 13, 0, 0]), moment([...base, 12, 0, 0])),
653+
).toBeGreaterThan(0);
654+
expect(
655+
adapter.compareTime(moment([...base, 12, 50, 11]), moment([...base, 12, 50, 10])),
656+
).toBeGreaterThan(0);
657+
expect(
658+
adapter.compareTime(moment([...base, 13, 0, 0]), moment([...base, 10, 59, 59])),
659+
).toBeGreaterThan(0);
660+
});
661+
662+
it('should add milliseconds to a date', () => {
663+
const amount = 1234567;
664+
const initial = moment([2024, JAN, 1, 12, 34, 56]);
665+
const result = adapter.addMilliseconds(initial, amount);
666+
expect(result).not.toBe(initial);
667+
expect(result.valueOf() - initial.valueOf()).toBe(amount);
668+
});
537669
});
538670

539671
describe('MomentDateAdapter with MAT_DATE_LOCALE override', () => {
540672
let adapter: MomentDateAdapter;
541673

542-
beforeEach(waitForAsync(() => {
674+
beforeEach(() => {
543675
TestBed.configureTestingModule({
544676
imports: [MomentDateModule],
545677
providers: [{provide: MAT_DATE_LOCALE, useValue: 'ja-JP'}],
546678
});
547-
}));
548679

549-
beforeEach(inject([DateAdapter], (d: MomentDateAdapter) => {
550-
adapter = d;
551-
}));
680+
adapter = TestBed.inject(DateAdapter) as MomentDateAdapter;
681+
});
552682

553683
it('should take the default locale id from the MAT_DATE_LOCALE injection token', () => {
554684
expect(adapter.format(moment([2017, JAN, 2]), 'll')).toEqual('2017年1月2日');
@@ -558,16 +688,14 @@ describe('MomentDateAdapter with MAT_DATE_LOCALE override', () => {
558688
describe('MomentDateAdapter with LOCALE_ID override', () => {
559689
let adapter: MomentDateAdapter;
560690

561-
beforeEach(waitForAsync(() => {
691+
beforeEach(() => {
562692
TestBed.configureTestingModule({
563693
imports: [MomentDateModule],
564694
providers: [{provide: LOCALE_ID, useValue: 'fr'}],
565695
});
566-
}));
567696

568-
beforeEach(inject([DateAdapter], (d: MomentDateAdapter) => {
569-
adapter = d;
570-
}));
697+
adapter = TestBed.inject(DateAdapter) as MomentDateAdapter;
698+
});
571699

572700
it('should take the default locale id from the LOCALE_ID injection token', () => {
573701
expect(adapter.format(moment([2017, JAN, 2]), 'll')).toEqual('2 janv. 2017');
@@ -577,7 +705,7 @@ describe('MomentDateAdapter with LOCALE_ID override', () => {
577705
describe('MomentDateAdapter with MAT_MOMENT_DATE_ADAPTER_OPTIONS override', () => {
578706
let adapter: MomentDateAdapter;
579707

580-
beforeEach(waitForAsync(() => {
708+
beforeEach(() => {
581709
TestBed.configureTestingModule({
582710
imports: [MomentDateModule],
583711
providers: [
@@ -587,11 +715,9 @@ describe('MomentDateAdapter with MAT_MOMENT_DATE_ADAPTER_OPTIONS override', () =
587715
},
588716
],
589717
});
590-
}));
591718

592-
beforeEach(inject([DateAdapter], (d: MomentDateAdapter) => {
593-
adapter = d;
594-
}));
719+
adapter = TestBed.inject(DateAdapter) as MomentDateAdapter;
720+
});
595721

596722
describe('use UTC', () => {
597723
it('should create Moment date in UTC', () => {
@@ -612,7 +738,7 @@ describe('MomentDateAdapter with MAT_MOMENT_DATE_ADAPTER_OPTIONS override', () =
612738
});
613739

614740
describe('strict mode', () => {
615-
beforeEach(waitForAsync(() => {
741+
beforeEach(() => {
616742
TestBed.resetTestingModule();
617743
TestBed.configureTestingModule({
618744
imports: [MomentDateModule],
@@ -625,11 +751,9 @@ describe('MomentDateAdapter with MAT_MOMENT_DATE_ADAPTER_OPTIONS override', () =
625751
},
626752
],
627753
});
628-
}));
629754

630-
beforeEach(inject([DateAdapter], (d: MomentDateAdapter) => {
631-
adapter = d;
632-
}));
755+
adapter = TestBed.inject(DateAdapter) as MomentDateAdapter;
756+
});
633757

634758
it('should detect valid strings according to given format', () => {
635759
expect(adapter.parse('1/2/2017', 'D/M/YYYY')!.format('l')).toEqual(

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,44 @@ export class MomentDateAdapter extends DateAdapter<Moment> {
251251
return moment.invalid();
252252
}
253253

254+
override setTime(target: Moment, hours: number, minutes: number, seconds: number): Moment {
255+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
256+
if (hours < 0 || hours > 23) {
257+
throw Error(`Invalid hours "${hours}". Hours value must be between 0 and 23.`);
258+
}
259+
260+
if (minutes < 0 || minutes > 59) {
261+
throw Error(`Invalid minutes "${minutes}". Minutes value must be between 0 and 59.`);
262+
}
263+
264+
if (seconds < 0 || seconds > 59) {
265+
throw Error(`Invalid seconds "${seconds}". Seconds value must be between 0 and 59.`);
266+
}
267+
}
268+
269+
return this.clone(target).set({hours, minutes, seconds});
270+
}
271+
272+
override getHours(date: Moment): number {
273+
return date.hours();
274+
}
275+
276+
override getMinutes(date: Moment): number {
277+
return date.minutes();
278+
}
279+
280+
override getSeconds(date: Moment): number {
281+
return date.seconds();
282+
}
283+
284+
override parseTime(value: any, parseFormat: string | string[]): Moment | null {
285+
return this.parse(value, parseFormat);
286+
}
287+
288+
override addMilliseconds(date: Moment, amount: number): Moment {
289+
return this.clone(date).add({milliseconds: amount});
290+
}
291+
254292
/** Creates a Moment instance while respecting the current UTC settings. */
255293
private _createMoment(
256294
date?: MomentInput,

src/material-moment-adapter/adapter/moment-date-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_MOMENT_DATE_FORMATS: MatDateFormats = {
1212
parse: {
1313
dateInput: 'l',
14+
timeInput: 'LT',
1415
},
1516
display: {
1617
dateInput: 'l',
18+
timeInput: 'LT',
1719
monthYearLabel: 'MMM YYYY',
1820
dateA11yLabel: 'LL',
1921
monthYearA11yLabel: 'MMMM YYYY',
22+
timeOptionLabel: 'LT',
2023
},
2124
};

0 commit comments

Comments
 (0)