@@ -66,6 +66,11 @@ export class NativeDateAdapter extends DateAdapter<Date> {
6666 * Without this `Intl.DateTimeFormat` sometimes chooses the wrong timeZone, which can throw off
6767 * the result. (e.g. in the en-US locale `new Date(1800, 7, 14).toLocaleDateString()`
6868 * will produce `'8/13/1800'`.
69+ *
70+ * TODO(mmalerba): drop this variable. It's not being used in the code right now. We're now
71+ * getting the string representation of a Date object from it's utc representation. We're keeping
72+ * it here for sometime, just for precaution, in case we decide to revert some of these changes
73+ * though.
6974 */
7075 useUtcForDisplay : boolean ;
7176
@@ -101,34 +106,35 @@ export class NativeDateAdapter extends DateAdapter<Date> {
101106
102107 getMonthNames ( style : 'long' | 'short' | 'narrow' ) : string [ ] {
103108 if ( SUPPORTS_INTL_API ) {
104- let dtf = new Intl . DateTimeFormat ( this . locale , { month : style } ) ;
105- return range ( 12 , i => this . _stripDirectionalityCharacters ( dtf . format ( new Date ( 2017 , i , 1 ) ) ) ) ;
109+ const dtf = new Intl . DateTimeFormat ( this . locale , { month : style , timeZone : 'utc' } ) ;
110+ return range ( 12 , i =>
111+ this . _stripDirectionalityCharacters ( this . _format ( dtf , new Date ( 2017 , i , 1 ) ) ) ) ;
106112 }
107113 return DEFAULT_MONTH_NAMES [ style ] ;
108114 }
109115
110116 getDateNames ( ) : string [ ] {
111117 if ( SUPPORTS_INTL_API ) {
112- let dtf = new Intl . DateTimeFormat ( this . locale , { day : 'numeric' } ) ;
118+ const dtf = new Intl . DateTimeFormat ( this . locale , { day : 'numeric' , timeZone : 'utc '} ) ;
113119 return range ( 31 , i => this . _stripDirectionalityCharacters (
114- dtf . format ( new Date ( 2017 , 0 , i + 1 ) ) ) ) ;
120+ this . _format ( dtf , new Date ( 2017 , 0 , i + 1 ) ) ) ) ;
115121 }
116122 return DEFAULT_DATE_NAMES ;
117123 }
118124
119125 getDayOfWeekNames ( style : 'long' | 'short' | 'narrow' ) : string [ ] {
120126 if ( SUPPORTS_INTL_API ) {
121- let dtf = new Intl . DateTimeFormat ( this . locale , { weekday : style } ) ;
127+ const dtf = new Intl . DateTimeFormat ( this . locale , { weekday : style , timeZone : 'utc' } ) ;
122128 return range ( 7 , i => this . _stripDirectionalityCharacters (
123- dtf . format ( new Date ( 2017 , 0 , i + 1 ) ) ) ) ;
129+ this . _format ( dtf , new Date ( 2017 , 0 , i + 1 ) ) ) ) ;
124130 }
125131 return DEFAULT_DAY_OF_WEEK_NAMES [ style ] ;
126132 }
127133
128134 getYearName ( date : Date ) : string {
129135 if ( SUPPORTS_INTL_API ) {
130- let dtf = new Intl . DateTimeFormat ( this . locale , { year : 'numeric' } ) ;
131- return this . _stripDirectionalityCharacters ( dtf . format ( date ) ) ;
136+ const dtf = new Intl . DateTimeFormat ( this . locale , { year : 'numeric' , timeZone : 'utc '} ) ;
137+ return this . _stripDirectionalityCharacters ( this . _format ( dtf , date ) ) ;
132138 }
133139 return String ( this . getYear ( date ) ) ;
134140 }
@@ -159,7 +165,6 @@ export class NativeDateAdapter extends DateAdapter<Date> {
159165 }
160166
161167 let result = this . _createDateWithOverflow ( year , month , date ) ;
162-
163168 // Check that the date wasn't above the upper bound for the month, causing the month to overflow
164169 if ( result . getMonth ( ) != month ) {
165170 throw Error ( `Invalid date "${ date } " for month with index "${ month } ".` ) ;
@@ -194,15 +199,10 @@ export class NativeDateAdapter extends DateAdapter<Date> {
194199 date . setFullYear ( Math . max ( 1 , Math . min ( 9999 , date . getFullYear ( ) ) ) ) ;
195200 }
196201
197- if ( this . useUtcForDisplay ) {
198- date = new Date ( Date . UTC (
199- date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) , date . getHours ( ) ,
200- date . getMinutes ( ) , date . getSeconds ( ) , date . getMilliseconds ( ) ) ) ;
201- displayFormat = { ...displayFormat , timeZone : 'utc' } ;
202- }
202+ displayFormat = { ...displayFormat , timeZone : 'utc' } ;
203203
204204 const dtf = new Intl . DateTimeFormat ( this . locale , displayFormat ) ;
205- return this . _stripDirectionalityCharacters ( dtf . format ( date ) ) ;
205+ return this . _stripDirectionalityCharacters ( this . _format ( dtf , date ) ) ;
206206 }
207207 return this . _stripDirectionalityCharacters ( date . toDateString ( ) ) ;
208208 }
@@ -275,7 +275,7 @@ export class NativeDateAdapter extends DateAdapter<Date> {
275275
276276 /** Creates a date but allows the month and date to overflow. */
277277 private _createDateWithOverflow ( year : number , month : number , date : number ) {
278- let result = new Date ( year , month , date ) ;
278+ const result = new Date ( year , month , date ) ;
279279
280280 // We need to correct for the fact that JS native Date treats years in range [0, 99] as
281281 // abbreviations for 19xx.
@@ -304,4 +304,22 @@ export class NativeDateAdapter extends DateAdapter<Date> {
304304 private _stripDirectionalityCharacters ( str : string ) {
305305 return str . replace ( / [ \u200e \u200f ] / g, '' ) ;
306306 }
307+
308+ /**
309+ * When converting Date object to string, javascript built-in functions may return wrong
310+ * results because it applies its internal DST rules. The DST rules around the world change
311+ * very frequently, and the current valid rule is not always valid in previous years though.
312+ * We work around this problem building a new Date object which has its internal UTC
313+ * representation with the local date and time.
314+ * @param dtf Intl.DateTimeFormat object, containg the desired string format. It must have
315+ * timeZone set to 'utc' to work fine.
316+ * @param date Date from which we want to get the string representation according to dtf
317+ * @returns A Date object with its UTC representation based on the passed in date info
318+ */
319+ private _format ( dtf : Intl . DateTimeFormat , date : Date ) {
320+ const d = new Date ( Date . UTC (
321+ date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) , date . getHours ( ) ,
322+ date . getMinutes ( ) , date . getSeconds ( ) , date . getMilliseconds ( ) ) ) ;
323+ return dtf . format ( d ) ;
324+ }
307325}
0 commit comments