This post is originally published on yoursunny.com blog https://yoursunny.com/t/2020/GMT-0456/
Recently, someone on the DCTech Slack community asked why the Date.prototype.toDateString
function is having an off-by-one error:
new Date("2020-10-17").toDateString();
"Fri Oct 16 2020"
My immediate response was: timezone.
The group then proceeded to discover that the Date
constructor would interpret a date-only string as being in UTC timezone.
Washington, DC uses Eastern Daylight Time that is four hours behind UTC, so the timezone of the constructed Date
object is 20:00:00 local time on the previous date.
Since toDateString
uses local time, it prints as the previous date.
After that, I started testing some boundary conditions:
new Date("0001-01-01").toString()
"Sun Dec 31 0000 19:03:58 GMT-0456 (Eastern Standard Time)"
new Date("0000-01-01").toString()
"Fri Dec 31 -0001 19:03:58 GMT-0456 (Eastern Standard Time)"
One problem is that, the year zero should not exist, but it's understandable that JavaScript has not been invented around that time.
What really puzzles me is, why does the timezone show up as GMT-0456, instead of the usual GMT-0500.
I did a binary search to find when did the timezone change from GMT-0456 to GMT-0500:
new Date("1883-11-18 12:03:57").toString();
"Sun Nov 18 1883 12:03:57 GMT-0456 (Eastern Standard Time)"
new Date("1883-11-18 12:03:58").toString();
"Sun Nov 18 1883 12:03:58 GMT-0500 (Eastern Standard Time)"
The magic timestamp turns out to be between 1883-11-18 12:03:57 and 1883-11-18 12:03:58.
Something must have happened on that day!
An Internet search of the date turned up this article: Today in History: November 18, 1883: Time zones standardized in Canada and USA.
Basically,
- Until 1883-11-18, each town sets its own time, based on their own estimation of solar time.
- Having different local time in each town was causing problems with the railroads, so that the railroad companies established five timezones for Canada and the United States.
- 1883-11-18 marks the date when Eastern Standard Time was established.
This explains the date 1883-11-18, but still doesn't explain why the cut-off time was 12:03:57 and why the previous timezone is GMT-0456.
I started looking for answers in the computer source code.
Since I was testing on Chrome browser, I started with the V8 JavaScript engine.
I located the code related to Date
and timezone, but didn't find GMT-0456 in there.
Instead, V8 invokes GetLocalOffsetFromOS
function, suggesting that the timezone information comes from the operating system.
I don't have access to Windows source code, but I recall that Linux uses tzdata for timezone information.
I downloaded the current Time Zone Database.
In the northamerica
data file, I found the following records:
# US eastern time, represented by New York
# From Paul Eggert (2014-09-06):
# Monthly Notices of the Royal Astronomical Society 44, 4 (1884-02-08), 208
# says that New York City Hall time was 3 minutes 58.4 seconds fast of
# Eastern time (i.e., -4:56:01.6) just before the 1883 switch. Round to the
# nearest second.
# Rule NAME FROM TO - IN ON AT SAVE LETTER
Rule NYC 1920 only - Mar lastSun 2:00 1:00 D
Rule NYC 1920 only - Oct lastSun 2:00 0 S
Rule NYC 1921 1966 - Apr lastSun 2:00 1:00 D
Rule NYC 1921 1954 - Sep lastSun 2:00 0 S
Rule NYC 1955 1966 - Oct lastSun 2:00 0 S
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone America/New_York -4:56:02 - LMT 1883 Nov 18 12:03:58
-5:00 US E%sT 1920
-5:00 NYC E%sT 1942
-5:00 US E%sT 1946
-5:00 NYC E%sT 1967
-5:00 US E%sT
So the answer to this GMT-0456 mystery is:
- Eastern Time is really "time in New York".
- Before Eastern Standard Time was established on the noon of Nov 18, 1883, the local time in New York was 12:03:58.
- That time was 4 hours and 56 minute (04:56) behind Greenwich Mean Time, so that it shows up as GMT-0456.
Top comments (3)
Thanks to your post, I have added test cases to my zoned-date library.
Precisely speaking, the clocks shown something like:
-> 1883-11-18T12:00:00.000
-> 1883-11-18T12:03:59.999 (to be backwarded in next tick, in old timezone so far, GMT-0456)
-> 1883-11-18T12:00:00.000 (backwarded, join new timezone GMT-5)
-> 1883-11-18T12:03:59.999
-> 1883-11-18T12:04:00.000
github.com/tranvansang/zoned-date/...
Nice to hear the story! 2 weeks holding on to the article, and you could have posted it on the anniversary ๐
Also, a takeaway from this story: never use a datetime object to represent a local date (in JS, use an ISO 8601 formatted string, or make your own
LocalDate
object).