date-fns / date-fns

⏳ Modern JavaScript date utility library ⌛️
https://date-fns.org
MIT License
34.08k stars 1.76k forks source link

formatISO doesn't use UTC (Z) timezone #2151

Open scscgit opened 3 years ago

scscgit commented 3 years ago

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:

devuxer commented 3 years ago

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).

laurensiusadi commented 3 years ago

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'")
devuxer commented 3 years ago

@laurensiusadi , check my answer there.

lazarnikolic1983 commented 3 years ago

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

rmclaughlin-nelnet commented 2 years ago

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

marcbachmann commented 2 years ago

A lot of methods are affected by timezone problems. https://github.com/date-fns/date-fns/pull/2619

let date = new Date()
"Wed Sep 08 2021 18:34:02 GMT+0200 (Central European Summer Time)"

date.toISOString()
"2021-09-08T16:34:02.400Z"

_.endOfMonth(date).toISOString()
"2021-09-30T21:59:59.999Z"

new Date(_.endOfMonth(date).getTime() - date.getTimezoneOffset() * 60000).toISOString()
"2021-09-30T23:59:59.999Z"
gedclack commented 2 years ago

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.

hugonteifeh commented 2 years ago

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

ummahusla commented 1 year ago

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.

HealYouDown commented 11 months ago

Will this ever be addressed?

dimitriy-k commented 10 months ago

it there any solution?

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

gives response localhost: 2030-12-20T00:52:30Z gitlabs CI: 2030-12-20T00:00:00Z

lucajung commented 8 months ago

+1

paulovieira commented 3 months ago

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
AshotN commented 2 months ago

I'm using UTCDate as provided by https://github.com/date-fns/utc

formatISO(new UTCDate('2024-04-16'), {})
keithics commented 2 months ago

you dont need to use formatISO. I simply do this instead:

  const currentDate = new Date();
  const oneMonthAgo = subMonths(currentDate, 1);
  const lastMonth = oneMonthAgo.toISOString();

  console.log("One month ago:", lastMonth);
halws commented 1 month ago

based on https://github.com/date-fns/date-fns/issues/2151#issuecomment-806452928 this helped to me

    const localDate = utcToZonedTime(
      format(new Date(analyticsData.generatedAt), "yyyy-MM-dd'T'HH:mm:ss'Z'"),
     // get user local timezone
      Intl.DateTimeFormat().resolvedOptions().timeZone
    );

// example of formatting
    const formattedDate = `Last refresh: ${formatDistanceToNow(localDate, {
      addSuffix: true,
    })}, ${format(localDate, 'HH:mm')} (Local Time)`;
floriangosse commented 2 weeks ago

This bug is a huge problem and has to be fixed immediately. It can be compared to Date.prototype.toISOString() which gives the correct output.

bfatoms commented 2 weeks ago

I just got this today..

image

output..

image
ZaDarkSide commented 6 days ago

Well it's not a bug, browsers use a simplified format based on ISO 8601 and date-fns implements the actual standard ISO 8601 they are not supposed to be the same, blame it on the guys that implemented Date.prototype.toISOString() in a simplified way...