@@ -165,50 +165,33 @@ def _get_dates_generator(
165165
166166 if sys .version_info >= (3 , 13 ):
167167 # `pendulum` is not supported in Python 3.13, so we use `whenever` instead
168- from whenever import ZonedDateTime
168+ from whenever import PlainDateTime , ZonedDateTime
169169
170170 if start is None :
171171 start = ZonedDateTime .now ("UTC" ).py_datetime ()
172172
173- if self .anchor_date .tzinfo is None :
174- from whenever import PlainDateTime
175-
176- anchor_zdt = PlainDateTime .from_py_datetime (self .anchor_date ).assume_tz (
177- "UTC"
178- )
179- elif isinstance (self .anchor_date .tzinfo , ZoneInfo ):
180- anchor_zdt = ZonedDateTime .from_py_datetime (self .anchor_date ).to_tz (
181- self .timezone or "UTC"
173+ target_timezone = self .timezone or "UTC"
174+
175+ def to_local_zdt (dt : datetime .datetime | None ) -> ZonedDateTime | None :
176+ if dt is None :
177+ return None
178+ if dt .tzinfo is None :
179+ return PlainDateTime .from_py_datetime (dt ).assume_tz (target_timezone )
180+ if isinstance (dt .tzinfo , ZoneInfo ):
181+ return ZonedDateTime .from_py_datetime (dt ).to_tz (target_timezone )
182+ # For offset-based tzinfo instances (e.g. datetime.timezone(+09:00)),
183+ # use astimezone to preserve the instant, then convert to ZonedDateTime.
184+ return ZonedDateTime .from_py_datetime (
185+ dt .astimezone (ZoneInfo (target_timezone ))
182186 )
183- else :
184- # This case handles rogue tzinfo objects that `whenever` doesn't play will with
185- anchor_zdt = ZonedDateTime .from_py_datetime (
186- self .anchor_date .replace (
187- tzinfo = ZoneInfo (self .anchor_date .tzname () or "UTC" )
188- )
189- ).to_tz (self .timezone or "UTC" )
190187
191- if start .tzinfo is None :
192- local_start = PlainDateTime .from_py_datetime (start ).assume_tz ("UTC" )
193- elif isinstance (start .tzinfo , ZoneInfo ):
194- local_start = ZonedDateTime .from_py_datetime (start ).to_tz (
195- self .timezone or "UTC"
196- )
197- else :
198- local_start = ZonedDateTime .from_py_datetime (
199- start .replace (tzinfo = ZoneInfo (start .tzname () or "UTC" ))
200- ).to_tz (self .timezone or "UTC" )
201-
202- if end is None :
203- local_end = None
204- elif isinstance (end .tzinfo , ZoneInfo ):
205- local_end = ZonedDateTime .from_py_datetime (end ).to_tz (
206- self .timezone or "UTC"
207- )
208- else :
209- local_end = ZonedDateTime .from_py_datetime (
210- end .replace (tzinfo = ZoneInfo (end .tzname () or "UTC" ))
211- ).to_tz (self .timezone or "UTC" )
188+ anchor_zdt = to_local_zdt (self .anchor_date )
189+ assert anchor_zdt is not None
190+
191+ local_start = to_local_zdt (start )
192+ assert local_start is not None
193+
194+ local_end = to_local_zdt (end )
212195
213196 offset = (
214197 local_start - anchor_zdt
0 commit comments