fix(scheduler): honor app timezone for cron triggers and log scheduled events

CronTrigger.from_crontab was constructed without a timezone, so a cron like
'0 9 * * *' fired at 09:00 host-local instead of 09:00 in the admin-configured
timezone. Now all tracker/action cron triggers are built with the app tz, and
the setting endpoint rebuilds existing cron jobs when the tz changes (since
CronTrigger freezes its tz at construction time).

The scheduler provider also renders current_date/time/datetime/weekday in the
configured tz and exposes a new 'timezone' template variable.

EventLog entries for scheduled_message now include schedule_type,
cron_expression/interval_seconds, timezone, and fire_count, and the dashboard
shows the event type with a label/icon/color.
This commit is contained in:
2026-04-23 13:35:49 +03:00
parent 5604c733d1
commit 1024085cdd
10 changed files with 209 additions and 17 deletions
@@ -94,6 +94,7 @@ async def update_settings(
old_base_url = await get_setting(session, "external_url")
old_secret = await get_setting(session, "telegram_webhook_secret")
old_cache_values = {k: await get_setting(session, k) for k in _CACHE_SETTING_KEYS}
old_timezone = await get_setting(session, "timezone")
for key in _SETTING_KEYS:
value = getattr(body, key, None)
@@ -128,6 +129,14 @@ async def update_settings(
new_base_url = await get_setting(session, "external_url")
new_secret = await get_setting(session, "telegram_webhook_secret")
new_timezone = await get_setting(session, "timezone")
# Cron triggers freeze their timezone at construction time, so a tz change
# has no effect until the jobs are rebuilt — do that here, before we
# return success, so the UI reflects the actual schedule immediately.
if new_timezone != old_timezone:
from ..services.scheduler import reschedule_cron_jobs_for_timezone_change
await reschedule_cron_jobs_for_timezone_change()
# Update webhook secret in the webhook handler module
if new_secret != old_secret:
@@ -242,6 +242,8 @@ async def get_template_variables(
"current_date": "Current date (formatted)",
"current_time": "Current time (formatted)",
"current_datetime": "Current date and time (formatted)",
"weekday": "Day of the week (Monday..Sunday)",
"timezone": "IANA timezone used for current_date/time",
},
},
}