Closed tzachshabtay closed 6 years ago
Please see Strings that specify an offset in the docs and the Set Zone section just after it.
@icambron specifying an offset and using the "Z" suffix is not the same thing. As offsets don't have a one-to-one relationships to timezones, it's completely reasonable for luxon to use the system's timezone. But the "Z" suffix is a special case as it's meant to be interpreted as UTC, see here: https://en.wikipedia.org/wiki/ISO_8601#Coordinated_Universal_Time_(UTC)
So I expect luxon to respect the ISO spec in that regard and use utc as a timezone and not the local system's timezone.
Luxon parses the Z out and uses it to interpret the time encoded in the string; the only question is whether it also sets the zone on the resulting datetime to UTC or not.
The point isn't whether Z is an offset or zone; check out the second example involving Europe/Paris
. (Perhaps the section title should be changed to reflect that.) Luxon considers both a zone or offset specified in the string independent of zone in the resulting DateTime, unless you specifically tell it otherwise. It would be silly to do otherwise, because it would create really inconsistent behavior for a caller who doesn't know whether the incoming string contains "Z" or not.
Is there a way to explicitly tell Luxon that the given string is date in UTC, and prevent it from doing anything to it?
@jayarjo
DateTime.fromISO(“2014-08-06T09:10:16”, { zone: “utc”})
That will both interpret the time as utc and set utc as the datetime’s zone. If you only want the first behavior, add .toLocal()
In my opinion, this is a flaw with Luxon, and a "gotcha" for developers who expect Luxon to respect ISO spec. Javascript's own Date() constructor properly interprets the Z:
const d = new Date('2018-07-25T00:00:00Z');
const l = new Date('2018-07-25T00:00:00');
d.toString()
// "Tue Jul 24 2018 18:00:00 GMT-0600 (Mountain Daylight Time)"
l.toString()
// "Wed Jul 25 2018 00:00:00 GMT-0600 (Mountain Daylight Time)"
Luxon is providing the added benefit of parsing offsets, but the presence of the "Z" at the end should override any offset and respect the spec.
@inorganik you're misreading the issue; Luxon respects the spec and interprets the Z correctly. In fact, its behavior is just like the native date's:
const d = luxon.DateTime.fromISO('2018-07-25T00:00:00Z');
const l = luxon.DateTime.fromISO('2018-07-25T00:00:00');
d.toString(); //=> "2018-07-24T20:00:00.000-04:00"
l.toString() //=> "2018-07-25T00:00:00.000-04:00"
The ticket is asking for Luxon to not just interpret the Z correctly (which it does), but to also set the zone property to UTC (which it does not).
@icambron I think we're talking around eachother. In your example, you can see the issue:
"2018-07-25T00:00:00Z"
gets formatted in your local time zone: "2018-07-24T20:00:00.000-04:00"
You have to specify the zone with Luxon, instead of just providing the "Z". Here's an example:
const time = '2018-07-25T00:00:00Z';
const utcA = DateTime.fromISO(time);
const utcB = DateTime.fromISO(time, { zone: 'utc' });
outputs:
utc A: 2018-07-24T18:00:00.000-06:00 utc B: 2018-07-25T00:00:00.000Z
I'm saying we would expect the zone to be set as UTC since we are providing it as UTC. But instead we have to use helper methods like the following to process all our Luxon dates:
const isoToUTC = (iso: string): DateTime => DateTime.fromISO(iso, { zone: 'utc' });
To add to my response - you are right, it does behave the same as native Date. But where I got confused and I think @tzachshabtay was confused (and my co-worker) was that Luxon doesn't display that date in the UTC time zone and instead converted it to our local time zone.
Yes, that's the issue and I explained above why it's not a good idea to change it. I'll try again below. But it was your additional claim that Luxon is violating the ISO spec that caught my attention here; that's simply a mistaken claim. Even the example you gave of the native Date shows it returning the date in your local zone.
My final word on the original issue: how Luxon parses and how it displays are simply different things. Using the string to choose the zone is the unusual case. By default, if a program using Luxon is passed an ISO string that happens to use an offset of, say, +03:30, we don't want to force that program to convert the times to the zone it has already chosen to work in. We parse the string (of course taking the offset into account) and convert the date the default zone. Then the caller can have consistent expectations for the properties of that datetime. Of course the need to "echo" the string is a real, if less common, use case and so it's supported with the additional setZone
parameter.
Please see Strings that specify an offset in the docs and the Set Zone section just after it.
The link is dead
Steps to reproduce (only if you're on a negative timezone offset locally):
const luxonDate1 = DateTime.fromISO("2021-09-11T00:00:00Z");
luxonDate1.toISODate() === '2021-09-10';
luxonDate1.toFormat('yyyy-MM-dd') === '2021-09-10';
const luxonDate2 = DateTime.fromISO("2021-09-11T00:00:00Z");
luxonDate2.toISODate() === '2021-09-10';
luxonDate2.toFormat('yyyy-MM-dd') === '2021-09-10';
This is odd especially because of this, just tested on 2.3.0:
const luxonDate3 = DateTime.fromJSDate(new Date("2021-09-11T00:00:00Z"));
luxonDate3.toISODate() === '2021-09-10';
luxonDate3.toFormat('yyyy-MM-dd') === '2021-09-10';
According to documentation from 2.3.0, fromISO method follows ISO 8601:
And according to wikipedia ISO 8601 finishing in Z is UTC:
Either the documentation has to change to address this or there must be an implementation to interpret strings ending with Z to be UTC zone as output too by default.
@GuilhermeRossato you're re-hashing an issue that has already been discussed in this thread. The key "gotcha" here that tripped up me and others is understanding that how a date is parsed and how a date is displayed (or formatted) are 2 different things.
When you parse an ISO date that ends in 'Z', luxon parses it as UTC, but when you display it by calling toISODate()
it behaves like the native Date
class and displays it in your local timezone. You have to explicitly tell luxon to display it in the UTC timezone like so:
const luxonDate1 = DateTime.fromISO("2021-09-11T00:00:00Z", { zone: 'utc' });
// or
const luxonDate1 = DateTime.fromISO("2021-09-11T00:00:00Z").toUTC();
I'm amazed to see how many words were wasted here to try to defend (an arguably bad (*)) design decision to make Luxon behave similar to native Date by default.... and none to actually answer the original question.
So, for the posterity - here's the real solution for everyone who might be wondering why Luxon is losing the timezone offset (regardless of whether it's UTC, or any other):
DateTime.fromISO('2024-01-10T15:00Z', {setZone:true}).toISO()
'2024-01-10T15:00:00.000Z'
(*) The reason we want to use Luxon is because the native Date support is full of broken idiosyncrasies like this one. We WANT it to behave more logically than the native.
@alenl Thanks for clarifying this just now, right when I stumble across this very issue. The {setZone:true}
part indeed makes Luxon respect the Z
in the input string.
So for example:
d = luxon.DateTime.fromISO('2023-12-17T06:08:09.933Z', {setZone:true})
d.hour
Outputs: 6, as expected.
Whereas:
d = luxon.DateTime.fromISO('2023-12-17T06:08:09.933Z')
d.hour
Will output 7 (in my timezone).
@alenl is it an undocumented feature? I cannot find it in the docs. Could you link the source?
@alenl is it an undocumented feature? I cannot find it in the docs. Could you link the source?
It's documented, just a bit cryptic:
opts.setZone boolean (default false)
override the zone with a fixed-offset zone specified in the string itself, if it specifies one
The result has my local timezone, whereas I expected the result to be in UTC timezone, as clearly depicted by the ending "Z" character.