无法比较天真和明智的datetime.now() <= challenge.datetime_end

346
我正在尝试使用比较运算符将当前日期和时间与模型中指定的日期和时间进行比较。
if challenge.datetime_start <= datetime.now() <= challenge.datetime_end:

脚本出现错误:
TypeError: can't compare offset-naive and offset-aware datetimes

模型看起来是这样的:
class Fundraising_Challenge(models.Model):
    name = models.CharField(max_length=100)
    datetime_start = models.DateTimeField()
    datetime_end = models.DateTimeField()

我也使用django的本地日期和时间。
我找不到的是django在DateTimeField()中使用的格式。它是naive还是aware?如何让datetime.now()识别本地日期时间?

2
https://dev59.com/oWgv5IYBdhLWcg3wXPou - catherine
1
有一个非常好用的日期处理库:Pendulum(我并非参与其中)。 - Thomas Decaux
14个回答

310

默认情况下,Python中的datetime对象是naive的,因此您需要使它们都成为naiveawaredatetime对象。可以使用以下方法完成:

默认情况下,Python中的datetime对象是naive的,因此您需要使它们都成为naiveawaredatetime对象。可以使用以下方法完成:

import datetime
import pytz

utc=pytz.UTC

challenge.datetime_start = utc.localize(challenge.datetime_start) 
challenge.datetime_end = utc.localize(challenge.datetime_end) 
# now both the datetime objects are aware, and you can compare them

注意:如果已经设置了tzinfo,这将引发ValueError异常。如果您不确定,请使用

start_time = challenge.datetime_start.replace(tzinfo=utc)
end_time = challenge.datetime_end.replace(tzinfo=utc)

顺便提一下,您可以按照以下方式将带有时区信息的UNIX时间戳格式化为datetime.datetime对象

d = datetime.datetime.utcfromtimestamp(int(unix_timestamp))
d_with_tz = datetime.datetime(
    year=d.year,
    month=d.month,
    day=d.day,
    hour=d.hour,
    minute=d.minute,
    second=d.second,
    tzinfo=pytz.UTC)

2
数值错误:在计算时出现“不是原始日期时间(tzinfo已设置)”的错误,datetimeStart = utc.localize(challenge.datetime_start)。 - sccrthlt
11
替换 tzinfo 不会进行任何转换,这使得比较是不正确的。 - OrangeDog
2
你可以从datetimetimezone模块获取utc时区。timezone.utc - mjuopperi
1
如果您已经知道两个值都是UTC,但不知道它们是否都是带偏移的日期时间,请仅使用“replace()”方法。我正在一个系统中工作,在该系统中,.NET应用程序将数据传递给Python API,有时.NET日期时间具有像“+00:00”这样的偏移量,有时没有,但它们仍然在UTC中。在带有偏移量的日期上使用datetime.parse()会在Python中创建一个带偏移的日期时间对象,如果我尝试将其与一个没有“+00:00”的字符串上的datetime.parse()进行比较,则会引发错误。 - jwatts1980
1
只有在您已经知道两个值都是UTC时间,并且不知道它们是否都是带有偏移的日期时间时,才使用"replace()"方法。我正在一个系统中工作,其中一个.NET应用程序将数据传递给Python API,有时.NET日期时间具有像"+00:00"这样的偏移量,有时没有,但它们仍然是UTC时间。在Python中对带有偏移量的日期使用datetime.parse()会创建一个带有偏移的日期时间对象,如果我尝试将其与一个没有"+00:00"的字符串上的datetime.parse()进行比较,会引发错误。 - undefined
显示剩余3条评论

170

datetime.datetime.now 不具备时区意识。

Django 自带了一个辅助程序,它需要 pytz

from django.utils import timezone
now = timezone.now()

你应该能够将nowchallenge.datetime_start进行比较。


8
如果USE_TZ=True,即使没有安装pytz(尽管可能建议安装其他原因),timezone.now()也会返回一个有时区信息的日期时间对象。 - jfs
这应该是被接受的答案。 - AD Progress

