Search code examples
pythonpython-3.xdatetimetimezonescheduler

python scheduler schedule job in specific time zone from any time zone


I'm trying to run a job ever day at 8:00 AM at timezone('Asia/Calcutta'), so I started using python schedule. Here's a sample snippet:

import schedule
import time

def job(t):
    print "I'm working...", t
    return

schedule.every().day.at("08:00").do(job,'It is 08:00')

while True:
    schedule.run_pending()
    time.sleep(30) # wait one minute

This snippets works very well if a execute in Indian servers, but if I run in COLAB or AWS (which are in different timezones), the job start at 8:00 am based on that particular time zone. I would like to make code to run every day 8:00 am on this timezone('Asia/Calcutta'), irrespective of the server time zone. I have gone through different articles from Stack Overflow related to timezones, getting offset and changing 8:00 am to something like 8:00 + some offset 4:30 hrs, but that did not work.

  1. looking for the best way to run python code on any server/any timezone, but scheduler will trigger in timezone('Asia/Calcutta') this timezone.

  2. is there a way to change datetime timezone on thread level or full python process level (only for that script/thread, not system level), so I want to change timezone once in python, and after that wherever I call datetime.now(), it should give time as per new timezone,

       eastern = timezone('Asia/Calcutta')
       # naive datetime
       naive_dt = datetime.now()
       # localized datetime
       loc_dt = datetime.now(eastern)
       print(naive_dt.strftime(fmt))
    
       print(loc_dt.strftime(fmt))
    

Based on this (answer) I am not interested to change system files, I'm looking for a pythonic way.


Solution

  • If you are looking for a scheduler with timezone support in mind, you might be interested in the scheduler library. An example for a similar situation as you described can be found in the documentation. I modified it below to suit your question and additionally added a second Job with an independant timezone for better demonstration.

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

    Question 1

    Setup your server and job timezones and create a payload function.

    import datetime as dt
    import time
    from scheduler import Scheduler
    import pytz 
    
    TZ_SERVER = dt.timezone.utc  # can be any valid timezone
    
    TZ_IST = pytz.timezone('Asia/Calcutta')  # IST - India Standard Time
    TZ_CEST = pytz.timezone('Europe/Berlin')  # CEST - Central European Summer Time
    
    def task(tzinfo):
        current_time = dt.datetime.now(tzinfo)
        print(f"I'm working... {current_time!s}")
    

    Create a Scheduler instance and schedule recurring daily jobs with specified times and timezones:

    schedule = Scheduler(tzinfo=TZ_SERVER)
    
    trigger_ist = dt.time(hour=8, tzinfo=TZ_IST)
    schedule.daily(trigger_ist, task, args=(TZ_IST,))
    
    trigger_cest = dt.time(hour=8, tzinfo=TZ_CEST)
    job_cest = schedule.daily(trigger_cest, task, args=(TZ_CEST,))
    

    Show a simple overview of the schedule.

    print(schedule)
    
    max_exec=inf, tzinfo=UTC, priority_function=linear_priority_function, #jobs=2
    
    type     function         due at              tzinfo          due in      attempts weight
    -------- ---------------- ------------------- ------------ --------- ------------- ------
    DAILY    task(..)         2021-07-20 08:00:00 IST            2:36:42         0/inf      1
    DAILY    task(..)         2021-07-20 08:00:00 CEST           6:06:42         0/inf      1
    

    Create your execution loop analogous to the schedule library:

    while True:
        schedule.exec_jobs()
        time.sleep(60)  # wait one minute
    

    Once the payload function is called you should see something similar to below.

    I'm working... 2021-07-20 08:00:33.173469+05:30
    

    Question 2

    I'm unsure if I understand your second question, but it is alway possible to cast one datetime.datetime object with a timezone into another datetime.datetime object with another timezone. E.g. you can do:

    now_cest = dt.datetime.now(TZ_CEST)
    now_ist = now_cest.astimezone(TZ_IST)