Bug 161623

Summary: toLocaleDateString() resolves incorrect date for some old dates
Product: WebKit Reporter: Mariusz Nowak <medikoo+webkit.org>
Component: JavaScriptCoreAssignee: Yusuke Suzuki <ysuzuki>
Status: RESOLVED FIXED    
Severity: Normal CC: andy, commit-queue, eric, ggaren, joren.broekema, rniwa, webkit-bug-importer, ysuzuki
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Mac   
OS: OS X 10.11   
See Also: https://bugs.webkit.org/show_bug.cgi?id=218604
Bug Depends on: 219915    
Bug Blocks:    
Attachments:
Description Flags
Patch
none
Patch none

Description Mariusz Nowak 2016-09-06 03:57:45 PDT
Running following:

(new Date(1969, 8, 26)).toLocaleDateString()

Results with:

"9/25/1969"

While it should result with:

"9/26/1969"

It goes up to around 1977:

> (new Date(1977, 8, 26)).toLocaleDateString()
< "9/25/1977"
> (new Date(1978, 8, 26)).toLocaleDateString()
< "9/26/1978"

Same issue affects V8, although it gets dates after 1970 right -> https://bugs.chromium.org/p/v8/issues/detail?id=5353
No such issue is observed on Firefox.
Comment 1 Andy VanWagoner 2018-07-27 10:40:22 PDT
This appears to be working now. Please reopen with additional details, like your local timezone, if you are continuing to see this issue.
Comment 2 Mariusz Nowak 2018-07-30 00:36:09 PDT
Bug still exists (nothing changed actually)
We still can observe following output:


> (new Date(1977, 8, 26)).toLocaleDateString()
> "9/25/1977"

