【问题标题】:Schedule in different time zone不同时区的时间表
【发布时间】:2018-01-04 05:50:36
【问题描述】:

我正在开发一个Django 项目。

我想自动同步课程。

我的代码:

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'

它适用于伦敦时区。

每个班级都有不同的时区。我希望时间表根据课程的相关时区自动运行。

sync_classes() 方法中,我获取所有课程并将其同步到伦敦时区。

问题:如何根据时区同步每个班级?

【问题讨论】:

  • 你能澄清一下类的时区吗?您是否在模型上存储时区?比如...user.timezone 什么的?此外,该任务每天午夜运行(伦敦 TZ),对吗?你希望你的课程发生什么?
  • @BorrajaX 我在类模型中存储了时区。该任务每天午夜运行。现在每节课都在伦敦时区运行。但它应该在其时区运行(应该在 class.timezone 中运行)。
  • 我不认为你可以用 Celery 做到这一点......不完全是(CELERY_TIMEZONE 只意味着当你告诉它在午夜运行此任务时,是 伦敦的午夜。例如,我建议您每小时运行一次任务(而不是像现在这样每天运行),并在任务中获取当时午夜的时区,并且同步时区在这些时区的对象。
  • @BorrajaX 有没有办法在所有时区的午夜同步任务?
  • 没有。首先,您不能保证您的条目(您的班级)将在午夜时分被访问。此外,时区也很混乱。您有一些时区在半小时内有偏移,甚至在 45 小时内(例如尼泊尔)

标签: python django timezone celerybeat


【解决方案1】:

当涉及到 Django/Celery 部分时,此代码未经测试,但想法如下:

  1. 将 Celery-Beat 的时间表更改为每 15 分钟运行一次。这应该几乎涵盖all the TZ offsets in existence,因为大多数情况是小时的倍数或半小时的倍数,只有少数情况是一刻钟的倍数(所以这个 15 分钟的解决方案应该涵盖所有 世界的时区偏移):

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

    此时,Celery 的时区无关紧要,因为我们将使用唯一的体面时区作为参考:UTC。

  2. 创建一个辅助函数,该函数将为您提供现在所在时区的名称(“现在”意思是“当你的任务运行时” em>) 是“几乎”午夜。您的任务将每 15 分钟运行一次......也许在 15 分钟标记后几毫秒......所以让我们给它 10 分钟的缓冲时间(应该是这样,way 绰绰有余)。只要缓冲区少于 15 分钟,你应该没问题(fine 意味着你不会有一个任务,下一个认为 “现在” 是午夜因此运行两次同步)

    这应该会有所帮助:

    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)
    

    如果要测试,把utc_now = pytz.utc.localize(datetime.datetime.utcnow())“probe”(或引用)改成几个手动值,比如utc_now = pytz.utc.localize(datetime.datetime(year=2018, month=1, day=4, hour=4, minute=1, second=0)

  3. 收集collected_tz_names 列表中now午夜 的时区后,运行同步方法。假设您需要同步的对象是User(s),对吗?并且您的 User 模型有一个属性 tz_name 指示每个用户的时区。在这种情况下,应该这样做:

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

请记住,在夏令时切换中,您最终可能会同步两次。

【讨论】:

    猜你喜欢
    • 2014-03-16
    • 2010-11-26
    • 1970-01-01
    • 2012-02-04
    • 2013-07-16
    • 2016-04-30
    • 1970-01-01
    • 2012-12-05
    相关资源
    最近更新 更多