From 7cb2dcc25579569701e8e2404a93d043eba09328 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Thu, 7 Apr 2016 16:21:23 -0500 Subject: [PATCH] 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 --- i3pystatus/clock.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/i3pystatus/clock.py b/i3pystatus/clock.py index dc6532d..6e657c4 100644 --- a/i3pystatus/clock.py +++ b/i3pystatus/clock.py @@ -84,8 +84,25 @@ class Clock(IntervalModule): elif isinstance(self.format, str) or isinstance(self.format, tuple): 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) + @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 def expand_formats(formats): def expand_format(format_): @@ -94,15 +111,18 @@ class Clock(IntervalModule): if len(format_) > 1 and os.path.isfile('/usr/share/zoneinfo/' + format_[1]): return (format_[0], format_[1]) else: - return (format_[0], time.tzname[0]) - return (format_, time.tzname[0]) + return (format_[0], None) + return (format_, None) return [expand_format(format_) for format_ in formats] def run(self): # set timezone - if time.tzname[0] is not self.format[self.current_format_id][1]: - os.environ.putenv('TZ', self.format[self.current_format_id][1]) + target_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() self.output = {