In latest:
- Safari Technology Preview (Release 61 (Safari 12.0, WebKit 13606.1.25.1))
- WebKit build archives(Version 11.1.2 (13605.3.8, 607+)
- macOS release (Version 11.1.2 (13605.3.8))
Comment 3 Andy VanWagoner 2018-07-30 07:06:32 PDT
Can you please run the following and comment the output here?

new Intl.DateTimeFormat().resolvedOptions().timeZone
new Date(1969, 8, 26).getTimezoneOffset()

That would be really helpful for me tracking down the issue, since it works properly in my time zone.
Comment 4 Mariusz Nowak 2018-07-30 07:07:57 PDT
Sorry I forgot to mention: I'm in Warsaw/Poland so CET timezone
Comment 5 Andy VanWagoner 2018-07-30 13:57:18 PDT
k, I was able to successfully reproduce the issue by setting my mac to Warsaw time.

new Intl.DateTimeFormat().resolvedOptions().timeZone // "Europe/Warsaw"
new Date(1977, 8, 26) // Mon Sep 26 1977 00:00:00 GMT+0200 (CEST)
new Date(1969, 8, 26).getTimezoneOffset() // -120
(new Date(1977, 8, 26)).toLocaleDateString('en', { hour: 'numeric', minute: 'numeric' }) // "9/25/1977, 11:00 PM"
(new Date(1977, 8, 26, 1)).toLocaleDateString('en', { hour: 'numeric', minute: 'numeric' }) // "9/26/1977, 12:00 AM"

So the locale formatter timezone ends up an hour different than Date constructor.
Chrome and Firefox differ from Safari in the date constructor behavior:

new Date(1977, 8, 26).getTimezoneOffset()
Chrome, Firefox: -60
Safari: -120

new Date(1977, 8, 26).getTime()
Chrome, Firefox: 244076400000
Safari: 244072800000
Comment 6 Andy VanWagoner 2018-07-30 13:57:53 PDT
There is some code that tries to work around using actually accurate DST information, because the ECMAScript spec specifies that behavior.
https://www.ecma-international.org/ecma-262/5.1/index.html#sec-15.9.1.8
"The implementation of ECMAScript should not try to determine whether the exact time was subject to daylight saving time, but just whether daylight saving time would have been in effect if the current daylight saving time algorithm had been used at the time. This avoids complications such as taking into account the years that the locale observed daylight saving time year round."

The ES2015 spec appears to change that expectation:
https://www.ecma-international.org/ecma-262/6.0/index.html#sec-daylight-saving-time-adjustment

And the latest drafts make it pretty explicit that historically accurate timezones should be used.
https://tc39.github.io/ecma262/#sec-local-time-zone-adjustment
"The local political rules for standard time and daylight saving time in effect at *t* should be used to determine the result in the way specified in the following three paragraphs."
Comment 7 Andy VanWagoner 2018-07-30 20:07:35 PDT
Since this isn't just a toLocaleString issue, but a date timezone offset issue, it'd probably be best if someone else took a look at it. My naive attempt to simply remove the likely offending code caused a bunch of other issues I'm not sure how to handle.
Comment 8 Joren Broekema 2020-02-27 04:45:56 PST
I think I was able to reproduce this bug on Safari 13.0.5 for MacOS Mojave 10.14.6, but with Intl.DateTimeFormat. 

console.log(new Intl.DateTimeFormat('en-GB').format(new Date('12/06/1939'))); 

Results in 11/06/1939. So 1 day off.

Strangely, anything between year 1940 and 1945 formats correctly. Then for 1946 up until 1976 it is off by 1 again. 1977 and onwards it seems to be correct, but I didn't check for all values so not 100% sure there.
Comment 9 Joren Broekema 2020-03-04 02:34:46 PST
I looked a bit further.
https://en.wikipedia.org/wiki/Daylight_saving_time#History gives some background on why this is happening for certain years. Also, I don't think Intl is at fault, I have issues with the native Date parsing process

> new Date('1939-12-06')
< Wed Dec 06 1939 01:00:00 GMT+0100 (CET)

> new Date('12/06/1939')
< Wed Dec 06 1939 00:00:00 GMT+0100 (CET)

So it parses it differently based on how you supply the date string.
Comment 10 Yusuke Suzuki 2020-11-05 00:47:36 PST
This is because OS `localtime_r`'s timezone information is broken, and it is the same in Linux etc.
This is fixed by using ICU TimeZone instead in bug 218348.
Comment 11 Yusuke Suzuki 2020-11-05 01:00:14 PST
(In reply to Joren Broekema from comment #9)
> I looked a bit further.
> https://en.wikipedia.org/wiki/Daylight_saving_time#History gives some
> background on why this is happening for certain years. Also, I don't think
> Intl is at fault, I have issues with the native Date parsing process
> 
> > new Date('1939-12-06')
> < Wed Dec 06 1939 01:00:00 GMT+0100 (CET)
> 
> > new Date('12/06/1939')
> < Wed Dec 06 1939 00:00:00 GMT+0100 (CET)
> 
> So it parses it differently based on how you supply the date string.

This is different issue from this bug. I think one format is parsed as local time, an the other is parsed UTC.
Comment 12 Yusuke Suzuki 2020-11-05 01:01:02 PST
(In reply to Yusuke Suzuki from comment #11)
> (In reply to Joren Broekema from comment #9)
> > I looked a bit further.
> > https://en.wikipedia.org/wiki/Daylight_saving_time#History gives some
> > background on why this is happening for certain years. Also, I don't think
> > Intl is at fault, I have issues with the native Date parsing process
> > 
> > > new Date('1939-12-06')
> > < Wed Dec 06 1939 01:00:00 GMT+0100 (CET)
> > 
> > > new Date('12/06/1939')
> > < Wed Dec 06 1939 00:00:00 GMT+0100 (CET)
> > 
> > So it parses it differently based on how you supply the date string.
> 
> This is different issue from this bug. I think one format is parsed as local
> time, an the other is parsed UTC.

https://bugs.webkit.org/show_bug.cgi?id=218604
Comment 13 Yusuke Suzuki 2020-11-05 01:04:10 PST
Created attachment 413271 [details]
Patch
Comment 14 Yusuke Suzuki 2020-11-05 01:06:32 PST
Created attachment 413272 [details]
Patch
Comment 15 EWS 2020-11-05 20:19:01 PST
Committed r269502: <https://trac.webkit.org/changeset/269502>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 413272 [details].
Comment 16 Radar WebKit Bug Importer 2020-11-05 20:20:22 PST
<rdar://problem/71105838>
Comment 17 WebKit Commit Bot 2020-12-15 13:20:20 PST
Re-opened since this is blocked by bug 219915
Comment 18 Eric Casthart 2020-12-17 15:02:29 PST
The patch landed for this appears to only address issues with Europe/Warsaw dates. I'm experiencing an issue like this in Safari 14.0.2 (15610.3.7.1.10, 15610) however in America/New_York with some relatively more recent dates. Some examples:

Wrong:

new Date(1983, 2, 27).toLocaleDateString('en-US') -> "3/26/1983"
new Date(1983, 3, 15).toLocaleDateString('en-US') -> "4/14/1983"
new Date(1983, 3, 24).toLocaleDateString('en-US') -> "4/23/1983"

Correct:

new Date(1983, 0, 27).toLocaleDateString('en-US') -> "1/27/1983"
new Date(1983, 1, 27).toLocaleDateString('en-US') -> "2/27/1983"
new Date(1983, 3, 27).toLocaleDateString('en-US') -> "3/27/1983"
new Date(1983, 3, 25).toLocaleDateString('en-US') -> "4/25/1983"

Significantly, US DST in 1983 started April 24.
Comment 19 Yusuke Suzuki 2020-12-17 15:41:20 PST
(In reply to Eric Casthart from comment #18)
> The patch landed for this appears to only address issues with Europe/Warsaw
> dates. I'm experiencing an issue like this in Safari 14.0.2 (15610.3.7.1.10,
> 15610) however in America/New_York with some relatively more recent dates.
> Some examples:
> 
> Wrong:
> 
> new Date(1983, 2, 27).toLocaleDateString('en-US') -> "3/26/1983"
> new Date(1983, 3, 15).toLocaleDateString('en-US') -> "4/14/1983"
> new Date(1983, 3, 24).toLocaleDateString('en-US') -> "4/23/1983"
> 
> Correct:
> 
> new Date(1983, 0, 27).toLocaleDateString('en-US') -> "1/27/1983"
> new Date(1983, 1, 27).toLocaleDateString('en-US') -> "2/27/1983"
> new Date(1983, 3, 27).toLocaleDateString('en-US') -> "3/27/1983"
> new Date(1983, 3, 25).toLocaleDateString('en-US') -> "4/25/1983"
> 
> Significantly, US DST in 1983 started April 24.

Please test it with Safari Technology Preview :)
Safari 14.0.2 does not include the patch.
Comment 20 Yusuke Suzuki 2020-12-17 15:41:45 PST
The reverted patch is once integrated since we workaround ICU missing API issue differently.
Comment 21 Eric Casthart 2020-12-17 15:46:23 PST
Ahh, I didn't think to try it because the diff seemed specific to Warsaw. But I can confirm that the wrong examples I provided below are now working as expected with Technology Preview Release 117 (Safari 14.1, WebKit 15611.1.7.2).

Thanks!

(In reply to Yusuke Suzuki from comment #19)
> (In reply to Eric Casthart from comment #18)
> > The patch landed for this appears to only address issues with Europe/Warsaw
> > dates. I'm experiencing an issue like this in Safari 14.0.2 (15610.3.7.1.10,
> > 15610) however in America/New_York with some relatively more recent dates.
> > Some examples:
> > 
> > Wrong:
> > 
> > new Date(1983, 2, 27).toLocaleDateString('en-US') -> "3/26/1983"
> > new Date(1983, 3, 15).toLocaleDateString('en-US') -> "4/14/1983"
> > new Date(1983, 3, 24).toLocaleDateString('en-US') -> "4/23/1983"
> > 
> > Correct:
> > 
> > new Date(1983, 0, 27).toLocaleDateString('en-US') -> "1/27/1983"
> > new Date(1983, 1, 27).toLocaleDateString('en-US') -> "2/27/1983"
> > new Date(1983, 3, 27).toLocaleDateString('en-US') -> "3/27/1983"
> > new Date(1983, 3, 25).toLocaleDateString('en-US') -> "4/25/1983"
> > 
> > Significantly, US DST in 1983 started April 24.
> 
> Please test it with Safari Technology Preview :)
> Safari 14.0.2 does not include the patch.
Comment 22 Yusuke Suzuki 2020-12-17 15:48:26 PST
(In reply to Eric Casthart from comment #21)
> Ahh, I didn't think to try it because the diff seemed specific to Warsaw.
> But I can confirm that the wrong examples I provided below are now working
> as expected with Technology Preview Release 117 (Safari 14.1, WebKit
> 15611.1.7.2).
> 
> Thanks!


Awesome!!!!!! Thanks for testing :D