Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pendulum/tz/zoneinfo/timezone.py: 25%
79 statements
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
1from datetime import datetime
2from typing import List
3from typing import Optional
5from pendulum.constants import DAYS_PER_YEAR
6from pendulum.constants import SECS_PER_YEAR
7from pendulum.helpers import is_leap
8from pendulum.helpers import local_time
9from pendulum.helpers import timestamp
10from pendulum.helpers import week_day
12from .posix_timezone import PosixTimezone
13from .transition import Transition
14from .transition_type import TransitionType
17class Timezone:
18 def __init__(
19 self,
20 transitions, # type: List[Transition]
21 posix_rule=None, # type: Optional[PosixTimezone]
22 extended=True, # type: bool
23 ):
24 self._posix_rule = posix_rule
25 self._transitions = transitions
27 if extended:
28 self._extends()
30 @property
31 def transitions(self): # type: () -> List[Transition]
32 return self._transitions
34 @property
35 def posix_rule(self):
36 return self._posix_rule
38 def _extends(self):
39 if not self._posix_rule:
40 return
42 posix = self._posix_rule
44 if not posix.dst_abbr:
45 # std only
46 # The future specification should match the last/default transition
47 ttype = self._transitions[-1].ttype
48 if not self._check_ttype(ttype, posix.std_offset, False, posix.std_abbr):
49 raise ValueError("Posix spec does not match last transition")
51 return
53 if len(self._transitions) < 2:
54 raise ValueError("Too few transitions for POSIX spec")
56 # Extend the transitions for an additional 400 years
57 # using the future specification
59 # The future specification should match the last two transitions,
60 # and those transitions should have different is_dst flags.
61 tr0 = self._transitions[-1]
62 tr1 = self._transitions[-2]
63 tt0 = tr0.ttype
64 tt1 = tr1.ttype
65 if tt0.is_dst():
66 dst = tt0
67 std = tt1
68 else:
69 dst = tt1
70 std = tt0
72 self._check_ttype(dst, posix.dst_offset, True, posix.dst_abbr)
73 self._check_ttype(std, posix.std_offset, False, posix.std_abbr)
75 # Add the transitions to tr1 and back to tr0 for each extra year.
76 last_year = local_time(tr0.local, 0, 0)[0]
77 leap_year = is_leap(last_year)
78 jan1 = datetime(last_year, 1, 1)
79 jan1_time = timestamp(jan1)
80 jan1_weekday = week_day(jan1.year, jan1.month, jan1.day) % 7
82 if local_time(tr1.local, 0, 0)[0] != last_year:
83 # Add a single extra transition to align to a calendar year.
84 if tt0.is_dst():
85 pt1 = posix.dst_end
86 else:
87 pt1 = posix.dst_start
89 tr1_offset = pt1.trans_offset(leap_year, jan1_weekday)
90 tr = Transition(jan1_time + tr1_offset - tt0.offset, tr1.ttype, tr0)
91 tr0 = tr
92 tr1 = tr0
93 tt0 = tr0.ttype
94 tt1 = tr1.ttype
96 if tt0.is_dst():
97 pt1 = posix.dst_end
98 pt0 = posix.dst_start
99 else:
100 pt1 = posix.dst_start
101 pt0 = posix.dst_end
103 tr = tr0
104 for year in range(last_year + 1, last_year + 401):
105 jan1_time += SECS_PER_YEAR[leap_year]
106 jan1_weekday = (jan1_weekday + DAYS_PER_YEAR[leap_year]) % 7
107 leap_year = not leap_year and is_leap(year)
109 tr1_offset = pt1.trans_offset(leap_year, jan1_weekday)
110 tr = Transition(jan1_time + tr1_offset - tt0.offset, tt1, tr)
111 self._transitions.append(tr)
113 tr0_offset = pt0.trans_offset(leap_year, jan1_weekday)
114 tr = Transition(jan1_time + tr0_offset - tt1.offset, tt0, tr)
115 self._transitions.append(tr)
117 def _check_ttype(
118 self,
119 ttype, # type: TransitionType
120 offset, # type: int
121 is_dst, # type: bool
122 abbr, # type: str
123 ): # type: (...) -> bool
124 return (
125 ttype.offset == offset
126 and ttype.is_dst() == is_dst
127 and ttype.abbreviation == abbr
128 )