11from __future__ import annotations
22
33import typing as t
4+ import zoneinfo
45from datetime import datetime
56from enum import Enum
67from pathlib import Path
@@ -177,6 +178,7 @@ class _Node(PydanticModel):
177178 the date from the scheduler will be used
178179 cron: A cron string specifying how often the node should be run, leveraging the
179180 [croniter](https://github.com/kiorky/croniter) library.
181+ cron_tz: Time zone for the cron, defaults to utc, [IANA time zones](https://docs.python.org/3/library/zoneinfo.html).
180182 interval_unit: The duration of an interval for the node. By default, it is computed from the cron expression.
181183 tags: A list of tags that can be used to filter nodes.
182184 stamp: An optional arbitrary string sequence used to create new node versions without making
@@ -190,6 +192,7 @@ class _Node(PydanticModel):
190192 start : t .Optional [TimeLike ] = None
191193 end : t .Optional [TimeLike ] = None
192194 cron : SQLGlotCron = "@daily"
195+ cron_tz : t .Optional [zoneinfo .ZoneInfo ] = None
193196 interval_unit_ : t .Optional [IntervalUnit ] = Field (alias = "interval_unit" , default = None )
194197 tags : t .List [str ] = []
195198 stamp : t .Optional [str ] = None
@@ -226,6 +229,22 @@ def _name_validator(cls, v: t.Any) -> t.Optional[str]:
226229 return v .meta ["sql" ]
227230 return str (v )
228231
232+ @field_validator ("cron_tz" , mode = "before" )
233+ def _cron_tz_validator (cls , v : t .Any ) -> t .Optional [zoneinfo .ZoneInfo ]:
234+ if not v or v == "UTC" :
235+ return None
236+
237+ v = str_or_exp_to_str (v )
238+
239+ try :
240+ return zoneinfo .ZoneInfo (v )
241+ except Exception as e :
242+ raise ConfigError (
243+ f"{ e } . { v } must be in { zoneinfo .available_timezones ()} or IANA time zone data is not available on your system. `pip install tzdata` to leverage cron time zones or remove this field which will default to UTC."
244+ )
245+
246+ return None
247+
229248 @field_validator ("start" , "end" , mode = "before" )
230249 @classmethod
231250 def _date_validator (cls , v : t .Any ) -> t .Optional [TimeLike ]:
@@ -319,7 +338,7 @@ def croniter(self, value: TimeLike) -> CroniterCache:
319338 if self ._croniter is None :
320339 self ._croniter = CroniterCache (self .cron , value )
321340 else :
322- self ._croniter .curr = to_datetime (value )
341+ self ._croniter .curr = to_datetime (value , tz = self . cron_tz )
323342 return self ._croniter
324343
325344 def cron_next (self , value : TimeLike , estimate : bool = False ) -> datetime :
0 commit comments