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

Jes*_*mez 2 python django datetime django-queryset django-timezone

我试图从表中检索最后 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]))
Run Code Online (Sandbox Code Playgroud)

我想避免使用 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()
Run Code Online (Sandbox Code Playgroud)

Jes*_*mez 11

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

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


Rob*_*obM 5

如果您发现自己执行timezone.localtime(dt_value)dt_value.astimezone(tzifo)循环了几百万次来计算时区中的当前日期,那么从 1.10 <= django.VERSION <= 2.1 开始,可能最好的方法是使用 django.db.models.functions .Trnc 和相关功能,即使用查询集,如:

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))
)
Run Code Online (Sandbox Code Playgroud)

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

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

关于 TruncMonth 和时区的注意事项

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

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