1

Seems there is a problem with definition of ISO date format in Javascript. As far as I undrerstand ISO-formatted date can include TZ offset that includes hours, minutes and seconds, e.g.:

1919-07-01T00:00:00+04:31:19

Trying to parse such datetime string using JavaScript new Date() object leads to an error:

new Date('1919-07-01T00:00:00+04:31:17').toLocaleDateString('ru-RU') => "Invalid Date"
new Date('1919-07-01T00:00:00+04:31').toLocaleDateString('ru-RU') => "01.07.1919"

The date specified in the example comes from the Java backend client/POSTGRESQL database where representation '1919-07-01T00:00:00+04:31:17' treated as a valid ISO date. The reason the date contains "seconds" in timezone offset is understood if we look as the following data regaring daylight savings changes: https://www.timeanddate.com/time/change/russia/moscow?year=1919

Is there any suggestion why it is impossible to overcome this limitation or where is the origin of the problem?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Pavel
  • 23
  • 5
  • 3
    https://en.wikipedia.org/wiki/ISO_8601#Times does not say that seconds are valid on the timezone. `1919-07-01T00:00:00+04:31:19` is an invalid date string. – evolutionxbox Aug 17 '21 at 09:29
  • 1
    Timezone Moscow Time/UTC+03:00. The problem is that for dates below 01.07.1919 it will have the offset +04:31:17 – Pavel Aug 17 '21 at 10:10
  • @evolutionxbox Wiki says that "Time zones in ISO 8601 are represented as local time (with the location unspecified), as UTC, or as an offset from UTC." Unfortunately I have not found the original ISO specfication text available online – Pavel Aug 17 '21 at 10:19
  • @Pavel does https://en.wikipedia.org/wiki/List_of_UTC_time_offsets help? – evolutionxbox Aug 17 '21 at 10:20
  • 1
    ISO 8601 might not specify seconds in the UTC offset which is why a parser doesn't "expect" it - nevertheless, such offsets exist, for the given tz see also the respective section in the [IANA database](https://github.com/eggert/tz/blob/5c79ca1f7b574798dc5f05ab0eeaae30ceb948e4/europe#L2553) for Europe/Moscow – FObersteiner Aug 17 '21 at 10:22
  • @MrFuppes Thank you for the good reference to IANA database. Anyway it does explain how to handle such dates using JavaScript? Most of known converters (e.g. momentjs) rely either on default "Date" implementation, either on limited set of timezones which can be parsed without error. In the same time postgre/java implementations could handle such dates without any error – Pavel Aug 17 '21 at 10:55
  • The JavaScript API doesn't seem to think the date is valid. Consider using a library like [luxon](https://moment.github.io/luxon/)? – evolutionxbox Aug 17 '21 at 11:17
  • 1
    Usage: Those seconds have got nothing to do with leap seconds. – Ole V.V. Aug 17 '21 at 11:43
  • Luxon does not work either – Pavel Aug 17 '21 at 11:54
  • @Pavel I cannot help with _"it does not work"_. Add an example please. – evolutionxbox Aug 17 '21 at 11:55
  • @evolutionxbox Tried in jsfiddle: http://jsfiddle.net/90f8ochz/ – Pavel Aug 17 '21 at 12:05
  • `fromISO` expects the timezone to only be minutes and hours. consider reading https://moment.github.io/luxon/#/parsing?id=ad-hoc-parsing – evolutionxbox Aug 17 '21 at 12:15
  • You might to look at this issue [Date-fns IANA](https://github.com/date-fns/date-fns/issues/180) and the two packages mentioned [Date-fns-timezone](https://www.npmjs.com/package/date-fns-timezone) and [Date-fns-tz](https://www.npmjs.com/package/date-fns-tz) – Adrian Klaver Aug 17 '21 at 15:08

1 Answers1

0

Seems there is a problem with definition of ISO date format in Javascript.

Javascript is based on ECMA-262, which is published by ECMA International. ISO 8601 is published by ISO, a different standards organisation. I don't have a full copy of ISO 8601, however I expect that there are extensions to allow seconds in the offset given they were fairly common prior to 1900.

ECMA-262 defines a couple of formats, one is based on a simplification of ISO 8601, so does not support all of the formats ISO 8601 does (the other is the format produced by toString).

Since the format in the OP is not consistent with one of the formats in ECMA-262, parsing is implementation dependent, see Why does Date.parse give incorrect results?

Your best option is to manually parse the string, either with a simple function or a library that supports seconds in the offset.

An example parse function is below (according to timeanddate the seconds part of the offset in 1919 should have been 19, not 17 so I've modified the original string accordingly):

// Parse format 1919-07-01T00:00:00+04:31:19
function parseWithTZSecs(date) {
  let [Y, M, D, H, m, s, oH, om, os] = date.match(/\d+/g);
  let sign = date.substring(19,20) == '+'? -1 : 1;
  return new Date(Date.UTC(Y, M-1, D, +H + sign*oH, +m + sign*om, +s + sign*os));
}

let date = parseWithTZSecs('1919-07-01T00:00:00+04:31:19');
// 1919-07-01, 12:00:00 AM
console.log(date.toLocaleString('en-CA',{timeZone: 'Europe/Moscow'}));

This is just an example, of course it should do validation of input and deal with unexpected formats and values.

RobG
  • 142,382
  • 31
  • 172
  • 209