Skip to content

Commit 6d828cf

Browse files
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 |
1 parent 5b3e846 commit 6d828cf

File tree

3 files changed

+66
-3
lines changed

3 files changed

+66
-3
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()).toEqual(false);
496+
expect(adapter.parse('1/2/2017', 'MM/DD/YYYY')!.isValid()).toEqual(false);
497+
expect(adapter.parse('Jan 5, 2017', 'MMMM D, YYYY')!.isValid()).toEqual(false);
498+
});
499+
500+
});
466501
});

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,27 @@
88

99
import {Inject, Injectable, Optional, InjectionToken} from '@angular/core';
1010
import {DateAdapter, MAT_DATE_LOCALE} from '@angular/material/core';
11+
import {MomentFormatSpecification} from 'moment';
1112
// Depending on whether rollup is used, moment needs to be imported differently.
1213
// Since Moment.js doesn't have a default export, we normally need to import using the `* as`
1314
// syntax. However, rollup creates a synthetic default module and we thus need to import it using
1415
// the `default as` syntax.
1516
// TODO(mmalerba): See if we can clean this up at some point.
1617
import * as _moment from 'moment';
1718
// tslint:disable-next-line:no-duplicate-imports
18-
import {default as _rollupMoment, Moment} from 'moment';
19+
import {default as _rollupMoment, Moment, MomentInput} from 'moment';
1920

2021
const moment = _rollupMoment || _moment;
2122

2223
/** Configurable options for {@see MomentDateAdapter}. */
2324
export interface MatMomentDateAdapterOptions {
25+
26+
/**
27+
* When enabled, the dates have to match the format exactly.
28+
* See https://momentjs.com/guides/#/parsing/strict-mode/.
29+
*/
30+
strict?: boolean;
31+
2432
/**
2533
* Turns the use of utc dates on or off.
2634
* Changing this will change how Angular Material components like DatePicker output dates.
@@ -241,7 +249,14 @@ export class MomentDateAdapter extends DateAdapter<Moment> {
241249
}
242250

243251
/** 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);
252+
private _createMoment(
253+
date: MomentInput,
254+
format?: MomentFormatSpecification,
255+
locale?: string,
256+
): Moment {
257+
const strict = this._options ? this._options.strict : undefined;
258+
return (this._options && this._options.useUtc)
259+
? moment.utc(date, format, locale, strict)
260+
: moment(date, format, locale, strict);
246261
}
247262
}

src/material/datepicker/datepicker.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,19 @@ By default the `MomentDateAdapter` will creates dates in your time zone specific
257257
})
258258
```
259259

260+
By default the `MomentDateAdapter` will parse dates in a forgiving way. This may result in dates
261+
being parsed incorrectly. You can change the default behaviour to parse dates strictly by providing
262+
the `MAT_MOMENT_DATE_ADAPTER_OPTIONS` and setting it to `strict: true`.
263+
264+
```ts
265+
@NgModule({
266+
imports: [MatDatepickerModule, MatMomentDateModule],
267+
providers: [
268+
{ provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { strict: true } }
269+
]
270+
})
271+
```
272+
260273
It is also possible to create your own `DateAdapter` that works with any date format your app
261274
requires. This is accomplished by subclassing `DateAdapter` and providing your subclass as the
262275
`DateAdapter` implementation. You will also want to make sure that the `MAT_DATE_FORMATS` provided

0 commit comments

Comments
 (0)