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

return datetimes in the active timezone with a django query

我正在尝试从 table 中检索最后 n 小时的行并在给定时区中打印它们的日期时间,给出打印日期时使用的时区,我正在尝试使用 activate 来制作 django return 具有正确时区的日期时间,但 returns 日期为 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 为我执行此操作,我是否有时会遗漏激活函数?

编辑

这是我的模型,您可以看到要显示的时区保存在 "monitor" 模型上:

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()

您可以使用django.utils中的now()函数,但您需要在设置中设置两个变量:USE_TZTIME_ZONE,第一个是True 和另一个将用于生成日期时间的默认时区。

您可以在 django 文档中查看更多信息 here

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

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

如果您发现自己在循环中执行 timezone.localtime(dt_value)dt_value.astimezone(tzifo) 数百万次以计算您所在时区的当前日期,那么从 1.10 <= django.VERSION <= 2.1 是使用 django.db.models.functions.Trunc 和相关函数,即使用像这样的查询集:

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))
)

这将 return 正确时区的日期时间或日期。您可以将其他 Trunc* 函数用作 shorthand。如果您只需要 datetime.dates

TruncDate 特别有用

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

关于 TruncMonth 和时区的注意事项

不久前我发现我无法从 TruncMonthTruncQuarter 中获得 'proper' 个月:1 月 1 日将成为 12 月 31 日。

TruncMonth 使用当前活动的时区,因此(正确地)2019-01-01T00:00:00Z 的 datetime 被转换为前一天对于任何与 UTC 有正偏移的时区(西方欧洲和更东边的任何地方)。 如果您只对事件 datetime 的 'pure month' 感兴趣(如果您使用的是 TruncMonth,则可能是)这没有帮助,但是如果您 timezone.activate(timezone.utc) 在执行查询(即评估您的 QuerySet),您将获得预期的结果。请记住,从您的午夜到 UTC 的午夜发生的事件将归入上个月(并且以相同的方式从您时区的午夜到 UTC 的午夜 datetimes 将转换为 'wrong' 月份)