elm-community / elm-time

A pure Elm date and time library.

Home Page:http://package.elm-lang.org/packages/elm-community/elm-time/latest

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Problem with Etc/UTC±n timezone definitions?

jeffesp opened this issue · comments

When I select one of the ETC/UTC + or - timezones, it looks as if they have the sign wrong.

Here's a sample from the elm-repl:

> import Time.DateTime as DateTime exposing (zero)
> import Time.ZonedDateTime as ZonedDateTime
> import Time.TimeZones exposing (etc_gmt_plus_8, etc_gmt_minus_8)
> dt = DateTime.dateTime { zero | year = 2018, month = 01, day = 09, hour = 0 }
DateTime { date = Date { year = 2018, month = 1, day = 9 }, offset = 0 }
    : Time.DateTime.DateTime
> dt |> ZonedDateTime.fromDateTime (etc_gmt_plus_8 ()) |> ZonedDateTime.toISO8601
"2018-01-08T16:00:00.000-08:00" : String
> dt |> ZonedDateTime.fromDateTime (etc_gmt_minus_8 ()) |> ZonedDateTime.toISO8601
"2018-01-09T08:00:00.000+08:00" : String

Note the -08:00 in the conversion using etc_gmt_plus_8 and the +08:00 in the conversion using etc_gmt_minus_8.

Looking at the source:

etc_gmt_plus_8_l = unpack "Etc/GMT+8|-08|80|0|"

it seems like this is maybe the -80 there rather than a +80? I didn't take the time to understand the data format there, so I might be totally off base, but it was my first thought as I was looking for a source of the bug.

No, I believe what the code has is correct. This was also tricky for me before I figured it out. Here's Wikipedia's assertion on how this works followed by how I explain it to myself.

Referencing Wikipedia on ISO8601 in the 2nd paragraph (bolding mine):

The following times all refer to the same moment: "18:30Z", "22:30+04", "1130−0700", and "15:00−03:30". Nautical time zone letters are not used with the exception of Z. To calculate UTC time one has to subtract the offset from the local time, e.g. for "15:00−03:30" do 15:00 − (−03:30) to get 18:30 UTC.

The key is that you are calculating how to adjust the UTC time to be the same moment in time as a specified time in another timezone. For example, Portugal is one hour behind UTC, so is offset -01:00. Hence 9:00 UTC is 8:00 in Portugal. But we're not trying to figure out Portugal time from UTC; we're trying to figure out what UTC time is when it's 9:00 in Portugal. UTC is an hour ahead, so it's 10:00 UTC when it's 9:00 in Portugal. In other words, you toggle the polarity of the offset when going to UTC.

I think that I get what you are saying, and it is played out in the dates from my reply session above. So converting to UTC+8, "2018-01-09 00:00" subtracts 8:00 and goes to "2018-01-08 16:00". That seems reasonable. But what is weird to me is that the "-08:00" displayed at the end of the date. I'm converting to UTC+8 but the date is telling me that it's UTC-8.

It helps me to remember that 2018-01-09 00:00+08:00 is saying that if I want to convert UTC time, which is 2018-01-09 00:00, to Philippines time, I add 8 hours and the Philippines time will be 2018-01-09 08:00. I.e. converting from UTC to some other timezone does NOT toggle the sign.

But what if we're going the opposite direction? If the local Philippines time is 2018-01-09 00:00, and I want to convert to UTC, then this time we must "go backwards", i.e. subtract the timezone offset from the Philippines time to obtain the UTC time. In this case, UTC is 8 hours behind and so becomes 2018-01-08 16:00

Does that help?

Thanks for all the explanation here. I think I get the whole when to add or subtract the TZ offset at this point. I want to ask about one other thing that also seems weird. If you can take a second to look at the details of this REPL session, you can see that each date does the correct math (as you explained above) but then the TZ on the string representation of the date has the opposite sign of what was requested. So when I use etc_gmt_plus_6 I get a string ending with -06:00 and when I use etc_gmt_minus_5 I get a string ending with +05:00. I assumed that when I pick to move into the +6 timezone the ending TZ in the string would be +06:00. Or is that wrong too in the same way as the math associated with moving timezones?

Here I have given a note about what is going on for each line I ran in the REPL:

> dt
DateTime { date = Date { year = 2018, month = 1, day = 9 }, offset = 0 }
    : Time.DateTime.DateTime

The base date

> dt |> ZonedDateTime.fromDateTime (TimeZones.etc_gmt_plus_8 ()) |> ZonedDateTime.toISO8601
"2018-01-08T16:00:00.000-08:00" : String

Going to plus_8 subtracts 8 hours correctly, but then the timezone is signified as -08:00 in the resulting string.

> dt |> ZonedDateTime.fromDateTime (TimeZones.etc_gmt_plus_7 ()) |> ZonedDateTime.toISO8601
"2018-01-08T17:00:00.000-07:00" : String

Same thing happens for plus_7

> dt |> ZonedDateTime.fromDateTime (TimeZones.etc_gmt_minus_1 ()) |> ZonedDateTime.toISO8601
"2018-01-09T01:00:00.000+01:00" : String

Effectively the opposite is happening when I use minus_1

> dt |> ZonedDateTime.fromDateTime (TimeZones.etc_gmt_minus_3 ()) |> ZonedDateTime.toISO8601
"2018-01-09T03:00:00.000+03:00" : String

Again same but with minus_3.

Thanks again for all your help with this.

The meaning of TimeZones.etc_gmt_plus_8 is actually -8 hours via these definitions:

Per TimeZones lines 1694 to 1696:

{-| Etc/GMT+8 -}
etc_gmt_plus_8 : () -> TimeZone
etc_gmt_plus_8 () = force etc_gmt_plus_8_l

etc_gmt_plus_8_l is assigned here:

Per TimeZoneData line 312:

etc_gmt_plus_8_l = unpack "Etc/GMT+8|-08|80|0|"

Observe that the offset expression is -08.

I haven't looked at the timezone Elm files much, so I don't know why their "polarity" is reversed. Again, whether it's expressed "plus" or "minus" depends upon whether you are converting from a non-UTC time to a UTC time or vice-versa.

From a user perspective, I think the polarity is wrong. Thanks for your explanation on all this and how it can depend on the perspective of which way you are going. I am going to close the issue and work around it my application.

Just to follow up on this once more, it's a source data problem, and nothing to do with the elm-time lib. I found this comment from the data file linked here: https://www.iana.org/time-zones

# Be consistent with POSIX TZ settings in the Zone names,
# even though this is the opposite of what many people expect.
# POSIX has positive signs west of Greenwich, but many people expect
# positive signs east of Greenwich.  For example, TZ='Etc/GMT+4' uses
# the abbreviation "-04" and corresponds to 4 hours behind UT
# (i.e. west of Greenwich) even though many people would expect it to
# mean 4 hours ahead of UT (i.e. east of Greenwich).

Hi @jeffesp. Thanks, your posting is very useful. By coincidence, I've taken a day off from work to see if I can finish the parser change, and I happen to be deep in this TimeZone code. I say this in that TIL that the ISO8601 and the TimeZone code specification are different; TimeZone uses IANA.

I'll use your reference in the documentation; I have been confused about this as well.

Reopening issue to add reference.

Added Etc/GMT comment.

Reclosing this issue