regebro / tzlocal

A Python module that tries to figure out what your local timezone is

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

get_system_offset for Europe/Dublin is wrong

ilcavero opened this issue · comments

The code assumes that daylight savings implies that time.altzone is in use, but in the case of Ireland it is the other way around. https://mm.icann.org/pipermail/tz/2019-March/027829.html This is on Linux. I don't know what's the proper way of doing this on Python/Linux but assert_tz_offset throwing an exception on this time zone is not a good idea

import datetime
import time
import pytz
pytz.timezone('Europe/Dublin')
<DstTzInfo 'Europe/Dublin' LMT-1 day, 23:35:00 STD>
tz = pytz.timezone('Europe/Dublin')
int(datetime.datetime.now(tz).utcoffset().total_seconds())
0
if time.daylight and time.localtime().tm_isdst > 0:
... time.altzone
...
-3600

I don't see the problem, and it seems to be working fine, as far as I can tell. Can you please explain how to replicate the problem?

The problem is that there is an assumption that time.localtime().tm_isdst>0 implies that the offset is time.altzone and that's probably true for all timezones except Europe/Dublin.
set your timezone to Dublin and check out this output:

>>> time.altzone
-3600
>>> time.daylight
1
>>> time.timezone
0
>>> time.tzname
('GMT', 'IST')

current time is GMT right now in real life

I can't replicate the problem you seem to think there is.
If, as the discussion you link to seems to say, Ireland recently have flipped what is standard and what is altzone, then the time.timezone and time.altzone should also be flipped, and hence there should not be a problem. This has not happened on my system, though, so I can't verify that.

So please demonstrate that there is an actual error, not just a hypothetical one based on how you read the code and guess at the effects.

Hi, the problem is that assert_tz_offset(tz) which is called from unix.py always fails when your timezone is Europe/Dublin because in get_system_offset() the time.localtime().tm_isdst returns 1 right now because as in the icann database it considers the current GMT to be daylight time zone and the non-daylight zone is the irish standard time (IST) which is observed during the summer, other methods of the time module behave differently and consider GMT to be the non-daylight time (see time.tzname and time.altzone above).
This is not a hypothetical problem, I got here while debugging a service that wouldn't start and ended in your library after following the stacktrace.
I have reproduced this behaviour on linux and windows, but on macOs it behaves consistently and it is not reproduced.

"Hi, the problem is that assert_tz_offset(tz) which is called from unix.py always fails when your timezone is Europe/Dublin"

Does it fail with "ValueError: Timezone offset does not match system offset"?
Did you check that your pytz is updated to the latest version?

"get_system_offset() the time.localtime().tm_isdst returns 1 right now"

Right. So, according to what you say, tm_isdst returns 1 in the winter for Europe/Dublin on your computer. That also means that time.altzone should be in effect. Can you verify that this is the case?

Because if isdst is 1 and altzone is NOT in effect, that I would say is a bug in the operating systems timezone setup. There are plenty of timezones that has isdst a 1 in the summer (although they usually are in the southern hemisphere) and this causes no problems.

We are using pytz 2019.3.

Does it fail with "ValueError: Timezone offset does not match system offset"? Yes

Did you check that your pytz is updated to the latest version?
my pytz version

Name: pytz
Version: 2019.3
Summary: World timezone definitions, modern and historical
Home-page: http://pythonhosted.org/pytz
Author: Stuart Bishop
Author-email: stuart@stuartbishop.net
License: MIT
Location: /usr/lib/python3.7/site-packages
Requires:
Required-by: pyRFC3339, certbot, acme

I don't think it matter though because the root of the problem is an inconsistency between method calls in the time module

tm_isdst returns 1 in the winter for Europe/Dublin on your computer. That also means that time.altzone should be in effect altzone should be 0 because right now we are on GMT, it returns -3600 instead

There are plenty of timezones that has isdst a 1 in the summer (although they usually are in the southern hemisphere) and this causes no problems. I think the problem is that there are different opinion on whether Ireland is currently in DST or not and the python time module is not consistent with either of them, time.localtime seems to be sourced from the OS config where GMT is DST and other methods like time.altzone are sourced from somewhere else where IST is the DST

Yeah, you are right, on my Linux virtual box I get time.localtime().tm_isdst to 1 now, while altzone is -3600. On my mac box, time.localtime().tm_isdst is 0, interestingly enough (same altzone, thought).

