|
@@ -1,10 +1,12 @@
|
|
|
from __future__ import annotations
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
-from datetime import datetime, timedelta, timezone
|
|
|
|
|
|
|
+from datetime import datetime, timedelta
|
|
|
from zoneinfo import ZoneInfo
|
|
from zoneinfo import ZoneInfo
|
|
|
|
|
|
|
|
from supply_infra.config import InfraSettings, get_infra_settings
|
|
from supply_infra.config import InfraSettings, get_infra_settings
|
|
|
|
|
|
|
|
|
|
+CHINA_TIMEZONE = ZoneInfo("Asia/Shanghai")
|
|
|
|
|
+
|
|
|
|
|
|
|
|
def validate_biz_dt(value: str) -> str:
|
|
def validate_biz_dt(value: str) -> str:
|
|
|
text = str(value).strip()
|
|
text = str(value).strip()
|
|
@@ -41,11 +43,12 @@ def build_date_snapshot(biz_dt: str) -> dict[str, str]:
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
-def utc_now() -> datetime:
|
|
|
|
|
- return datetime.now(timezone.utc).replace(tzinfo=None)
|
|
|
|
|
|
|
+def china_now() -> datetime:
|
|
|
|
|
+ """Return a naive China Standard Time value for MySQL DATETIME columns."""
|
|
|
|
|
+ return datetime.now(CHINA_TIMEZONE).replace(tzinfo=None)
|
|
|
|
|
|
|
|
|
|
|
|
|
-def next_schedule_utc(
|
|
|
|
|
|
|
+def next_schedule_china(
|
|
|
*,
|
|
*,
|
|
|
settings: InfraSettings | None = None,
|
|
settings: InfraSettings | None = None,
|
|
|
now: datetime | None = None,
|
|
now: datetime | None = None,
|
|
@@ -65,15 +68,15 @@ def next_schedule_utc(
|
|
|
)
|
|
)
|
|
|
if candidate <= local_now:
|
|
if candidate <= local_now:
|
|
|
candidate += timedelta(days=1)
|
|
candidate += timedelta(days=1)
|
|
|
- return candidate.astimezone(timezone.utc).replace(tzinfo=None)
|
|
|
|
|
|
|
+ return candidate.astimezone(CHINA_TIMEZONE).replace(tzinfo=None)
|
|
|
|
|
|
|
|
|
|
|
|
|
-def next_daily_deadline_utc(
|
|
|
|
|
|
|
+def next_daily_deadline_china(
|
|
|
*,
|
|
*,
|
|
|
settings: InfraSettings | None = None,
|
|
settings: InfraSettings | None = None,
|
|
|
now: datetime | None = None,
|
|
now: datetime | None = None,
|
|
|
) -> datetime:
|
|
) -> datetime:
|
|
|
- """Return the next local calendar day's scheduler start as UTC-naive."""
|
|
|
|
|
|
|
+ """Return the next local calendar day's scheduler start as China-time naive."""
|
|
|
active = settings or get_infra_settings()
|
|
active = settings or get_infra_settings()
|
|
|
zone = ZoneInfo(active.scheduler_timezone)
|
|
zone = ZoneInfo(active.scheduler_timezone)
|
|
|
local_now = now or datetime.now(zone)
|
|
local_now = now or datetime.now(zone)
|
|
@@ -87,7 +90,7 @@ def next_daily_deadline_utc(
|
|
|
second=0,
|
|
second=0,
|
|
|
microsecond=0,
|
|
microsecond=0,
|
|
|
)
|
|
)
|
|
|
- return candidate.astimezone(timezone.utc).replace(tzinfo=None)
|
|
|
|
|
|
|
+ return candidate.astimezone(CHINA_TIMEZONE).replace(tzinfo=None)
|
|
|
|
|
|
|
|
|
|
|
|
|
def deadline_for_trigger(
|
|
def deadline_for_trigger(
|
|
@@ -99,4 +102,4 @@ def deadline_for_trigger(
|
|
|
"""Scheduled daily runs have a next-day deadline; manual runs do not."""
|
|
"""Scheduled daily runs have a next-day deadline; manual runs do not."""
|
|
|
if trigger_type not in {"cron", "reconcile"}:
|
|
if trigger_type not in {"cron", "reconcile"}:
|
|
|
return None
|
|
return None
|
|
|
- return next_daily_deadline_utc(settings=settings, now=now)
|
|
|
|
|
|
|
+ return next_daily_deadline_china(settings=settings, now=now)
|