suganthan sivananthan
suganthan sivananthan

Reputation: 143

Schedule in different time zone

I'm developing a Django project.

I want to sync classes automatically.

My code:

from datetime import timedelta
from celery.schedules import crontab

CELERYBEAT_SCHEDULE = {
'sync-classes': {
    'task': 'scheduler.tasks.sync_classes',
    'schedule': crontab(hour='0', minute=0),
}

CELERY_ENABLE_UTC = False
CELERY_TIMEZONE = 'Europe/London'

It works in the London time zone.

Each class has a different time zone. I want the schedule to run automatically based on the relevant time zone of classes.

In sync_classes() method I get all classes and sync it in London Time zone.

Question: How can I sync every class based on its time zone?

Upvotes: 2

Views: 3120

Answers (1)

Savir
Savir

Reputation: 18438

This code is untested when it comes to the Django/Celery part, but here's the idea:

  1. Change your Celery-Beat's schedule to run every 15 minutes. That should pretty much cover all the TZ offsets in existence because most are either multiples of hour, or multiples of half an hour, with only a few cases being multiples of a quarter of hour (so this 15 minutes resolution should cover all the timezone offsets of the world):

    CELERYBEAT_SCHEDULE = {
        'sync-classes': {
        'task': 'scheduler.tasks.sync_classes',
        'schedule': crontab(minute='*/15'),
    }
    
    CELERY_ENABLE_UTC = False   
    CELERY_TIMEZONE = 'Europe/London'
    

    At this point, Celery's timezone doesn't matter because we're gonna use the only decent timezone as a reference: UTC.

  2. Create an auxiliary function that will give you the names of the timezones in which now ("now" meaning "when your task is run") is "almost" midnight. Your task will run every 15 minutes... Maybe a few milliseconds after the 15 minutes mark... so let's give it 10 minutes of buffer (that should be way, way more than enough). As long as the buffer is less than 15 minutes, you should be fine (fine meaning that you won't have one task and the next thinking that "now" is midnight and therefore running the synchronization twice)

    This should help:

    import pytz
    import datetime
    
    utc_now = pytz.utc.localize(datetime.datetime.utcnow())
    collected_tz_names = []
    for tz in pytz.all_timezones_set:
        test_dt = utc_now.astimezone(pytz.timezone(tz))
        print("tz: %s, test_dt.time() %s" % (tz, test_dt.time()))
        is_midnight = (
            datetime.time(hour=0, minute=0, second=0) <=
            test_dt.time() <=
            datetime.time(hour=0, minute=10, second=0)
        )
        if is_midnight:
            collected_tz_names.append(tz)
    print("collected %s" % collected_tz_names)
    

    If you want to test it, change the utc_now = pytz.utc.localize(datetime.datetime.utcnow()) "probe" (or reference) to a few manual values, such as utc_now = pytz.utc.localize(datetime.datetime(year=2018, month=1, day=4, hour=4, minute=1, second=0))

  3. Once you've collected the timezones in which now is midnight in the collected_tz_names list, run your sync method. Let's say the objects you need to synchronize are the User(s), right? And that your User model has an attribute tz_name that indicates which is the timezone of each user. In that case, this should do:

    for user in User.objects.filter(tz_name__in=collected_tz_names):
        user.synchronize()
    

Keep in mind that in Daylight savings times switches, you might end up synchronizing twice.

Upvotes: 2

Related Questions