date-fns: formatISO doesn't use UTC (Z) timezone

When making a sanity check of now.toISOString() === formatISO(now) (with const now = new Date()), it fails due to a timezone difference, as the formatISO doesn’t match the browser’s toISOString, which explicitly claims that The timezone is always zero UTC offset, as denoted by the suffix "Z".. The formatISO docs @v2.16.1 doesn’t mention anything about this - it actually doesn’t even seem to support configuring UTC to be used - and it has an example, which is thus incorrect (non-deterministic):

// Represent 18 September 2019 in ISO 8601 format (UTC):
const result = formatISO(new Date(2019, 8, 18, 19, 0, 52))
//=> '2019-09-18T19:00:52Z'

Actual result:

formatISO(new Date())2021-01-12T02:57:09+01:00
formatISO(new Date(2019, 8, 18, 19, 0, 52))2019-09-18T19:00:52+02:00
new Date(2019, 8, 18, 19, 0, 52).toISOString()2019-09-18T17:00:52.000Z

This may be related to closed issues of https://github.com/date-fns/date-fns/issues/1672 and https://github.com/date-fns/date-fns/issues/1559 where the problem was the opposite, as the ‘Z’ wasn’t previously included at all.

Is anyone up to a quest of finding a proper way to use { representation: 'time' } at least with the original toISOString()?


Tested on v2.16.1 in:

  • Chrome 87.0.4280.88
  • Firefox 84.0.2

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 62
  • Comments: 15

Most upvoted comments

I’m surprised that such a critical bug has not been addressed for over a year!

I’m surprised that such a critical bug has not been addressed for over a year!

Same here, but it’s still not fixed lol.

I just ran into this. It would be great to get this fixed. Otherwise formatISO is completely unusable

Hi guys, any update on this? I just found out the formatISO output is different between actual value and the value stated in the documentation.

+1

Will this ever be addressed?

I posted similar question in discussion https://github.com/date-fns/date-fns/discussions/2366

So, in the meantime, should I just use this instead?

format(date, "yyyy-MM-dd'T'HH:mm:ss'Z'")

I believe that solution should be to at least put warning regarding this in the documentation. I had a bug in my app that was caused by getting in new versions of Chrome something else than it was expected based on docs. This is how it is looking currently in Chrome 89.0.4 image

@laurensiusadi , check my answer there.

The documentation is confusing on this. The examples for formatISO imply that it yields a date string in UTC time, but on my Windows machine at least, it always yields a data string in local time (i.e., the operating system’s time zone setting).

Conversely, the built-in toISOString appears to always yield a date string in UTC regardless of the OS’s time zone setting.

Taking a quick look at the ISO 8601 standard, either of these approaches appear to be acceptable:

Time zones in ISO 8601 are represented as local time (with the location unspecified), as UTC, or as an offset from UTC.

What’s really bizarre is if you use utcToZonedTime (a function in date-fns-tz) to produce a date in a time zone other than your local setting and run formatISO on that, you’ll get a blatantly incorrect UTC offset. What I think is happening is that it converts the date to the desired time zone, but the JS Date object has no way of knowing that, so when you pass it into formatISO, it just assumes local time. I guess this is why date-fns-tz has it’s own format method (though it doesn’t have it’s own formatISO method, which would be helpful).

I just got caught with this discrepancy between formatISO() and the native .toISOString().

It would be useful if the documentation was updated to reflect that the ISO string returned by formatISO is the local timezone. It’s as simple as showing something like this

const result = formatISO(new Date(2019, 8, 18, 19, 0, 52))
//=> '2019-09-18T19:00:52+01:00'
//note that the timezone is local