This is certainly a bug, but it's unclear where that bug is (my bet is on either Linux, or Pythons interpretation of this), but it definitely not going to get fixed any time soon, and it also means that the is_dst flag is useless for the time being. I'll change the way the local DST offset is retrieved.

I can confirm I have the exact same issue and have independently traced in back to the same check: https://github.com/regebro/tzlocal/blob/master/tzlocal/utils.py#L15

Relevant stacktrace:

  File "/home/jjst/.virtualenvs/scrapy/lib/python3.6/site-packages/dateparser/conf.py", line 83, in wrapper                                                   
    return f(*args, **kwargs)                                                                                                                                 
  File "/home/jjst/.virtualenvs/scrapy/lib/python3.6/site-packages/dateparser/__init__.py", line 53, in parse                                                 
    data = parser.get_date_data(date_string, date_formats)                                                                                                    
  File "/home/jjst/.virtualenvs/scrapy/lib/python3.6/site-packages/dateparser/date.py", line 417, in get_date_data                                            
    locale, date_string, date_formats, settings=self._settings)                                                                                               
  File "/home/jjst/.virtualenvs/scrapy/lib/python3.6/site-packages/dateparser/date.py", line 189, in parse                                                    
    return instance._parse()                                                                                                                                  
  File "/home/jjst/.virtualenvs/scrapy/lib/python3.6/site-packages/dateparser/date.py", line 199, in _parse                                                   
    date_obj = parser()                                                                                                                                       
  File "/home/jjst/.virtualenvs/scrapy/lib/python3.6/site-packages/dateparser/date.py", line 212, in _try_freshness_parser                                    
    return freshness_date_parser.get_date_data(self._get_translated_date(), self._settings)                                                                   
  File "/home/jjst/.virtualenvs/scrapy/lib/python3.6/site-packages/dateparser/freshness_date_parser.py", line 147, in get_date_data                           
    date, period = self.parse(date_string, settings)                                                                                                          
  File "/home/jjst/.virtualenvs/scrapy/lib/python3.6/site-packages/dateparser/freshness_date_parser.py", line 94, in parse                                    
    self.now = datetime.now(self.get_local_tz())                                                                                                              
  File "/home/jjst/.virtualenvs/scrapy/lib/python3.6/site-packages/dateparser/freshness_date_parser.py", line 46, in get_local_tz                             
    return get_localzone()                                                                                                                                    
  File "/home/jjst/.virtualenvs/scrapy/lib/python3.6/site-packages/tzlocal/unix.py", line 165, in get_localzone                                               
    _cache_tz = _get_localzone()                                                                                                                              
  File "/home/jjst/.virtualenvs/scrapy/lib/python3.6/site-packages/tzlocal/unix.py", line 90, in _get_localzone                                               
    utils.assert_tz_offset(tz)                                                                                                                                
  File "/home/jjst/.virtualenvs/scrapy/lib/python3.6/site-packages/tzlocal/utils.py", line 38, in assert_tz_offset                                            
    raise ValueError(msg)                                                                                                                                     
ValueError: Timezone offset does not match system offset: 0 != 3600. Please, check your config files.  
In [17]: tz = pytz.timezone('Europe/Dublin')                                                                                                                  
                                                                                                                                                              
In [18]: int(datetime.datetime.now(tz).utcoffset().total_seconds())                                                                                           
    ...:                                                                                                                                                      
    ...:                                                                                                                                                      
Out[18]: 0                                                                                                                                                    
                                                                                                                                                              
In [19]: time.timezone                                                                                                                                        
Out[19]: 0                                                                                                                                                    
                                                                                                                                                              
In [20]: time.altzone                                                                                                                                         
Out[20]: -3600                                                                                                                                                
                                                                                                                                                              
In [21]: time.daylight                                                                                                                                        
Out[21]: 1                                                                                                                                                                                                                                   
                                                                                                                                                              
In [23]: time.localtime().tm_isdst                                                                                                                            
Out[23]: 1                                                                                                                                                    
                                                                                                                                                              
In [24]: time.daylight and time.localtime().tm_isdst > 0       
Out[24]: True

(Linux/Ubuntu 18.04, tzlocal 2.0.0)

It seems this check is making assumptions that cannot be correct for all timezone configurations.

@regebro any idea when this one will get fixed? Thanks!

I can confirm that I am having the same problem, which leads to:
ValueError: Timezone offset does not match system offset: 0 != 3600. Please, check your config files.
To test I have changed Dublin to Amsterdam and problem has disappeared.
Any ideas how to do it without manually switching timezones?

Thanks.