4

I want to schedule a python function to run everyday at a certain time for a list of customers with different timezones.

This is basically what I want to do:

import schedule
import time

def job(text):
    print("Hello " + text)

def add_job(user_tz, time, text):
    schedule.every().day.at(time).do(job(text)) 
    # the above adds all jobs at local time, I want to use different timezones with these

def run_job():
    while(1):
        schedule.run_pending()
        time.sleep(1)

if __name__=='__main__':
    add_job('America/New_York', "12:00", 'New York')
    add_job('Europe/London', "12:00", 'London')
    run_job()

I am using this for posting/receiving some stuff using flask and an external API.

Celery or heroku scheduler or something heavy is not what I am looking for, something lightweight and pythonic for debian(or nix) env would be ideal. I have looked into scheduler, tzcron and APScheduler, but could not figure out how we would be able to use them with timezones.

Also I tried using crontab but could not figure out how to add jobs at run time, since I want to be able to add/remove jobs using the above mentioned functions at runtime as well.

I have some experience with python, but its my first problem with timezones and I dont know much about this, so please feel free to enlighten me if there is something I missed or if there is any other way of doing it.

Thanks!

jpotyka
  • 150
  • 1
  • 7
skybunk
  • 833
  • 2
  • 12
  • 17
  • Look at Python's `datetime` package. Convert the foreign time zone into the local one. Another possibility is to make a solid attempt to use `crontab`, and post your troubles in a separate question. If you get a solution in either place, you win. – Prune Nov 17 '17 at 17:50
  • @Prune I will try. Seems that converting foreign timezones to local ones could work. I am so dumb, thank you for suggesting that – skybunk Nov 17 '17 at 17:55

2 Answers2

8

The described problem sounds like the scheduler library for python provides a solution out of the box that requires no further customization by the user. The scheduler library is designed so that jobs can be scheduled in different timezones, it is irrelevant with which timezone the scheduler is created and in which independent timezones the jobs are scheduled.

Disclosure: I'm one of the authors of the scheduler library

For demonstration, I have adapted an example from the documentation to the question:

import datetime as dt
from scheduler import Scheduler
import scheduler.trigger as trigger

# Create a payload callback function
def useful():
    print("Very useful function.")

# Instead of setting the timezones yourself you can use the `pytz` library
tz_new_york = dt.timezone(dt.timedelta(hours=-5))
tz_wuppertal = dt.timezone(dt.timedelta(hours=2))
tz_sydney = dt.timezone(dt.timedelta(hours=10))

# can be any valid timezone
schedule = Scheduler(tzinfo=dt.timezone.utc)

# schedule jobs
schedule.daily(dt.time(hour=12, tzinfo=tz_new_york), useful)
schedule.daily(dt.time(hour=12, tzinfo=tz_wuppertal), useful)
schedule.daily(dt.time(hour=12, tzinfo=tz_sydney), useful)

# Show a table overview of your jobs
print(schedule)
max_exec=inf, tzinfo=UTC, priority_function=linear_priority_function, #jobs=3

type     function         due at              tzinfo          due in      attempts weight
-------- ---------------- ------------------- ------------ --------- ------------- ------
DAILY    useful()         2021-07-20 12:00:00 UTC-05:00      1:23:39         0/inf      1
DAILY    useful()         2021-07-21 12:00:00 UTC+10:00     10:23:39         0/inf      1
DAILY    useful()         2021-07-21 12:00:00 UTC+02:00     18:23:39         0/inf      1

Execute jobs using a simple loop:

import time
while True:
    schedule.exec_jobs()
    time.sleep(1)  # wait a second

Edit: An asyncio example is available in the doc too.

jpotyka
  • 150
  • 1
  • 7
5

The arrow library is great for this, and much simpler than the standard date/time (imo). arrow docs.

import arrow
from datetime import datetime

now = datetime.now()
atime = arrow.get(now)
print(now)
print (atime)

eastern = atime.to('US/Eastern')
print (eastern)
print (eastern.datetime)

2017-11-17 09:53:58.700546
2017-11-17T09:53:58.700546+00:00
2017-11-17T04:53:58.700546-05:00
2017-11-17 04:53:58.700546-05:00

I would change your "add_job" method modify all of my incoming dates to a standard time zone (utc for example).

SteveJ
  • 3,034
  • 2
  • 27
  • 47