133

一行代码解决方案

if timezone_aware_var <= datetime.datetime.now(timezone_aware_var.tzinfo):
    pass #some code

解释版

# Timezone info of your timezone aware variable
timezone = your_timezone_aware_variable.tzinfo

# Current datetime for the timezone of your variable
now_in_timezone = datetime.datetime.now(timezone)

# Now you can do a fair comparison, both datetime variables have the same time zone
if your_timezone_aware_variable <= now_in_timezone:
    pass #some code

总结

您必须向您的 now() 日期时间添加时区信息。
但是,您必须添加与参考变量相同的时区;这就是为什么我首先读取tzinfo属性的原因。


3
这似乎是最合乎逻辑的方法 - 谢谢! - bcelary
朴素的解决方案。逻辑解决方案。这个解决方案应该是最佳答案。 - AnandShiva

55

禁用时区。

使用challenge.datetime_start.replace(tzinfo=None);

你也可以对其他datetime使用replace(tzinfo=None)

if challenge.datetime_start.replace(tzinfo=None) <= datetime.now().replace(tzinfo=None) <= challenge.datetime_end.replace(tzinfo=None):

14

没有第三方库,只使用原生的 datetime 模块。

from datetime import datetime, timedelta, timezone

time1 = datetime.strptime('2021-07-15T00:22:02+0000', '%Y-%m-%dT%H:%M:%S%z')
time2 = datetime(2021, 7, 15, tzinfo=timezone(offset=timedelta()))
if time1 < time2:
    print(True)

你会如何使用datetime.now()使其正常工作? - jtlz2
+许多,因为不需要第三方库!! - jtlz2
3
@jtlz2,你可以使用datetime.now().replace(tzinfo=timezone(offset=timedelta())) - George V. Reilly

4

对我来说它正在工作。 在这里,我获取表格创建的日期时间,并在该日期时间上添加10分钟。 随后根据当前时间,执行过期操作。

from datetime import datetime, time, timedelta
import pytz

增加了10分钟到数据库日期时间

table_datetime = '2019-06-13 07:49:02.832969'(示例)

# Added 10 minutes on database datetime
# table_datetime = '2019-06-13 07:49:02.832969' (example)

table_expire_datetime = table_datetime + timedelta(minutes=10 )

# Current datetime
current_datetime = datetime.now()


# replace the timezone in both time
expired_on = table_expire_datetime.replace(tzinfo=utc)
checked_on = current_datetime.replace(tzinfo=utc)


if expired_on < checked_on:
    print("Time Crossed)
else:
    print("Time not crossed ")

这对我很有效。


3

如果要将你的日期时间对象从naive转换为时区感知,只需添加以下函数:

datetimeObject.astimezone()

2
你正在尝试设置已经有时区的 date_time 的时区。 请使用 replaceastimezone 函数。
local_tz = pytz.timezone('Asia/Kolkata')

current_time = datetime.now().replace(tzinfo=pytz.utc).astimezone(local_tz)

1
所以我解决这个问题的方法是确保两个日期时间在正确的时区。 我可以看到您正在使用datetime.now(),它将返回未设置tzinfo的系统当前时间。 tzinfo是附加到datetime的信息,用于让它知道它处于哪个时区。 如果您使用的是naive datetime,则需要在整个系统中保持一致。 我强烈建议仅使用datetime.utcnow(),因为您在某个地方创建了具有tzinfo关联的datetime,所以您需要确保这些datetime已本地化(具有tzinfo关联)到正确的时区。看看Delorean,它使处理此类事情变得更加容易。

16
您在utcnow中也会遇到此问题。 - Andy Hayden
1
赞同。utcnow 不会添加时区信息。 - Kyle

1
如果您正在使用 SQLAlchemy 并存储 DateTime,那么您可以将其与时区信息一起存储。这将允许您将该 DateTime 与带有时区信息的 DateTimes 进行比较。在 SQLAlchemy 核心中的示例列定义如下:
Column("created_at", DateTime(timezone=True), nullable=False)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接