def fromutc(self, dt): # type: (_D) -> _D stamp = timestamp(dt) transition = self._lookup_transition(stamp, is_utc=True) if stamp < transition.at and transition.previous is not None: transition = transition.previous stamp += transition.ttype.offset return dt.__class__(*local_time(stamp, 0, dt.microsecond), tzinfo=self)
def _normalize( self, dt, dst_rule=None # type: datetime # type: Union[str, None] ): # type: (...) -> datetime sec = timestamp(dt) fold = 0 transition = self._lookup_transition(sec) if not _HAS_FOLD and dst_rule is None: dst_rule = POST_TRANSITION if dst_rule is None: dst_rule = PRE_TRANSITION if dt.fold == 1: dst_rule = POST_TRANSITION if sec < transition.local: if transition.is_ambiguous(sec): # Ambiguous time if dst_rule == TRANSITION_ERROR: raise AmbiguousTime(dt) # We set the fold attribute for later if dst_rule == POST_TRANSITION: fold = 1 else: transition = transition.previous if transition.is_ambiguous(sec): # Ambiguous time if dst_rule == TRANSITION_ERROR: raise AmbiguousTime(dt) # We set the fold attribute for later if dst_rule == POST_TRANSITION: fold = 1 elif transition.is_missing(sec): # Skipped time if dst_rule == TRANSITION_ERROR: raise NonExistingTime(dt) # We adjust accordingly if dst_rule == POST_TRANSITION: sec += transition.fix fold = 1 else: sec -= transition.fix kwargs = {"tzinfo": self} if _HAS_FOLD or isinstance(dt, pendulum.DateTime): kwargs["fold"] = fold return dt.__class__(*local_time(sec, 0, dt.microsecond), **kwargs)
def _get_transition(self, dt): # type: (_datetime) -> Transition if dt.tzinfo is not None and dt.tzinfo is not self: dt = dt - dt.utcoffset() stamp = timestamp(dt) transition = self._lookup_transition(stamp, is_utc=True) else: stamp = timestamp(dt) transition = self._lookup_transition(stamp) if stamp < transition.local and transition.previous is not None: fold = getattr(dt, "fold", 1) if transition.is_ambiguous(stamp): if fold == 0: transition = transition.previous elif transition.previous.is_ambiguous(stamp) and fold == 0: pass else: transition = transition.previous return transition
def _convert(self, dt): # type: (datetime) -> datetime if dt.tzinfo is self: return self._normalize(dt, dst_rule=POST_TRANSITION) if not isinstance(dt.tzinfo, Timezone): return dt.astimezone(self) stamp = timestamp(dt) if isinstance(dt.tzinfo, FixedTimezone): offset = dt.tzinfo.offset else: transition = dt.tzinfo._lookup_transition(stamp) offset = transition.ttype.offset if stamp < transition.local: if (transition.previous.is_ambiguous(stamp) and getattr(dt, 'fold', 1) == 0): pass else: offset = transition.previous.ttype.offset stamp -= offset transition = self._lookup_transition(stamp, is_utc=True) if stamp < transition.at: transition = transition.previous offset = transition.ttype.offset stamp += offset fold = int(not transition.ttype.is_dst()) kwargs = { 'tzinfo': self, } if _HAS_FOLD or isinstance(dt, pendulum.DateTime): kwargs['fold'] = fold return dt.__class__(*local_time(stamp, 0, dt.microsecond), **kwargs)
def _extends(self): if not self._posix_rule: return posix = self._posix_rule if not posix.dst_abbr: # std only # The future specification should match the last/default transition ttype = self._transitions[-1].ttype if not self._check_ttype(ttype, posix.std_offset, False, posix.std_abbr): raise ValueError("Posix spec does not match last transition") return if len(self._transitions) < 2: raise ValueError("Too few transitions for POSIX spec") # Extend the transitions for an additional 400 years # using the future specification # The future specification should match the last two transitions, # and those transitions should have different is_dst flags. tr0 = self._transitions[-1] tr1 = self._transitions[-2] tt0 = tr0.ttype tt1 = tr1.ttype if tt0.is_dst(): dst = tt0 std = tt1 else: dst = tt1 std = tt0 self._check_ttype(dst, posix.dst_offset, True, posix.dst_abbr) self._check_ttype(std, posix.std_offset, False, posix.std_abbr) # Add the transitions to tr1 and back to tr0 for each extra year. last_year = local_time(tr0.local, 0, 0)[0] leap_year = is_leap(last_year) jan1 = datetime(last_year, 1, 1) jan1_time = timestamp(jan1) jan1_weekday = week_day(jan1.year, jan1.month, jan1.day) % 7 if local_time(tr1.local, 0, 0)[0] != last_year: # Add a single extra transition to align to a calendar year. if tt0.is_dst(): pt1 = posix.dst_end else: pt1 = posix.dst_start tr1_offset = pt1.trans_offset(leap_year, jan1_weekday) tr = Transition(jan1_time + tr1_offset - tt0.offset, tr1.ttype, tr0) tr0 = tr tr1 = tr0 tt0 = tr0.ttype tt1 = tr1.ttype if tt0.is_dst(): pt1 = posix.dst_end pt0 = posix.dst_start else: pt1 = posix.dst_start pt0 = posix.dst_end tr = tr0 for year in range(last_year + 1, last_year + 401): jan1_time += SECS_PER_YEAR[leap_year] jan1_weekday = (jan1_weekday + DAYS_PER_YEAR[leap_year]) % 7 leap_year = not leap_year and is_leap(year) tr1_offset = pt1.trans_offset(leap_year, jan1_weekday) tr = Transition(jan1_time + tr1_offset - tt0.offset, tt1, tr) self._transitions.append(tr) tr0_offset = pt0.trans_offset(leap_year, jan1_weekday) tr = Transition(jan1_time + tr0_offset - tt1.offset, tt0, tr) self._transitions.append(tr)
def _extends(self): if not self._posix_rule: return posix = self._posix_rule if not posix.dst_abbr: # std only # The future specification should match the last/default transition ttype = self._transitions[-1].ttype if not self._check_ttype(ttype, posix.std_offset, False, posix.std_abbr): raise ValueError("Posix spec does not match last transition") return if len(self._transitions) < 2: raise ValueError("Too few transitions for POSIX spec") # Extend the transitions for an additional 400 years # using the future specification # The future specification should match the last two transitions, # and those transitions should have different is_dst flags. tr0 = self._transitions[-1] tr1 = self._transitions[-2] tt0 = tr0.ttype tt1 = tr1.ttype if tt0.is_dst(): dst = tt0 std = tt1 else: dst = tt1 std = tt0 self._check_ttype(dst, posix.dst_offset, True, posix.dst_abbr) self._check_ttype(std, posix.std_offset, False, posix.std_abbr) # Add the transitions to tr1 and back to tr0 for each extra year. last_year = local_time(tr0.local, 0, 0)[0] leap_year = is_leap(last_year) jan1 = datetime(last_year, 1, 1) jan1_time = timestamp(jan1) jan1_weekday = week_day(jan1.year, jan1.month, jan1.day) % 7 if local_time(tr1.local, 0, 0)[0] != last_year: # Add a single extra transition to align to a calendar year. if tt0.is_dst(): pt1 = posix.dst_end else: pt1 = posix.dst_start tr1_offset = pt1.trans_offset(leap_year, jan1_weekday) tr = Transition(jan1_time + tr1_offset - tt0.offset, tr1.ttype, tr0) tr0 = tr tr1 = tr0 tt0 = tr0.ttype tt1 = tr1.ttype if tt0.is_dst(): pt1 = posix.dst_end pt0 = posix.dst_start else: pt1 = posix.dst_start pt0 = posix.dst_end tr = tr0 for year in range(last_year + 1, last_year + 401): jan1_time += SECS_PER_YEAR[leap_year] jan1_weekday = (jan1_weekday + DAYS_PER_YEAR[leap_year]) % 7 leap_year = not leap_year and is_leap(year) tr1_offset = pt1.trans_offset(leap_year, jan1_weekday) tr = Transition(jan1_time + tr1_offset - tt0.offset, tt1, tr) self._transitions.append(tr) tr0_offset = pt0.trans_offset(leap_year, jan1_weekday) tr = Transition(jan1_time + tr0_offset - tt1.offset, tt0, tr) self._transitions.append(tr)