@@ -67,6 +67,11 @@ export class NativeDateAdapter extends DateAdapter<Date> {
6767 * Without this `Intl.DateTimeFormat` sometimes chooses the wrong timeZone, which can throw off
6868 * the result. (e.g. in the en-US locale `new Date(1800, 7, 14).toLocaleDateString()`
6969 * will produce `'8/13/1800'`.
70+ *
71+ * TODO(mmalerba): drop this variable. It's not being used in the code right now. We're now
72+ * getting the string representation of a Date object from it's utc representation. We're keeping
73+ * it here for sometime, just for precaution, in case we decide to revert some of these changes
74+ * though.
7075 */
7176 useUtcForDisplay : boolean = true ;
7277
@@ -97,34 +102,35 @@ export class NativeDateAdapter extends DateAdapter<Date> {
97102
98103 getMonthNames ( style : 'long' | 'short' | 'narrow' ) : string [ ] {
99104 if ( SUPPORTS_INTL_API ) {
100- let dtf = new Intl . DateTimeFormat ( this . locale , { month : style } ) ;
101- return range ( 12 , i => this . _stripDirectionalityCharacters ( dtf . format ( new Date ( 2017 , i , 1 ) ) ) ) ;
105+ const dtf = new Intl . DateTimeFormat ( this . locale , { month : style , timeZone : 'utc' } ) ;
106+ return range ( 12 , i =>
107+ this . _stripDirectionalityCharacters ( this . _format ( dtf , new Date ( 2017 , i , 1 ) ) ) ) ;
102108 }
103109 return DEFAULT_MONTH_NAMES [ style ] ;
104110 }
105111
106112 getDateNames ( ) : string [ ] {
107113 if ( SUPPORTS_INTL_API ) {
108- let dtf = new Intl . DateTimeFormat ( this . locale , { day : 'numeric' } ) ;
114+ const dtf = new Intl . DateTimeFormat ( this . locale , { day : 'numeric' , timeZone : 'utc '} ) ;
109115 return range ( 31 , i => this . _stripDirectionalityCharacters (
110- dtf . format ( new Date ( 2017 , 0 , i + 1 ) ) ) ) ;
116+ this . _format ( dtf , new Date ( 2017 , 0 , i + 1 ) ) ) ) ;
111117 }
112118 return DEFAULT_DATE_NAMES ;
113119 }
114120
115121 getDayOfWeekNames ( style : 'long' | 'short' | 'narrow' ) : string [ ] {
116122 if ( SUPPORTS_INTL_API ) {
117- let dtf = new Intl . DateTimeFormat ( this . locale , { weekday : style } ) ;
123+ const dtf = new Intl . DateTimeFormat ( this . locale , { weekday : style , timeZone : 'utc' } ) ;
118124 return range ( 7 , i => this . _stripDirectionalityCharacters (
119- dtf . format ( new Date ( 2017 , 0 , i + 1 ) ) ) ) ;
125+ this . _format ( dtf , new Date ( 2017 , 0 , i + 1 ) ) ) ) ;
120126 }
121127 return DEFAULT_DAY_OF_WEEK_NAMES [ style ] ;
122128 }
123129
124130 getYearName ( date : Date ) : string {
125131 if ( SUPPORTS_INTL_API ) {
126- let dtf = new Intl . DateTimeFormat ( this . locale , { year : 'numeric' } ) ;
127- return this . _stripDirectionalityCharacters ( dtf . format ( date ) ) ;
132+ const dtf = new Intl . DateTimeFormat ( this . locale , { year : 'numeric' , timeZone : 'utc '} ) ;
133+ return this . _stripDirectionalityCharacters ( this . _format ( dtf , date ) ) ;
128134 }
129135 return String ( this . getYear ( date ) ) ;
130136 }
@@ -155,7 +161,6 @@ export class NativeDateAdapter extends DateAdapter<Date> {
155161 }
156162
157163 let result = this . _createDateWithOverflow ( year , month , date ) ;
158-
159164 // Check that the date wasn't above the upper bound for the month, causing the month to overflow
160165 if ( result . getMonth ( ) != month ) {
161166 throw Error ( `Invalid date "${ date } " for month with index "${ month } ".` ) ;
@@ -190,15 +195,10 @@ export class NativeDateAdapter extends DateAdapter<Date> {
190195 date . setFullYear ( Math . max ( 1 , Math . min ( 9999 , date . getFullYear ( ) ) ) ) ;
191196 }
192197
193- if ( this . useUtcForDisplay ) {
194- date = new Date ( Date . UTC (
195- date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) , date . getHours ( ) ,
196- date . getMinutes ( ) , date . getSeconds ( ) , date . getMilliseconds ( ) ) ) ;
197- displayFormat = { ...displayFormat , timeZone : 'utc' } ;
198- }
198+ displayFormat = { ...displayFormat , timeZone : 'utc' } ;
199199
200200 const dtf = new Intl . DateTimeFormat ( this . locale , displayFormat ) ;
201- return this . _stripDirectionalityCharacters ( dtf . format ( date ) ) ;
201+ return this . _stripDirectionalityCharacters ( this . _format ( dtf , date ) ) ;
202202 }
203203 return this . _stripDirectionalityCharacters ( date . toDateString ( ) ) ;
204204 }
@@ -271,7 +271,7 @@ export class NativeDateAdapter extends DateAdapter<Date> {
271271
272272 /** Creates a date but allows the month and date to overflow. */
273273 private _createDateWithOverflow ( year : number , month : number , date : number ) {
274- let result = new Date ( year , month , date ) ;
274+ const result = new Date ( year , month , date ) ;
275275
276276 // We need to correct for the fact that JS native Date treats years in range [0, 99] as
277277 // abbreviations for 19xx.
@@ -300,4 +300,22 @@ export class NativeDateAdapter extends DateAdapter<Date> {
300300 private _stripDirectionalityCharacters ( str : string ) {
301301 return str . replace ( / [ \u200e \u200f ] / g, '' ) ;
302302 }
303+
304+ /**
305+ * When converting Date object to string, javascript built-in functions may return wrong
306+ * results because it applies its internal DST rules. The DST rules around the world change
307+ * very frequently, and the current valid rule is not always valid in previous years though.
308+ * We work around this problem building a new Date object which has its internal UTC
309+ * representation with the local date and time.
310+ * @param dtf Intl.DateTimeFormat object, containg the desired string format. It must have
311+ * timeZone set to 'utc' to work fine.
312+ * @param date Date from which we want to get the string representation according to dtf
313+ * @returns A Date object with its UTC representation based on the passed in date info
314+ */
315+ private _format ( dtf : Intl . DateTimeFormat , date : Date ) {
316+ const d = new Date ( Date . UTC (
317+ date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) , date . getHours ( ) ,
318+ date . getMinutes ( ) , date . getSeconds ( ) , date . getMilliseconds ( ) ) ) ;
319+ return dtf . format ( d ) ;
320+ }
303321}
0 commit comments