Javascript Date parsing returning strange results in Chrome
Okay, seems like this behaviour cannot be avoided, so you should parse dates manually. But the way to parse it is pretty simple.
If we are parsing date in ISO 8601 format, the mask of date string looks like this:
<yyyy>-<mm>-<dd>T<hh>:<mm>:<ss>(.<ms>)?(Z|(+|-)<hh>:<mm>)?
1. Getting date and time separately
The T
in string separates date from time. So, we can just split ISO string by T
var isoString = `2019-05-09T13:26:10.979Z`
var [dateString, timeString] = isoString.split("T")
2. Extracting date parameters from date string
So, we have dateString == "2019-05-09"
. This is pretty simple now to get this parameters separately
var [year, month, date] = dateString.split("-").map(Number)
3. Handling time string
With time string we should make more complex actions due to its variability.
We have timeString == "13:26:10Z"
Also it's possible timeString == "13:26:10"
and timeString == "13:26:10+01:00
var clearTimeString = timeString.split(/[Z+-]/)[0]
var [hours, minutes, seconds] = clearTimeString.split(":").map(Number)
var offset = 0 // we will store offset in minutes, but in negation of native JS Date getTimezoneOffset
if (timeString.includes("Z")) {
// then clearTimeString references the UTC time
offset = new Date().getTimezoneOffset() * -1
} else {
var clearOffset = timeString.split(/[+-]/)[1]
if (clearOffset) {
// then we have offset tail
var negation = timeString.includes("+") ? 1 : -1 // detecting is offset positive or negative
var [offsetHours, offsetMinutes] = clearOffset.split(":").map(Number)
offset = (offsetMinutes + offsetHours * 60) * negation
} // otherwise we do nothing because there is no offset marker
}
At this point we have our data representation in numeric format:
year
, month
, date
, hours
, minutes
, seconds
and offset
in minutes.
4. Using ...native JS Date constructor
Yes, we cannot avoid it, because it is too cool. JS Date
automatically match date for all negative and too big values. So we can just pass all parameters in raw format, and the JS Date
constructor will create the right date for us automatically!
new Date(year, month - 1, date, hours, minutes + offset, seconds)
Voila! Here is fully working example.
function convertHistoricalDate(isoString) {
var [dateString, timeString] = isoString.split("T")
var [year, month, date] = dateString.split("-").map(Number)
var clearTimeString = timeString.split(/[Z+-]/)[0]
var [hours, minutes, seconds] = clearTimeString.split(":").map(Number)
var offset = 0 // we will store offset in minutes, but in negation of native JS Date getTimezoneOffset
if (timeString.includes("Z")) {
// then clearTimeString references the UTC time
offset = new Date().getTimezoneOffset() * -1
} else {
var clearOffset = timeString.split(/[+-]/)[1]
if (clearOffset) {
// then we have offset tail
var negation = timeString.includes("+") ? 1 : -1 // detecting is offset positive or negative
var [offsetHours, offsetMinutes] = clearOffset.split(":").map(Number)
offset = (offsetMinutes + offsetHours * 60) * negation
} // otherwise we do nothing because there is no offset marker
}
return new Date(year, month - 1, date, hours, minutes + offset, seconds)
}
var testDate1 = convertHistoricalDate("1894-01-01T00:00:00+01:00")
var testDate2 = convertHistoricalDate("1893-01-01T00:00:00+01:00")
var testDate3 = convertHistoricalDate("1894-01-01T00:00:00-01:00")
var testDate4 = convertHistoricalDate("1893-01-01T00:00:00-01:00")
console.log(testDate1.toLocaleDateString(), testDate1.toLocaleTimeString())
console.log(testDate2.toLocaleDateString(), testDate2.toLocaleTimeString())
console.log(testDate3.toLocaleDateString(), testDate3.toLocaleTimeString())
console.log(testDate4.toLocaleDateString(), testDate4.toLocaleTimeString())
Note
In this case we are getting Date
instance with all its own values (like .getHours()
) being normalized, including timezone offset. The testDate1.toISOString
will still return weird result. But if you are working with this date, it will probably 100% fit your needings.
Hope that helped :)