diff --git a/src/material/core/datetime/native-date-adapter.spec.ts b/src/material/core/datetime/native-date-adapter.spec.ts index 8e7040fa78f6..f502bd2d539b 100644 --- a/src/material/core/datetime/native-date-adapter.spec.ts +++ b/src/material/core/datetime/native-date-adapter.spec.ts @@ -1,14 +1,10 @@ -import {Platform} from '@angular/cdk/platform'; import {LOCALE_ID} from '@angular/core'; import {waitForAsync, inject, TestBed} from '@angular/core/testing'; import {DEC, FEB, JAN, MAR} from '../../testing'; import {DateAdapter, MAT_DATE_LOCALE, NativeDateAdapter, NativeDateModule} from './index'; -const SUPPORTS_INTL = typeof Intl != 'undefined'; - describe('NativeDateAdapter', () => { - let platform: Platform; let adapter: NativeDateAdapter; let assertValidDate: (d: Date | null, valid: boolean) => void; @@ -18,10 +14,8 @@ describe('NativeDateAdapter', () => { }).compileComponents(); })); - beforeEach(inject([DateAdapter, Platform], - (dateAdapter: NativeDateAdapter, _platform: Platform) => { + beforeEach(inject([DateAdapter], (dateAdapter: NativeDateAdapter) => { adapter = dateAdapter; - platform = _platform; assertValidDate = (d: Date | null, valid: boolean) => { expect(adapter.isDateInstance(d)).not @@ -62,30 +56,16 @@ describe('NativeDateAdapter', () => { }); it('should get narrow month names', () => { - // Edge & IE use same value for short and narrow. - if (platform.EDGE || platform.TRIDENT) { - expect(adapter.getMonthNames('narrow')).toEqual([ - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' - ]); - } else { - expect(adapter.getMonthNames('narrow')).toEqual([ - 'J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D' - ]); - } + expect(adapter.getMonthNames('narrow')).toEqual([ + 'J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D' + ]); }); it('should get month names in a different locale', () => { adapter.setLocale('ja-JP'); - if (SUPPORTS_INTL) { - expect(adapter.getMonthNames('long')).toEqual([ - '1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月' - ]); - } else { - expect(adapter.getMonthNames('long')).toEqual([ - 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', - 'October', 'November', 'December' - ]); - } + expect(adapter.getMonthNames('long')).toEqual([ + '1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月' + ]); }); it('should get date names', () => { @@ -97,18 +77,11 @@ describe('NativeDateAdapter', () => { it('should get date names in a different locale', () => { adapter.setLocale('ja-JP'); - if (SUPPORTS_INTL) { - expect(adapter.getDateNames()).toEqual([ - '1日', '2日', '3日', '4日', '5日', '6日', '7日', '8日', '9日', '10日', '11日', '12日', - '13日', '14日', '15日', '16日', '17日', '18日', '19日', '20日', '21日', '22日', '23日', '24日', - '25日', '26日', '27日', '28日', '29日', '30日', '31日' - ]); - } else { - expect(adapter.getDateNames()).toEqual([ - '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', - '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31' - ]); - } + expect(adapter.getDateNames()).toEqual([ + '1日', '2日', '3日', '4日', '5日', '6日', '7日', '8日', '9日', '10日', '11日', '12日', + '13日', '14日', '15日', '16日', '17日', '18日', '19日', '20日', '21日', '22日', '23日', '24日', + '25日', '26日', '27日', '28日', '29日', '30日', '31日' + ]); }); it('should get long day of week names', () => { @@ -124,29 +97,16 @@ describe('NativeDateAdapter', () => { }); it('should get narrow day of week names', () => { - // Edge & IE use two-letter narrow days. - if (platform.EDGE || platform.TRIDENT) { - expect(adapter.getDayOfWeekNames('narrow')).toEqual([ - 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa' - ]); - } else { - expect(adapter.getDayOfWeekNames('narrow')).toEqual([ - 'S', 'M', 'T', 'W', 'T', 'F', 'S' - ]); - } + expect(adapter.getDayOfWeekNames('narrow')).toEqual([ + 'S', 'M', 'T', 'W', 'T', 'F', 'S' + ]); }); it('should get day of week names in a different locale', () => { adapter.setLocale('ja-JP'); - if (SUPPORTS_INTL) { - expect(adapter.getDayOfWeekNames('long')).toEqual([ - '日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日' - ]); - } else { - expect(adapter.getDayOfWeekNames('long')).toEqual([ - 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' - ]); - } + expect(adapter.getDayOfWeekNames('long')).toEqual([ + '日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日' + ]); }); it('should get year name', () => { @@ -165,11 +125,7 @@ describe('NativeDateAdapter', () => { it('should get year name in a different locale', () => { adapter.setLocale('ja-JP'); - if (SUPPORTS_INTL) { - expect(adapter.getYearName(new Date(2017, JAN, 1))).toBe('2017年'); - } else { - expect(adapter.getYearName(new Date(2017, JAN, 1))).toBe('2017'); - } + expect(adapter.getYearName(new Date(2017, JAN, 1))).toBe('2017年'); }); it('should get first day of week', () => { @@ -203,15 +159,9 @@ describe('NativeDateAdapter', () => { return adapter.format(adapter.createDate(year, JAN, 1), {}); }; - if (SUPPORTS_INTL) { - expect(createAndFormat(50)).toBe('1/1/50'); - expect(createAndFormat(99)).toBe('1/1/99'); - expect(createAndFormat(100)).toBe('1/1/100'); - } else { - expect(createAndFormat(50)).toBe('Sat Jan 01 0050'); - expect(createAndFormat(99)).toBe('Thu Jan 01 0099'); - expect(createAndFormat(100)).toBe('Fri Jan 01 0100'); - } + expect(createAndFormat(50)).toBe('1/1/50'); + expect(createAndFormat(99)).toBe('1/1/99'); + expect(createAndFormat(100)).toBe('1/1/100'); }); it("should get today's date", () => { @@ -244,41 +194,20 @@ describe('NativeDateAdapter', () => { }); it('should format', () => { - if (SUPPORTS_INTL) { - expect(adapter.format(new Date(2017, JAN, 1), {})).toEqual('1/1/2017'); - } else { - expect(adapter.format(new Date(2017, JAN, 1), {})).toEqual('Sun Jan 01 2017'); - } + expect(adapter.format(new Date(2017, JAN, 1), {})).toEqual('1/1/2017'); }); it('should format with custom format', () => { - if (SUPPORTS_INTL) { - expect(adapter.format(new Date(2017, JAN, 1), { - year: 'numeric', - month: 'long', - day: 'numeric' - })).toEqual('January 1, 2017'); - } else { - expect(adapter.format(new Date(2017, JAN, 1), { - year: 'numeric', - month: 'long', - day: 'numeric' - })).toEqual('Sun Jan 01 2017'); - } + expect(adapter.format(new Date(2017, JAN, 1), { + year: 'numeric', + month: 'long', + day: 'numeric' + })).toEqual('January 1, 2017'); }); it('should format with a different locale', () => { adapter.setLocale('ja-JP'); - if (SUPPORTS_INTL) { - // Edge & IE use a different format in Japanese. - if (platform.EDGE || platform.TRIDENT) { - expect(adapter.format(new Date(2017, JAN, 1), {})).toEqual('2017年1月1日'); - } else { - expect(adapter.format(new Date(2017, JAN, 1), {})).toEqual('2017/1/1'); - } - } else { - expect(adapter.format(new Date(2017, JAN, 1), {})).toEqual('Sun Jan 01 2017'); - } + expect(adapter.format(new Date(2017, JAN, 1), {})).toEqual('2017/1/1'); }); it('should throw when attempting to format invalid date', () => { @@ -352,11 +281,7 @@ describe('NativeDateAdapter', () => { }); it('should use UTC for formatting by default', () => { - if (SUPPORTS_INTL) { - expect(adapter.format(new Date(1800, 7, 14), {day: 'numeric'})).toBe('14'); - } else { - expect(adapter.format(new Date(1800, 7, 14), {day: 'numeric'})).toBe('Thu Aug 14 1800'); - } + expect(adapter.format(new Date(1800, 7, 14), {day: 'numeric'})).toBe('14'); }); it('should count today as a valid date instance', () => { @@ -432,10 +357,7 @@ describe('NativeDateAdapter with MAT_DATE_LOCALE override', () => { })); it('should take the default locale id from the MAT_DATE_LOCALE injection token', () => { - const expectedValue = SUPPORTS_INTL ? - ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag'] : - ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; - + const expectedValue = ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag']; expect(adapter.getDayOfWeekNames('long')).toEqual(expectedValue); }); @@ -456,10 +378,7 @@ describe('NativeDateAdapter with LOCALE_ID override', () => { })); it('should cascade locale id from the LOCALE_ID injection token to MAT_DATE_LOCALE', () => { - const expectedValue = SUPPORTS_INTL ? - ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag'] : - ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; - + const expectedValue = ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag']; expect(adapter.getDayOfWeekNames('long')).toEqual(expectedValue); }); }); diff --git a/src/material/core/datetime/native-date-adapter.ts b/src/material/core/datetime/native-date-adapter.ts index 0ed5b3c54b2a..e69a1d7f22c1 100644 --- a/src/material/core/datetime/native-date-adapter.ts +++ b/src/material/core/datetime/native-date-adapter.ts @@ -10,44 +10,6 @@ import {Platform} from '@angular/cdk/platform'; import {Inject, Injectable, Optional} from '@angular/core'; import {DateAdapter, MAT_DATE_LOCALE} from './date-adapter'; -// TODO(mmalerba): Remove when we no longer support safari 9. -/** Whether the browser supports the Intl API. */ -let SUPPORTS_INTL_API: boolean; - -// We need a try/catch around the reference to `Intl`, because accessing it in some cases can -// cause IE to throw. These cases are tied to particular versions of Windows and can happen if -// the consumer is providing a polyfilled `Map`. See: -// https://github.com/Microsoft/ChakraCore/issues/3189 -// https://github.com/angular/components/issues/15687 -try { - SUPPORTS_INTL_API = typeof Intl != 'undefined'; -} catch { - SUPPORTS_INTL_API = false; -} - -/** The default month names to use if Intl API is not available. */ -const DEFAULT_MONTH_NAMES = { - 'long': [ - 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', - 'October', 'November', 'December' - ], - 'short': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], - 'narrow': ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'] -}; - - -/** The default date names to use if Intl API is not available. */ -const DEFAULT_DATE_NAMES = range(31, i => String(i + 1)); - - -/** The default day of the week names to use if Intl API is not available. */ -const DEFAULT_DAY_OF_WEEK_NAMES = { - 'long': ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], - 'short': ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], - 'narrow': ['S', 'M', 'T', 'W', 'T', 'F', 'S'] -}; - - /** * Matches strings that have the form of a valid RFC 3339 string * (https://tools.ietf.org/html/rfc3339). Note that the string may not actually be a valid date @@ -69,29 +31,20 @@ function range(length: number, valueFunction: (index: number) => T): T[] { /** Adapts the native JS Date for use with cdk-based components that work with dates. */ @Injectable() export class NativeDateAdapter extends DateAdapter { - /** Whether to clamp the date between 1 and 9999 to avoid IE and Edge errors. */ - private readonly _clampDate: boolean; - /** - * Whether to use `timeZone: 'utc'` with `Intl.DateTimeFormat` when formatting dates. - * Without this `Intl.DateTimeFormat` sometimes chooses the wrong timeZone, which can throw off - * the result. (e.g. in the en-US locale `new Date(1800, 7, 14).toLocaleDateString()` - * will produce `'8/13/1800'`. - * - * TODO(mmalerba): drop this variable. It's not being used in the code right now. We're now - * getting the string representation of a Date object from its utc representation. We're keeping - * it here for sometime, just for precaution, in case we decide to revert some of these changes - * though. + * @deprecated No longer being used. To be removed. + * @breaking-change 14.0.0 */ - useUtcForDisplay: boolean = true; - - constructor(@Optional() @Inject(MAT_DATE_LOCALE) matDateLocale: string, platform: Platform) { + useUtcForDisplay: boolean = false; + + constructor(@Optional() @Inject(MAT_DATE_LOCALE) matDateLocale: string, + /** + * @deprecated No longer being used. To be removed. + * @breaking-change 14.0.0 + */ + _platform?: Platform) { super(); super.setLocale(matDateLocale); - - // IE does its own time zone correction, so we disable this on IE. - this.useUtcForDisplay = !platform.TRIDENT; - this._clampDate = platform.TRIDENT || platform.EDGE; } getYear(date: Date): number { @@ -111,38 +64,23 @@ export class NativeDateAdapter extends DateAdapter { } getMonthNames(style: 'long' | 'short' | 'narrow'): string[] { - if (SUPPORTS_INTL_API) { - const dtf = new Intl.DateTimeFormat(this.locale, {month: style, timeZone: 'utc'}); - return range(12, i => - this._stripDirectionalityCharacters(this._format(dtf, new Date(2017, i, 1)))); - } - return DEFAULT_MONTH_NAMES[style]; + const dtf = new Intl.DateTimeFormat(this.locale, {month: style, timeZone: 'utc'}); + return range(12, i => this._format(dtf, new Date(2017, i, 1))); } getDateNames(): string[] { - if (SUPPORTS_INTL_API) { - const dtf = new Intl.DateTimeFormat(this.locale, {day: 'numeric', timeZone: 'utc'}); - return range(31, i => this._stripDirectionalityCharacters( - this._format(dtf, new Date(2017, 0, i + 1)))); - } - return DEFAULT_DATE_NAMES; + const dtf = new Intl.DateTimeFormat(this.locale, {day: 'numeric', timeZone: 'utc'}); + return range(31, i => this._format(dtf, new Date(2017, 0, i + 1))); } getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] { - if (SUPPORTS_INTL_API) { - const dtf = new Intl.DateTimeFormat(this.locale, {weekday: style, timeZone: 'utc'}); - return range(7, i => this._stripDirectionalityCharacters( - this._format(dtf, new Date(2017, 0, i + 1)))); - } - return DEFAULT_DAY_OF_WEEK_NAMES[style]; + const dtf = new Intl.DateTimeFormat(this.locale, {weekday: style, timeZone: 'utc'}); + return range(7, i => this._format(dtf, new Date(2017, 0, i + 1))); } getYearName(date: Date): string { - if (SUPPORTS_INTL_API) { - const dtf = new Intl.DateTimeFormat(this.locale, {year: 'numeric', timeZone: 'utc'}); - return this._stripDirectionalityCharacters(this._format(dtf, date)); - } - return String(this.getYear(date)); + const dtf = new Intl.DateTimeFormat(this.locale, {year: 'numeric', timeZone: 'utc'}); + return this._format(dtf, date); } getFirstDayOfWeek(): number { @@ -199,20 +137,8 @@ export class NativeDateAdapter extends DateAdapter { throw Error('NativeDateAdapter: Cannot format invalid date.'); } - if (SUPPORTS_INTL_API) { - // On IE and Edge the i18n API will throw a hard error that can crash the entire app - // if we attempt to format a date whose year is less than 1 or greater than 9999. - if (this._clampDate && (date.getFullYear() < 1 || date.getFullYear() > 9999)) { - date = this.clone(date); - date.setFullYear(Math.max(1, Math.min(9999, date.getFullYear()))); - } - - displayFormat = {...displayFormat, timeZone: 'utc'}; - - const dtf = new Intl.DateTimeFormat(this.locale, displayFormat); - return this._stripDirectionalityCharacters(this._format(dtf, date)); - } - return this._stripDirectionalityCharacters(date.toDateString()); + const dtf = new Intl.DateTimeFormat(this.locale, {...displayFormat, timeZone: 'utc'}); + return this._format(dtf, date); } addCalendarYears(date: Date, years: number): Date { @@ -300,17 +226,6 @@ export class NativeDateAdapter extends DateAdapter { return ('00' + n).slice(-2); } - /** - * Strip out unicode LTR and RTL characters. Edge and IE insert these into formatted dates while - * other browsers do not. We remove them to make output consistent and because they interfere with - * date parsing. - * @param str The string to strip direction characters from. - * @returns The stripped string. - */ - private _stripDirectionalityCharacters(str: string) { - return str.replace(/[\u200e\u200f]/g, ''); - } - /** * When converting Date object to string, javascript built-in functions may return wrong * results because it applies its internal DST rules. The DST rules around the world change diff --git a/tools/public_api_guard/material/core.md b/tools/public_api_guard/material/core.md index 1c71c84fbab8..4bd3a10e2adf 100644 --- a/tools/public_api_guard/material/core.md +++ b/tools/public_api_guard/material/core.md @@ -441,7 +441,8 @@ export function mixinTabIndex>(base: T // @public export class NativeDateAdapter extends DateAdapter { - constructor(matDateLocale: string, platform: Platform); + constructor(matDateLocale: string, + _platform?: Platform); // (undocumented) addCalendarDays(date: Date, days: number): Date; // (undocumented) @@ -487,6 +488,7 @@ export class NativeDateAdapter extends DateAdapter { today(): Date; // (undocumented) toIso8601(date: Date): string; + // @deprecated (undocumented) useUtcForDisplay: boolean; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration;