Skip to content

Commit dd42956

Browse files
MatthiasKunnenmmalerba
authored andcommitted
feat(moment-date-adapter): implement strict mode (#16462)
* feat(moment-date-adapter): implement strict mode Moment supports a strict flag which solves a lot of parsing errors. Examples: | Input | Format | Forgiving | Strict | |-----------------|--------------|------------|--------------| | 1/2/2017 | M/D/YYYY | 2017-01-02 | 2017-01-02 | | 1/2/2017 | MM/DD/YYYY | 2017-01-02 | Invalid date | | Januari 1, 2017 | MMMM D, YYYY | 2017-01-01 | Invalid date | | Jan 1, 2017 | MMMM D, YYYY | 2017-01-01 | Invalid date | | 2017-1-1 | MMMM D, YYYY | 2017-01-20 | Invalid date | * docs(datepicker): remove spaces in useUtc example * refactor(moment-date-adapter): change toEqual -> ToBe in strict mode boolean tests * refactor(moment-date-adapter): destructure options useUtc has been made optional to prevent the necessity for default values. * docs(datepicker): remove spaces around strict example braces * docs(datepicker): add links to Moment's strict/forgiving mode
1 parent 8658c30 commit dd42956

File tree

3 files changed

+70
-5
lines changed

3 files changed

+70
-5
lines changed

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,4 +463,39 @@ describe('MomentDateAdapter with MAT_MOMENT_DATE_ADAPTER_OPTIONS override', () =
463463
});
464464
});
465465

466+
describe('strict mode', () => {
467+
468+
beforeEach(async(() => {
469+
TestBed.resetTestingModule();
470+
TestBed.configureTestingModule({
471+
imports: [MomentDateModule],
472+
providers: [
473+
{
474+
provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS,
475+
useValue: {
476+
strict: true,
477+
},
478+
},
479+
]
480+
}).compileComponents();
481+
}));
482+
483+
beforeEach(inject([DateAdapter], (d: MomentDateAdapter) => {
484+
adapter = d;
485+
}));
486+
487+
it('should detect valid strings according to given format', () => {
488+
expect(adapter.parse('1/2/2017', 'D/M/YYYY')!.format('l'))
489+
.toEqual(moment([2017, FEB, 1]).format('l'));
490+
expect(adapter.parse('February 1, 2017', 'MMMM D, YYYY')!.format('l'))
491+
.toEqual(moment([2017, FEB, 1]).format('l'));
492+
});
493+
494+
it('should detect invalid strings according to given format', () => {
495+
expect(adapter.parse('2017-01-01', 'MM/DD/YYYY')!.isValid()).toBe(false);
496+
expect(adapter.parse('1/2/2017', 'MM/DD/YYYY')!.isValid()).toBe(false);
497+
expect(adapter.parse('Jan 5, 2017', 'MMMM D, YYYY')!.isValid()).toBe(false);
498+
});
499+
500+
});
466501
});

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

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,25 @@ import {DateAdapter, MAT_DATE_LOCALE} from '@angular/material/core';
1515
// TODO(mmalerba): See if we can clean this up at some point.
1616
import * as _moment from 'moment';
1717
// tslint:disable-next-line:no-duplicate-imports
18-
import {default as _rollupMoment, Moment} from 'moment';
18+
import {default as _rollupMoment, Moment, MomentFormatSpecification, MomentInput} from 'moment';
1919

2020
const moment = _rollupMoment || _moment;
2121

2222
/** Configurable options for {@see MomentDateAdapter}. */
2323
export interface MatMomentDateAdapterOptions {
24+
25+
/**
26+
* When enabled, the dates have to match the format exactly.
27+
* See https://momentjs.com/guides/#/parsing/strict-mode/.
28+
*/
29+
strict?: boolean;
30+
2431
/**
2532
* Turns the use of utc dates on or off.
2633
* Changing this will change how Angular Material components like DatePicker output dates.
2734
* {@default false}
2835
*/
29-
useUtc: boolean;
36+
useUtc?: boolean;
3037
}
3138

3239
/** InjectionToken for moment date adapter to configure options. */
@@ -241,7 +248,15 @@ export class MomentDateAdapter extends DateAdapter<Moment> {
241248
}
242249

243250
/** Creates a Moment instance while respecting the current UTC settings. */
244-
private _createMoment(...args: any[]): Moment {
245-
return (this._options && this._options.useUtc) ? moment.utc(...args) : moment(...args);
251+
private _createMoment(
252+
date: MomentInput,
253+
format?: MomentFormatSpecification,
254+
locale?: string,
255+
): Moment {
256+
const {strict, useUtc}: MatMomentDateAdapterOptions = this._options || {};
257+
258+
return useUtc
259+
? moment.utc(date, format, locale, strict)
260+
: moment(date, format, locale, strict);
246261
}
247262
}

src/material/datepicker/datepicker.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,22 @@ By default the `MomentDateAdapter` will creates dates in your time zone specific
252252
@NgModule({
253253
imports: [MatDatepickerModule, MatMomentDateModule],
254254
providers: [
255-
{ provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } }
255+
{provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: {useUtc: true}}
256+
]
257+
})
258+
```
259+
260+
By default the `MomentDateAdapter` will parse dates in a
261+
[forgiving way](https://momentjs.com/guides/#/parsing/forgiving-mode/). This may result in dates
262+
being parsed incorrectly. You can change the default behaviour to
263+
[parse dates strictly](https://momentjs.com/guides/#/parsing/strict-mode/) by providing
264+
the `MAT_MOMENT_DATE_ADAPTER_OPTIONS` and setting it to `strict: true`.
265+
266+
```ts
267+
@NgModule({
268+
imports: [MatDatepickerModule, MatMomentDateModule],
269+
providers: [
270+
{provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: {strict: true}}
256271
]
257272
})
258273
```

0 commit comments

Comments
 (0)