Fix improper usage of time.tzset() (#347)

* Fix improper usage of time.tzset()

time.tzname is a tuple containing the non-daylight-savings and
daylight-savings timezone abbreviations. However, when the TZ
environment variable is set to just the daylight-savings timezone (as
the clock module was changed to do in e31c58f), time.tzset() will break
time.tzname by setting both elements of the tuple to that timezone,
causing the effective timezone to fallback to UTC:

>>> time.tzname
('CST', 'CDT')
>>> time.localtime().tm_hour
1
>>> os.environ.putenv('TZ', 'CST')
>>> time.tzset()
>>> time.tzname
('CST', 'CST')
>>> # ^^^ This is broken
...
>>> time.localtime().tm_hour
6
>>> os.environ.putenv('TZ', 'CST+06:00CDT')
>>> time.tzset()
>>> time.tzname
('CST', 'CDT')
>>> time.localtime().tm_hour
1

This fixes this incorrect behavior by building a proper TZ environment
variable to set localtime.

* Use time.timezone instead of time.altzone

* Make _get_local_tz a static method
This commit is contained in:
Erik Johnson 2016-04-07 16:21:23 -05:00 committed by enkore
parent c0cdfae1f8
commit 7cb2dcc255

View File

@ -84,8 +84,25 @@ class Clock(IntervalModule):
elif isinstance(self.format, str) or isinstance(self.format, tuple): elif isinstance(self.format, str) or isinstance(self.format, tuple):
self.format = [self.format] self.format = [self.format]
self._local_tzname = self._get_local_tz()
self._non_daylight_zone = time.tzname[0]
self.format = self.expand_formats(self.format) self.format = self.expand_formats(self.format)
@staticmethod
def _get_local_tz():
'''
Returns a string representing localtime, suitable for setting localtime
using time.tzset().
https://docs.python.org/3/library/time.html#time.tzset
'''
hours_offset = time.timezone / 3600.0
plus_minus = '+' if hours_offset >= 0 else '-'
hh = int(hours_offset)
mm = 60 * (hours_offset % 1)
return '%s%s%02d:%02d%s' % (time.tzname[0], plus_minus,
hh, mm, time.tzname[1])
@staticmethod @staticmethod
def expand_formats(formats): def expand_formats(formats):
def expand_format(format_): def expand_format(format_):
@ -94,15 +111,18 @@ class Clock(IntervalModule):
if len(format_) > 1 and os.path.isfile('/usr/share/zoneinfo/' + format_[1]): if len(format_) > 1 and os.path.isfile('/usr/share/zoneinfo/' + format_[1]):
return (format_[0], format_[1]) return (format_[0], format_[1])
else: else:
return (format_[0], time.tzname[0]) return (format_[0], None)
return (format_, time.tzname[0]) return (format_, None)
return [expand_format(format_) for format_ in formats] return [expand_format(format_) for format_ in formats]
def run(self): def run(self):
# set timezone # set timezone
if time.tzname[0] is not self.format[self.current_format_id][1]: target_tz = self.format[self.current_format_id][1]
os.environ.putenv('TZ', self.format[self.current_format_id][1]) if target_tz is None and time.tzname[0] != self._non_daylight_zone \
or target_tz is not None and time.tzname[0] != target_tz:
new_tz = self._local_tzname if target_tz is None else target_tz
os.environ.putenv('TZ', new_tz)
time.tzset() time.tzset()
self.output = { self.output = {