<@ULVA73B9P> for a deployment schedule, is it poss...
# marvin-ai
m
@Marvin for a deployment schedule, is it possible to specify the start time in a given timezone and the end time in another timezome?
m
thought for 148.0 seconds
Short answer: No. In Prefect 3.x, each schedule has a single
timezone
that applies to all of its timing logic. You can’t define a schedule window with a start in one timezone and an end in another. There are no `active_from`/`active_until` fields with per-window timezones either; any start/end you see are used as query filters when generating dates/runs, and they’re normalized into the schedule’s single timezone. Workarounds: - Create multiple schedules on the same deployment, each with its own timezone - Or create multiple deployments, one per timezone Example using one deployment with two schedules in different timezones:
Copy code
from prefect import flow
from prefect.schedules import Cron

@flow
def my_flow():
    print("Hello from a multi-timezone schedule")

if __name__ == "__main__":
    my_flow.serve(
        name="multi-tz-example",
        schedules=[
            Cron(cron="0 8 * * *", timezone="America/New_York", slug="ny-8am"),
            Cron(cron="0 8 * * *", timezone="Europe/London", slug="ldn-8am"),
        ],
    )
If you can share the behavior you’re aiming for (e.g., “run between 09:00 in TZ A and 17:00 in TZ B”), I can suggest the cleanest pattern to model it with multiple schedules or parameters.
m
@Marvin from 8.50 am in Paris to 4 pm in New York
m
thought for 337.4 seconds
Got it — to confirm, do you want: - A window where the flow runs periodically only between 08:50 in Paris and 16:00 in New York each day, or - Just two runs per day at those exact times? If you want a recurring window, here’s a simple, robust pattern: schedule the flow frequently and exit immediately when outside the window. This handles DST and the fact that the start/end are in different timezones.
Copy code
from datetime import datetime, time, timedelta, timezone
from zoneinfo import ZoneInfo

from prefect import flow
from prefect.logging import get_run_logger
from prefect.schedules import Interval

PARIS_TZ = ZoneInfo("Europe/Paris")
NY_TZ = ZoneInfo("America/New_York")
START_TOD = time(8, 50)   # 08:50 Paris
END_TOD = time(16, 0)     # 16:00 New York

def current_window_bounds(now_utc: datetime | None = None):
    if now_utc is None:
        now_utc = datetime.now(timezone.utc)

    # Find the last Paris 08:50 at or before 'now'
    now_paris = now_utc.astimezone(PARIS_TZ)
    start_paris_date = now_paris.date()
    if now_paris.time() < START_TOD:
        start_paris_date = start_paris_date.fromordinal(start_paris_date.toordinal() - 1)

    start_local = datetime.combine(start_paris_date, START_TOD, tzinfo=PARIS_TZ)
    start_utc = start_local.astimezone(timezone.utc)

    # Find the first New York 16:00 strictly after 'start_utc'
    start_in_ny = start_utc.astimezone(NY_TZ)
    end_ny_date = start_in_ny.date()
    if start_in_ny.time() >= END_TOD:
        end_ny_date = end_ny_date.fromordinal(end_ny_date.toordinal() + 1)

    end_local = datetime.combine(end_ny_date, END_TOD, tzinfo=NY_TZ)
    end_utc = end_local.astimezone(timezone.utc)

    return start_utc, end_utc

def in_window(now_utc: datetime | None = None) -> bool:
    now_utc = now_utc or datetime.now(timezone.utc)
    start_utc, end_utc = current_window_bounds(now_utc)
    return start_utc <= now_utc < end_utc

@flow
def my_flow():
    logger = get_run_logger()
    if not in_window():
        <http://logger.info|logger.info>("Outside window (08:50 Paris → 16:00 New York); exiting.")
        return
    <http://logger.info|logger.info>("Inside window; do work here.")
    # your tasks go here

if __name__ == "__main__":
    # Run every 10 minutes, but only do work inside the window
    my_flow.serve(
        name="windowed",
        schedules=[Interval(interval=timedelta(minutes=10), timezone="UTC")],
    )
If instead you only want runs at those two times (no window), you can add two schedules to the same deployment:
Copy code
from prefect.schedules import Cron

my_flow.serve(
    name="two-daily-triggers",
    schedules=[
        Cron("50 8 * * *", timezone="Europe/Paris", slug="paris-08-50"),
        Cron("0 16 * * *", timezone="America/New_York", slug="ny-16-00"),
    ],
)
Tell me which pattern matches your intent, and I can tailor it further (e.g., cadence inside the window, handling weekends, etc.).
m
@Marvin I want a run every minute between two points
m
thought for 163.0 seconds
Great — you want one flow run every minute, but only during the window from 08:50 in Paris to 16:00 in New York. Because a single Prefect schedule can’t span two timezones, the simplest reliable pattern is: - Schedule the flow every minute - Exit immediately when outside the window (computed with tz-aware datetimes) Here’s a drop-in example:
Copy code
from datetime import datetime, time, timedelta, timezone
from zoneinfo import ZoneInfo

from prefect import flow
from prefect.logging import get_run_logger

PARIS_TZ = ZoneInfo("Europe/Paris")
NY_TZ = ZoneInfo("America/New_York")
START_TOD = time(8, 50)   # 08:50 Paris
END_TOD = time(16, 0)     # 16:00 New York

def current_window_bounds(now_utc: datetime | None = None):
    now_utc = now_utc or datetime.now(timezone.utc)

    # Start = most recent Paris 08:50 at or before now
    now_paris = now_utc.astimezone(PARIS_TZ)
    start_date_paris = now_paris.date()
    start_today_paris = datetime.combine(start_date_paris, START_TOD, tzinfo=PARIS_TZ)
    if now_paris < start_today_paris:
        start_date_paris = start_date_paris - timedelta(days=1)
    start_local = datetime.combine(start_date_paris, START_TOD, tzinfo=PARIS_TZ)
    start_utc = start_local.astimezone(timezone.utc)

    # End = first New York 16:00 strictly after start
    start_in_ny = start_utc.astimezone(NY_TZ)
    end_date_ny = start_in_ny.date()
    end_today_ny = datetime.combine(end_date_ny, END_TOD, tzinfo=NY_TZ)
    if start_in_ny >= end_today_ny:
        end_date_ny = end_date_ny + timedelta(days=1)
    end_local = datetime.combine(end_date_ny, END_TOD, tzinfo=NY_TZ)
    end_utc = end_local.astimezone(timezone.utc)

    return start_utc, end_utc

def in_window(now_utc: datetime | None = None) -> bool:
    now_utc = now_utc or datetime.now(timezone.utc)
    start_utc, end_utc = current_window_bounds(now_utc)
    return start_utc <= now_utc < end_utc

@flow
def my_flow():
    logger = get_run_logger()
    if not in_window():
        <http://logger.info|logger.info>("Outside window (08:50 Paris → 16:00 New York); exiting.")
        return
    <http://logger.info|logger.info>("Inside window; do work here.")
    # Your work here

if __name__ == "__main__":
    # One run per minute; the flow no-ops outside the window
    from datetime import timedelta
    my_flow.serve(
        name="minute-window-paris-to-ny",
        interval=timedelta(minutes=1),
        # Optional: prevent overlaps if runs may exceed 1 minute
        # global_limit=1,
    )
Notes: - This correctly handles DST for both Europe/Paris and America/New_York. - It will still create a Prefect run every minute all day; runs outside the window just no-op quickly. - If you need zero runs outside the window (not even no-ops), you’ll need an external scheduler or small helper flow to pause/resume the deployment at those local times via API. I can share that pattern if you prefer.