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.2.7, created at 2023-06-07 06:35 +0000

1from datetime import datetime 

2from typing import List 

3from typing import Optional 

4 

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 

11 

12from .posix_timezone import PosixTimezone 

13from .transition import Transition 

14from .transition_type import TransitionType 

15 

16 

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 

26 

27 if extended: 

28 self._extends() 

29 

30 @property 

31 def transitions(self): # type: () -> List[Transition] 

32 return self._transitions 

33 

34 @property 

35 def posix_rule(self): 

36 return self._posix_rule 

37 

38 def _extends(self): 

39 if not self._posix_rule: 

40 return 

41 

42 posix = self._posix_rule 

43 

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") 

50 

51 return 

52 

53 if len(self._transitions) < 2: 

54 raise ValueError("Too few transitions for POSIX spec") 

55 

56 # Extend the transitions for an additional 400 years 

57 # using the future specification 

58 

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 

71 

72 self._check_ttype(dst, posix.dst_offset, True, posix.dst_abbr) 

73 self._check_ttype(std, posix.std_offset, False, posix.std_abbr) 

74 

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 

81 

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 

88 

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 

95 

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 

102 

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) 

108 

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) 

112 

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) 

116 

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 )