While True python statement to execute code biweekly on a specific date WITHOUT cron?

Question:

How can I execute code, like a simple print("whatever"), biweekly, at a specific time. For example, every other Monday print("test") at 1 PM EST. This has to be without cron. I realize there are numerous solutions using crontab. Not an option for me. The other closest solutions on here relies on the calendar library, and executes code weekly on a specific day, but not biweekly.

Does anybody know of a simple and easy way to do this?

i’ve tried this
Python scheduling a job starting every weekday and running every hour

I was expecting a way to run tasks biweekly, but its not possible with that library.

edit:

basically this, but biweekly only:

# Schedule Library imported
import schedule
import time

# Functions setup
def sudo_placement():
    print("Do something ")

schedule.every().monday.at("09:01").do(sudo_placement)
schedule.every().monday.at("09:02").do(sudo_placement)
schedule.every().monday.at("09:03").do(sudo_placement)


while True:

    schedule.run_pending()
    time.sleep(1)
Asked By: bigbird342d

||

Answers:

We use sometimes croniter. Not quite for the need you have, but more to limit the rate of certain updates on some client code, by specifying a "schedule" using cron expressions.

In your case, you could do something like this:

from croniter import croniter
from datetime import datetime
import pytz

def every_other(it):
    while True:
        t = it.get_next(datetime)
        yield t
        it.get_next(datetime)

# for the example
DO_SLEEP = False  
max_runs = 5

# every other Monday 1pm ET, starting with the first Monday in January 2023
base = datetime(2023, 1, 1, tzinfo=pytz.timezone('EST5EDT'))
it = croniter('0 13 * * Mon', base)

for t in every_other(it):
    sleep_time = t.timestamp() - time.time()
    if sleep_time < 0:
        continue
    if max_runs <= 0:
        break
    max_runs -= 1
    print(f'next run: {t:%Y-%m-%d %H:%M:%S %Z}')
    if DO_SLEEP:
        time.sleep(sleep_time)
    # do whatever

This prints out (running on 2023-02-12):

next run: 2023-02-13 13:00:00 EST
next run: 2023-02-27 13:00:00 EST
next run: 2023-03-13 13:00:00 EDT
next run: 2023-03-27 13:00:00 EDT
next run: 2023-04-10 13:00:00 EDT

Shift the biweekly sequence

Say we wanted the biweekly series to start on 2023-02-20 instead of 2023-02-13. Simply change the base above. For example:

base = datetime(2023, 2, 20, tzinfo=pytz.timezone('EST5EDT'))

The run dates will then be:

next run: 2023-02-20 13:00:00 EST
next run: 2023-03-06 13:00:00 EST
next run: 2023-03-20 13:00:00 EDT
next run: 2023-04-03 13:00:00 EDT
next run: 2023-04-17 13:00:00 EDT

In other words, just set base to whichever first Monday you’d like in the series, before 1pm ET (or any datetime between the previous Monday after 1pm to the chosen start day, before 1pm).

Notes

  1. To actually run on the prescribed schedule (every other Monday at 1pm ET), then set DO_SLEEP = True.
  2. To run "forever", remove the max_runs logic, which is there only for this example.
  3. That program can be forcefully interrupted (e.g. reboot, or kill -9) and restarted at any time: it will resume with waiting for the next "other Monday" in the series (anchored on the first Monday in 2023).
  4. This could be extended to handle multiple schedules and actions, but would need a stack of "next run times". Relatively easy to do, but beyond the scope of this narrow question.
  5. For very long wait times (two weeks), I don’t know if a single time.sleep() is accurate enough. It might be worth implementing a wait_until(timestamp) that waits shorter and shorter periods until the prescribed time. This could then be made quite accurate (well under a second).
Answered By: Pierre D
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.