Danny Vilela

    Danny Vilela

    9 months ago
    Hi all! A co-worker is trying to schedule a Flow to run on the 2nd of every month, at 8:00 AM PT. I pointed him to the
    IntervalSchedule
    with
    pendulum
    (since that’s what I’ve used for daily/weekly tasks) but he noticed that the results don’t quite line up with what he was expecting:
    import pendulum
    from prefect.schedules.schedules import IntervalSchedule
    from prefect.schedules.clocks import CronClock
    
    # Set our start date.
    next_start_date: pendulum.DateTime = (
        pendulum.now(tz="America/Los_Angeles")
        .start_of(unit="month")
        .set(day=2, hour=8, minute=0, second=0)
    )
    
    # Set our monthly interval.
    monthly: pendulum.Duration = pendulum.duration(months=1)
    
    # Inspect the next few clock emissions.
    schedule: IntervalSchedule = IntervalSchedule(start_date=next_start_date, interval=monthly)
    print(schedule.next(n=3))
    # [
    #   DateTime(2022, 1, 1, 8, 0, 0, tzinfo=Timezone('America/Los_Angeles')), 
    #   DateTime(2022, 1, 31, 8, 0, 0, tzinfo=Timezone('America/Los_Angeles')), 
    #   DateTime(2022, 3, 2, 8, 0, 0, tzinfo=Timezone('America/Los_Angeles'))
    # ]
    Why does the
    IntervalSchedule
    not fire on
    2022-01-02
    ,
    2022-02-02
    ,
    2022-03-02
    , etc? It appears to just be incrementing by 30 days, but that’s not quite what I’d expect. Is this a
    pendulum
    thing? (Edit: it’s maybe worth noting that in the example above, just doing
    next_start_date + monthly
    does give you the correct
    DateTime(2022, 1, 2, 8, 0, 0, tzinfo=Timezone('America/Los_Angeles'))
    . So I think it may actually be a Prefect thing?)
    On further inspection, it looks like the problem is that: 1. We pass a
    pendulum.Duration
    to the
    IntervalSchedule
    function, which “casts” it as a
    datetime.timedelta
    before passing to the
    IntervalClock
    initializer. 2. In
    Schedule.next
    we call
    _get_clock_events
    which calls
    clocks.events
    . 3.
    IntervalClock
    does some book-keeping but ultimately extracts
    days = interval.days
    from our input interval. Here,
    monthly.days
    defaults to 30 since it has no notion of which month we’re referring to. Hence, the
    IntervalClock
    will always assume that each monthly
    pendulum.Duration
    is equivalent to 30 days. I’m not exactly sure how to fix that, but maybe it starts with casting
    interval
    to a
    pendulum.Duration
    ?
    Anna Geller

    Anna Geller

    9 months ago
    Using the good old cron, this would be:
    import pendulum
    from prefect.schedules import CronSchedule
    
    schedule = CronSchedule("0 8 2 * *", start_date=pendulum.today(tz="America/Los_Angeles"))
    
    for sched in schedule.next(20):
        print(sched)
    but we also support RRule schedules, maybe it can be even easier for you
    Danny Vilela

    Danny Vilela

    9 months ago
    Yeah, I ended up pointing him to CronSchedule which works as expected. This just seems like a bug in the
    IntervalScheduler
    , so I was wondering if it was known. Would it be helpful to file an issue? I couldn’t find any existing issues referring to this situation 🤔 Regardless, thank you @Anna Geller!
    Zach Schumacher

    Zach Schumacher

    9 months ago
    this website is a life saver https://crontab.guru