NanoDates.jl icon indicating copy to clipboard operation
NanoDates.jl copied to clipboard

Local time and offsets the wrong way round when parsing/construction from string

Open anowacki opened this issue 1 year ago • 2 comments

As far as I understand from Wikipedia, offsets in ISO timestamps represent the offset from UTC of a ISO 8601 string in the local (offset) time zone. That is, the time given before the Z or ±hh:mm is the local time, and the offset gives the difference between the local time and UTC.

Assuming this is correct, then this means that the following should all be equivalent:

2000-01-01T06:00:00Z 2000-01-01T06:00:00+00:00 2000-01-01T00:00:00-06:00 2000-01-01T12:00:00+06:00

All this given, I understand that the relationship between the local time local_time, UTC time utc_time and local time zone offset offset should be utc_time = local_time - offset. (Note the subtraction of the offset, not addition!)

Perhaps you might be able to explain whether this is correct? Some other people agree. Likewise the datetime standard library in Python agrees (note tm_hour=1 on the last line):

# datetime at 2000-01-01T00:00:00-01:00
dt = datetime.datetime(2000, 1, 1, 0, 0, 0, 0, datetime.timezone(datetime.timedelta(hours=-1)))

print(dt)
2000-01-01 00:00:00-01:00

# UTC date: 2000-01-01T01:00:00:00
print(dt.utctimetuple())
time.struct_time(tm_year=2000, tm_mon=1, tm_mday=1, tm_hour=1, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=0)

If the above is all correct, then I think that NanoDates has offsets implemented the wrong way round. One of the tests:

@test NanoDate(
    "2022-06-18T12:15:30.123456789-04:00",
    dateformat"yyyy-mm-ddTHH:MM:SS.sssssssss+hh:mm";
    localtime=true
) == NanoDate(2022, 06, 18, 8, 15, 30, 123, 456, 789)

As far as I can see, when localtime is true, then the offset should be ignored entirely, and this should give back NanoDate(2022, 06, 18, 12, 15, 30, 123, 456, 789) (hour is 12).

When localtime is false, on the other hand, it should give NanoDate(2022, 06, 18, 16, 15, 30, 123, 456, 789) (hour is 12 – (–4) = 16).

As far as I can see, to fix this, the behaviour of localtime in the NanoDate constructor should be switched, and the offset should be subtracted, not added, in getting the UTC time.

Am I right, or have I got horribly confused?

anowacki avatar Aug 16 '24 14:08 anowacki

I should add that when asking for a time with ndnow, the behaviour appears the right way round (given I am in British Summer Time, UTC+01:00):

julia> ndnow(LOCAL)
2024-08-20T20:57:25.694112083

julia> ndnow(UTC)
2024-08-20T19:57:25.695622333

and likewise timestamp looks to also agree:

julia> timestamp(ndnow(LOCAL), localtime=true)
"2024-08-20T20:58:00.141808500+01:00"

So I think the issue is just with parsing dates with offsets.

(Just to note my preference—I think that parsing dates with offsets should give you back UTC by default, with the option to ignore the offset and return the local time. At present, it's the other way round (even if it appears to have a bug!), so it would be a breaking change.)

anowacki avatar Aug 20 '24 20:08 anowacki

Looking into this -- thank you.

JeffreySarnoff avatar Aug 20 '24 20:08 JeffreySarnoff