@@ -72,7 +72,7 @@ def find_timezone_transitions(
7272 """
7373 transitions = []
7474
75- # Sample monthly to detect transitions
75+ # Sample daily to detect transition windows
7676 current = start_dt
7777 prev_info = None
7878
@@ -84,22 +84,63 @@ def find_timezone_transitions(
8484 info ["utc_offset_seconds" ] != prev_info ["utc_offset_seconds" ]
8585 or info ["is_dst" ] != prev_info ["is_dst" ]
8686 ):
87- # Transition detected, add it
87+ # Transition detected between prev_time and current
88+ # Use binary search to find exact transition time (within 1 minute)
89+ prev_time = current - timedelta (days = 1 )
90+ transition_time = _find_exact_transition (tz_name , prev_time , current , prev_info , info )
91+
8892 transitions .append (
8993 {
90- "from_datetime" : current .isoformat (),
94+ "from_datetime" : transition_time .isoformat (),
9195 "utc_offset_seconds" : info ["utc_offset_seconds" ],
9296 "is_dst" : info ["is_dst" ],
9397 "abbreviation" : info ["abbreviation" ],
9498 }
9599 )
96100
97101 prev_info = info
98- current += timedelta (days = 30 ) # Sample monthly
102+ current += timedelta (days = 1 ) # Sample daily
99103
100104 return transitions
101105
102106
107+ def _find_exact_transition (
108+ tz_name : str ,
109+ start : datetime ,
110+ end : datetime ,
111+ prev_info : dict [str , str | int | bool ],
112+ new_info : dict [str , str | int | bool ],
113+ ) -> datetime :
114+ """Binary search to find exact transition time within a window.
115+
116+ Args:
117+ tz_name: IANA timezone identifier
118+ start: Start of window (before transition)
119+ end: End of window (after transition)
120+ prev_info: Timezone info before transition
121+ new_info: Timezone info after transition
122+
123+ Returns:
124+ Datetime of transition (accurate to ~1 minute)
125+ """
126+ # Binary search with 1-minute precision
127+ while (end - start ).total_seconds () > 60 :
128+ mid = start + (end - start ) / 2
129+ mid_info = get_timezone_info_at_datetime (tz_name , mid )
130+
131+ if (
132+ mid_info ["utc_offset_seconds" ] == prev_info ["utc_offset_seconds" ]
133+ and mid_info ["is_dst" ] == prev_info ["is_dst" ]
134+ ):
135+ # Still in old state, transition is after mid
136+ start = mid
137+ else :
138+ # In new state, transition is before mid
139+ end = mid
140+
141+ return end
142+
143+
103144def list_all_timezones (
104145 country_code : str | None = None , search : str | None = None
105146) -> list [dict [str , str | None ]]:
0 commit comments