zopefoundation / DateTime

This package provides a DateTime data type, as known from Zope. Unless you need to communicate with Zope APIs, you're probably better off using Python's built-in datetime module.
Other
19 stars 25 forks source link

Timezone WAT converts to GMT-1 instead of GMT+1 #31

Closed ramonski closed 2 years ago

ramonski commented 2 years ago

BUG/PROBLEM REPORT (OR OTHER COMMON ISSUE)

The timezone Africa/Libreville converts to West African Time WAT and results in date objects using GMT-1 instead of GMT+1.

https://github.com/zopefoundation/DateTime/blob/master/src/DateTime/pytz_support.py#L178

What I did:

In my zope.conf I have this TZ setting:

TZ Africa/Libreville

This is the same time that is set on the OS:

root@senaite-test # timedatectl                                                                                                                                                                                         /home/senaite/senaite
                      Local time: Do 2022-02-10 19:57:11 WAT
                  Universal time: Do 2022-02-10 18:57:11 UTC
                        RTC time: Do 2022-02-10 18:57:11
                       Time zone: Africa/Libreville (WAT, +0100)
       System clock synchronized: yes
systemd-timesyncd.service active: yes
                 RTC in local TZ: no

And get converted to WAT:

>>> import time
>>> time.tzname
('WAT', 'WAT')

Although the current time is displayed correct at the time of instantiation:

(Pdb++) DateTime()
DateTime('2022/02/10 21:18:56.073462 GMT-1')

this call shows that it is actually a display (cache?) issue and the time is in UTC-1:

(Pdb++) DateTime().strftime("%H")
'19'

I believe that this is an error between the POSIX style notation and the GMT timezone that DateTime uses.

The Olson tz database defines Etc/GMT+N timezones which conform with the POSIX style:

Those zone names beginning with "Etc/GMT" have their sign reversed from the standard ISO 8601 convention. In the "Etc" area, zones west of GMT have a positive sign and those east have a negative sign in their name (e.g "Etc/GMT-14" is 14 hours ahead of GMT.)

https://en.wikipedia.org/wiki/Tz_database

What I expect to happen:

The timezone WAT should be UTC+1

What actually happened:

The timezone WAT is UTC-1

What version of Python and Zope/Addons I am using:

ramonski commented 2 years ago

My workaround for now is to use the Central European Time CET, which works correctly:

https://www.timeanddate.com/time/zones/wat

d-maurer commented 2 years ago

Ramon Bartl wrote at 2022-2-10 14:02 -0800:

The timezone Africa/Libreville converts to West African Time WAT and results in date objects using GMT-1 instead of GMT+1.

Would you check please whether https://github.com/zopefoundation/DateTime/pull/32 fixes the problem.

If you have a pip based installation, this would look like:

git clone -b 'fix_WAT#31' https://github.com/zopefoundation/DateTime.git
pip install -e DateTime
ramonski commented 2 years ago

Hi Dieter, thanks for your quick response and PR. This fixes the issue:

(Pdb++) import os
(Pdb++) os.environ["TZ"]
'Africa/Libreville'
(Pdb++) import time
(Pdb++) time.tzname
('WAT', 'WAT')
(Pdb++) DateTime()
DateTime('2022/02/11 08:17:51.225511 GMT+1')
(Pdb++) DateTime().tzoffset()
3600

However, what still confuses me is the wrong display of a DateTime object:

(Pdb++) os.environ['TZ'] = 'Africa/Accra'
(Pdb++) time.tzset()
(Pdb++) time.tzname
('GMT', 'GMT')
(Pdb++) DateTime()
DateTime('2022/02/11 07:53:24.598037 GMT+1')
(Pdb++) DateTime().tzoffset()
3600
(Pdb++) DateTime().strftime("%H")
'08'
(Pdb++) DateTime().ISO()
'2022-02-11T07:54:50+01:00'
(Pdb++) time.localtime()
time.struct_time(tm_year=2022, tm_mon=2, tm_mday=11, tm_hour=7, tm_min=54, tm_sec=10, tm_wday=4, tm_yday=42, tm_isdst=0)

Although the time is shown correctly for the set timezone, the displayed timezone is still stuck at GMT+1 and internally used?

ramonski commented 2 years ago

Ok, I think I found it here: https://github.com/zopefoundation/DateTime/blob/master/src/DateTime/DateTime.py#L176 tzname is imported only once at module level, so although the environment variable changed, it is still stuck at the old timezone. A solution would be to import tzname inside _findLocalTimeZoneName again or use it from the time module directly, e.g. time.tzname.

d-maurer commented 2 years ago

Ramon Bartl wrote at 2022-2-11 00:45 -0800:

Ok, I think I found it here: https://github.com/zopefoundation/DateTime/blob/master/src/DateTime/DateTime.py#L176 tzname is imported only once at module level, so although the environment variable changed, it is still stuck at the old timezone. A solution would be to import tzname inside _findLocalTimeZoneName again or use it from the time module directly, e.g. time.tzname.

time.tzname does not guarantee to auto update to changes of environment variables. An explicit call to time.tzset is required.

This demonstrates that changes to timezone related envvars (i.e. TZ) after process start are considered rare and require special measures from the changing application.

When you see the correct timezone if TZ remains unchanged since process start, likely no change is necessary.

ramonski commented 2 years ago

Well, these ones had to be change then as well:

_localzone0 = _findLocalTimeZoneName(0)
_localzone1 = _findLocalTimeZoneName(1)
_multipleZones = (_localzone0 != _localzone1)

so that in localZone and in _calcTimezoneName the function _findLocalTimeZoneName would be called

Then it would behave correctly:

>>> from DateTime import DateTime
>>> DateTime()
DateTime('2022/02/11 09:56:47.025072 GMT+1')
>>> import time
>>> import os
>>> os.environ["TZ"] = "Africa/Accra"
>>> time.tzset()
>>> DateTime()
DateTime('2022/02/11 08:58:0.949750 GMT')
>>> DateTime().tzoffset()
0
>>> DateTime().ISO()
'2022-02-11 08:59:22'
ramonski commented 2 years ago

time.tzname does not guarantee to auto update to changes of environment variables. An explicit call to time.tzset is required.

Yes you are right. It is unlikely that the timezone changes in between. Would be nice for testing purposes though.

d-maurer commented 2 years ago

Ramon Bartl wrote at 2022-2-11 01:04 -0800:

time.tzname does not guarantee to auto update to changes of environment variables. An explicit call to time.tzset is required.

Yes you are right. It is unlikely that the timezone changes in between. Would be nice for testing purposes though.

Would you like to become a Zope contributor? You would then be able to implement the improvements yourself.

ramonski commented 2 years ago

Yes of course, that would be nice. However, these timezones are kind of haunting me and will probably driving me nuts sooner or later. But at least some other people can review my code and give me feedback if my changes have side effects that I have maybe overlooked. Thanks again Dieter and from my point of view #32 can be merged.

d-maurer commented 2 years ago

Ramon Bartl wrote at 2022-2-11 02:59 -0800:

Yes of course, that would be nice.

You would need to sign a contributor agreement.

Search plone.org for the document. Apparently, there are two contibutor types "Zope" and "Plone". For write access to zopefoundation, you need to select "Zope".

dataflake commented 2 years ago

No need to search, all details are at https://www.zope.dev/developer/becoming-a-committer.html

icemac commented 2 years ago

Fix released as https://pypi.org/project/DateTime/4.4/.

ramonski commented 2 years ago

Thanks @dataflake for sharing the link, I signed the contract already and sent it back. Also thanks @icemac for the quick release!