Skip to content

Commit dd2fbc8

Browse files
crisbetojelbourn
authored andcommitted
fix(native-date-adapter): avoid error when formatting edge case dates in IE11 and Edge (#9523)
Avoids an error that can crash the consumer's app on IE11 and Edge when attempting to format a date whose year is less than 1 or greater than 9999.
1 parent 245caae commit dd2fbc8

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

src/lib/core/datetime/native-date-adapter.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,14 @@ describe('NativeDateAdapter', () => {
359359
it('should create an invalid date', () => {
360360
assertValidDate(adapter.invalid(), false);
361361
});
362+
363+
it('should not throw when attempting to format a date with a year less than 1', () => {
364+
expect(() => adapter.format(new Date(-1, 1, 1), {})).not.toThrow();
365+
});
366+
367+
it('should not throw when attempting to format a date with a year greater than 9999', () => {
368+
expect(() => adapter.format(new Date(10000, 1, 1), {})).not.toThrow();
369+
});
362370
});
363371

364372

src/lib/core/datetime/native-date-adapter.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ function range<T>(length: number, valueFunction: (index: number) => T): T[] {
5858
/** Adapts the native JS Date for use with cdk-based components that work with dates. */
5959
@Injectable()
6060
export class NativeDateAdapter extends DateAdapter<Date> {
61+
/** Whether to clamp the date between 1 and 9999 to avoid IE and Edge errors. */
62+
private readonly _clampDate: boolean;
63+
6164
/**
6265
* Whether to use `timeZone: 'utc'` with `Intl.DateTimeFormat` when formatting dates.
6366
* Without this `Intl.DateTimeFormat` sometimes chooses the wrong timeZone, which can throw off
@@ -71,10 +74,13 @@ export class NativeDateAdapter extends DateAdapter<Date> {
7174
super.setLocale(matDateLocale);
7275

7376
// IE does its own time zone correction, so we disable this on IE.
74-
// TODO(mmalerba): replace with !platform.TRIDENT, logic currently duplicated to avoid breaking
75-
// change from injecting the Platform.
76-
this.useUtcForDisplay = !(typeof document === 'object' && !!document &&
77-
/(msie|trident)/i.test(navigator.userAgent));
77+
// TODO(mmalerba): replace with checks from PLATFORM, logic currently duplicated to avoid
78+
// breaking change from injecting the Platform.
79+
const isBrowser = typeof document === 'object' && !!document;
80+
const isIE = isBrowser && /(msie|trident)/i.test(navigator.userAgent);
81+
82+
this.useUtcForDisplay = !isIE;
83+
this._clampDate = isIE || (isBrowser && /(edge)/i.test(navigator.userAgent));
7884
}
7985

8086
getYear(date: Date): number {
@@ -179,14 +185,23 @@ export class NativeDateAdapter extends DateAdapter<Date> {
179185
if (!this.isValid(date)) {
180186
throw Error('NativeDateAdapter: Cannot format invalid date.');
181187
}
188+
182189
if (SUPPORTS_INTL_API) {
190+
// On IE and Edge the i18n API will throw a hard error that can crash the entire app
191+
// if we attempt to format a date whose year is less than 1 or greater than 9999.
192+
if (this._clampDate && (date.getFullYear() < 1 || date.getFullYear() > 9999)) {
193+
date = this.clone(date);
194+
date.setFullYear(Math.max(1, Math.min(9999, date.getFullYear())));
195+
}
196+
183197
if (this.useUtcForDisplay) {
184198
date = new Date(Date.UTC(
185199
date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(),
186200
date.getMinutes(), date.getSeconds(), date.getMilliseconds()));
187201
displayFormat = {...displayFormat, timeZone: 'utc'};
188202
}
189-
let dtf = new Intl.DateTimeFormat(this.locale, displayFormat);
203+
204+
const dtf = new Intl.DateTimeFormat(this.locale, displayFormat);
190205
return this._stripDirectionalityCharacters(dtf.format(date));
191206
}
192207
return this._stripDirectionalityCharacters(date.toDateString());

0 commit comments

Comments
 (0)