Source code for redis_tasks.smear_dst

import bisect
import datetime
from collections import namedtuple

import pytz
from pytz.tzinfo import DstTzInfo

Transition = namedtuple('Transition', [
    'start', 'end',
    'utc_start', 'utc_end',
    'old_utcoffset', 'new_utcoffset'])


[docs]class DstSmearingTz: def __init__(self, name): self._transition_times = [] self._transition_times_utc = [] self._transition_infos = [] self.tz = pytz.timezone(name) if isinstance(self.tz, DstTzInfo): self._load_transitions() def _load_transitions(self): prev_utcoffset = None HOUR = datetime.timedelta(hours=1) for transition_time, (utcoffset, dstoffset, tzname) in \ zip(self.tz._utc_transition_times, self.tz._transition_info): if prev_utcoffset is not None: if abs((prev_utcoffset - utcoffset).total_seconds()) == 3600: if prev_utcoffset < utcoffset: start = transition_time + prev_utcoffset - HOUR end = transition_time + utcoffset + HOUR else: start = transition_time + utcoffset end = transition_time + prev_utcoffset transition = Transition( start=start, end=end, utc_start=transition_time - HOUR, utc_end=transition_time + HOUR, old_utcoffset=prev_utcoffset, new_utcoffset=utcoffset, ) self._transition_times.append(start) self._transition_times.append(end) self._transition_times_utc.append(transition.utc_start) self._transition_times_utc.append(transition.utc_end) self._transition_infos.append((True, transition)) self._transition_infos.append((False, transition)) prev_utcoffset = utcoffset
[docs] def from_utc(self, utc): if utc.utcoffset() is None or utc.utcoffset().total_seconds() != 0: raise ValueError('Datetime is not in utc') if not self._transition_infos: return utc.astimezone(self.tz).replace(tzinfo=None) utc = utc.replace(tzinfo=None) idx = bisect.bisect_right(self._transition_times_utc, utc) - 1 in_trans, transition = self._transition_infos[idx] if not in_trans: return utc + transition.new_utcoffset else: percentage = (utc - transition.utc_start) / (transition.utc_end - transition.utc_start) utcoffset = (transition.old_utcoffset * (1 - percentage) + transition.new_utcoffset * percentage) return utc + utcoffset
[docs] def to_utc(self, dt): if dt.tzinfo is not None: raise ValueError('Not naive datetime (tzinfo is already set)') if not self._transition_infos: return self.tz.localize(dt).astimezone(pytz.UTC) idx = bisect.bisect_right(self._transition_times, dt) - 1 in_trans, transition = self._transition_infos[idx] if not in_trans: utc = dt - transition.new_utcoffset else: percentage = (dt - transition.start) / (transition.end - transition.start) utcoffset = (transition.old_utcoffset * (1 - percentage) + transition.new_utcoffset * percentage) utc = dt - utcoffset return utc.replace(tzinfo=pytz.UTC)