使用 django 查询返回活动时区中的日期时间

Posted

技术标签:

【中文标题】使用 django 查询返回活动时区中的日期时间【英文标题】:return datetimes in the active timezone with a django query 【发布时间】:2016-01-19 18:34:23 【问题描述】:

我正在尝试从表中检索最后 n 小时的行并在给定的时区打印它们的日期时间,给出打印日期时使用的时区,我正在尝试使用 activate 使 django 返回正确的日期时间时区,但它以 UTC 格式返回日期。

这是我当前的代码:

min_time = datetime.datetime.now(link.monitor.timezone) - datetime.timedelta(hours=period)

timezone.activate(link.monitor.timezone)
rows = TraceHttp.objects.values_list('time', 'elapsed').filter(time__gt=min_time,link_id=link_id,elapsed__gt=0)

array = []
for row in rows:
    array.append((row[0].astimezone(link.monitor.timezone),row[1]))

我想避免使用 astimezone 函数并让 Django 为我执行此操作,是否有时我会遗漏 activate 函数?

编辑

这是我的模型,您可以看到要显示的时区保存在“监视器”模型上:

class Link(models.Model):
   ...
   monitor = models.ForeignKey(Monitor)
   ...

class Monitor(models.Model):
    ...
    timezone = TimeZoneField(default='Europe/London')

class TraceHttp(models.Model):
    link = models.ForeignKey(Link)
    time = models.DateTimeField()
    elapsed = models.FloatField()

【问题讨论】:

【参考方案1】:

经过一些研究,我注意到 Django 总是将日期时间返回为 UTC,您可以通过使用 datetime.astimezone(timezone) 方法或激活某个时区来在正确的时区解释它们。

django active 函数只是改变了日期时间在模板上的呈现方式,但实际上并没有本地化时区。

【讨论】:

【参考方案2】:

如果您发现自己在循环中执行 timezone.localtime(dt_value)dt_value.astimezone(tzifo) 几百万次以计算您所在时区的当前日期,那么截至 1.10

from django.db.models.functions import Trunc, TruncDate

qs = MyModel.objects.filter(...).values(
    'dtime',
    ...,
    dtime_at_my_tz=Trunc('dtime', 'second', tzinfo=yourtz),
    date_at_my_tz=TruncDate('dtime', tzinfo=yourtz),
    month=TruncDate(Trunc('dtime', 'month', tzinfo=yourtz)),
    quarter=TruncDate(Trunc('dtime', 'quarter', tzinfo=yourtz))
)

这将返回正确时区的日期时间或日期。您可以使用其他 Trunc* 函数作为速记。如果您只需要datetime.dates,TruncDate 特别有用

这会将日期计算卸载到数据库中,通常会大大降低代码复杂性并提高速度(在我的例子中,超过 650 万个 timezone.localtime(ts) 贡献了总 CPU 时间的 25%)

关于 TruncMonth 和时区的注意事项

不久前,我发现我无法从 TruncMonthTruncQuarter 中获得“合适的”月份:1 月 1 日将变成 12 月 31 日。

TruncMonth 使用当前活动的时区,因此(正确地)datetime of 2019-01-01T00:00:00Z 对于与 UTC 有正偏移的任何时区(西欧和更远的地方)转换为前一天东方)。 如果您只对事件datetime 的“纯月份”感兴趣(如果您使用的是 TruncMonth,您可能会感兴趣),那么这没有帮助,但是如果您在执行查询之前timezone.activate(timezone.utc)(也就是说,评估您的 QuerySet),您将获得预期的结果。请记住,从您的午夜到 UTC 午夜发生的事件将低于上个月(同样datetimes 从您的时区午夜到 UTC 午夜将被转换为“错误”月份)

【讨论】:

【参考方案3】:

您可以使用 Django.utils 中的 now() 函数,但是,您需要在设置中设置两个变量。 USE_TZ 和 TIME_ZONE,第一个为 true,另一个为用于生成日期时间的默认时区。 您可以在 django 文档中查看更多信息here

【讨论】:

你能发布一些代码来展示 now() 函数如何与我的代码一起工作吗?我已经设置了 USE_TZ=True 和 TIME_ZONE,请注意 link.monitor.timezone 存储在数据库中,可能是任何时区 好的,现在我明白了,你能把字段放在模型中吗?因为Django默认不存储时区...

以上是关于使用 django 查询返回活动时区中的日期时间的主要内容,如果未能解决你的问题,请参考以下文章

Django:时区支持处于活动状态时的天真日期时间(sqlite)

Django:RunTimeWarning:DateTimeField 在时区支持处于活动状态时收到了一个天真的日期时间

在时区支持处于活动状态时收到一个天真的日期时间

Python 字符串到 Django 时区(知道日期时间)

Django 日期时间查询

处理日期/时间和时区的问题