1

The issue I'm having is that Chrome and IE/Edge give different results serializing certain JavaScript datetimes to JSON - for example summer of 1945 in the UK.

I have a JavaScript object containing a Date type set via a calendar component which sets time to midnight local time. This object is serialized via JSON.stringify which in turn uses Date.toJSON which in turn uses Date.toISOString.

If I have a local datetime of say 1st May 1977 in my UK locale, this serializes to "1977-04-30T23:00:00.000Z", i.e. UTC 11pm 30th April 1977. This is fine, however if I start with local 1st May 1945, I end up with different results using Chrome vs IE/Edge. Chrome correctly gives me "1945-04-30T22:00:00.000Z". The reason this is 10pm rather than 11pm is because the UK operated double daylight savings time during WW2.

IE/Edge however subtracts one hour meaning the JSON UTC datetime string is out by an hour.

What this means for me is that I cannot trust the JSON serialized data passed back to the server unless I know which browser it came from.

I'm contemplating overriding Data.prototype.toJSON to change the serialization to something based on local time, but including the timezone offset, such as "1945-05-01T00:00:00.000+0200". At least then I know what the user actually intended. However this does seem a bit extreme and wonder if I'm missing something. JSON.stringify is the usual method for serializing data to be shipped to a server, but this whole mechanism seems flawed.

1 Answers1

0

Indeed, historical time zone information varies greatly depending on implementation. You're seeing different results in different browsers because Windows does not track historical changes for the UK. IE and classic Edge (not the Chromium version) use the Windows time zone data to make these conversions. Chrome delivers the correct results because it ships with it's own time zone data from the IANA time zone database (via ICU).

For the scenario your describing, where a user picks a date from a picker (presumably a birth date or something similar), the best way to deal with this is to not. Instead of using a Date object (which isn't really a date but actually a timestamp), keep the original year, month, and day that the user selected. You can either keep separate values for each, or an object or array of your own creation, or more commonly you can keep a date-only string in ISO 8601 format, such as "1945-04-30".

Indeed, if you use an <input type="date"/> HTML5 element, its value will be a string in that format rather than a Date object. There are other date pickers that will return that string, but if you're stuck using one that returns a Date object, then simply construct your own string from the results of .getFullYear(), .getMonth(), and .getDate() (padding zeros appropriately).

Then you can pass that string in your JSON results without having to worry about any conversion to/from UTC on either side, nor whether the historical time zone data is present.

As an alternative, if you're stuck using Date objects, then an approach that some use is to explicitly set the time to a mid-day hour (such as 12 noon) before it is serialized. That keeps the year month and day components on the same original calendar date even if the time converts to/from UTC.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • Thanks, I did wonder about artificially setting the time to noon, but that seemed such an ugly hack I thought I must be missing something obvious. Apparently not. I'll take a closer look at what I can do with my calendar component. – Spiny Norman Dec 04 '19 at 10:27
  • I see you are also involved with [Moment.js](https://momentjs.com/). I'm already using this, so just wondering if there's anything in there I can usefully use in this case? – Spiny Norman Dec 04 '19 at 10:34
  • You could do something like `moment(dateObjectFromYourPicker).format('YYYY-MM-DD')` to just send the date. You could also, without Moment, do `dateObjectFromYourPicker.toLocaleDateString('en-CA', { dateStyle: 'short' })`. (`en-CA` gives the yyyy-mm-dd format, so you can treat it as ISO 8601) – Matt Johnson-Pint Dec 05 '19 at 16:56
  • You might also consider [this approach](https://stackoverflow.com/a/31104671/634824) to make this work with your JSON. – Matt Johnson-Pint Dec 05 '19 at 16:57
  • 1
    I think I'll probably go down the route of overriding Date.prototype.toJSON and I might as well use Moment as it's already a dependency. Thanks for the help. – Spiny Norman Dec 06 '19 at 16:01