Невозможно сравнить наивный и осведомленный datetime.now () <= challenge.datetime_end

154

Я пытаюсь сравнить текущую дату и время с датами и временем, указанными в моделях, используя операторы сравнения:

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 (). Это наивно или в курсе? И как мне получить datetime.now () для распознавания локали datetime?

sccrthlt
источник
1
есть очень хорошая библиотека для игры с датой: маятник (я не аффилирован)
Томас Деко

Ответы:

137

По умолчанию datetimeобъект находится naiveв Python, поэтому вам нужно сделать их оба наивными или осведомленными datetimeобъектами. Это можно сделать с помощью:

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

Примечание: это подняло бы, ValueErrorесли tzinfoуже установлено. Если вы не уверены в этом, просто используйте

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)
Вирен Раджпут
источник
Он говорит: ValueError: Не наивное datetime (tzinfo уже установлена), когда он пытается вычислить: datetimeStart = utc.localize (challenge.datetime_start)
sccrthlt
да, это вызывает ValueError.
Дмитрий Михайлов
4
Замена tzinfoне делает никакого преобразования, делая сравнение некорректным.
OrangeDog
+1 за это. И, используя, utc = pytz.utcчтобы предотвратить ошибку Pylint No value for argument 'dt' in unbound method call (no-value-for-parameter). Pytz Link
Сэм
90

datetime.datetime.now не знает часовой пояс.

Джанго приходит с помощником для этого, который требует pytz

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

Вы должны быть в состоянии сравнить nowсchallenge.datetime_start

Альфредо Агирре
источник
3
If USE_TZ=Trueзатем timezone.now()возвращает объект datetime с учетом часового пояса, даже если pytzон не установлен (хотя может быть рекомендовано установить его по другим причинам).
Jfs
49

Одна строка кода решения

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атрибут.

ePi272314
источник
18

Отключить часовой пояс. использованиеchallenge.datetime_start.replace(tzinfo=None);

Вы также можете использовать replace(tzinfo=None)для другой даты и времени .

if challenge.datetime_start.replace(tzinfo=None) <= datetime.now().replace(tzinfo=None) <= challenge.datetime_end.replace(tzinfo=None):
Амин Фатхи
источник
2

Таким образом, я бы решил эту проблему, чтобы убедиться, что эти два времени находятся в правильном часовом поясе.

Я вижу, что вы используете, datetime.now()который будет возвращать текущее время системы без установленной tzinfo.

tzinfo - это информация, прикрепленная к дате и времени, чтобы сообщить ей, в каком часовом поясе она находится. Если вы используете наивное время и дату, вы должны быть последовательными во всей своей системе. Я очень рекомендую использовать толькоdatetime.utcnow()

поскольку вы где-то создаете дату и время, с которыми связана tzinfo, вам нужно убедиться, что они локализованы (с ассоциированной tzinfo) в правильном часовом поясе.

Взгляните на Delorean , это значительно облегчает работу с подобными вещами.

myusuf3
источник
8
Вы также видите эту проблему с utcnow.
Энди Хейден,
0

Это работает от меня. Здесь я подхожу к таблице, созданной datetime, и добавляю 10 минут на datetime. позже, в зависимости от текущего времени, выполняются операции истечения срока действия.

from datetime import datetime, time, timedelta
import pytz

Добавлено 10 минут в базу данных datetime

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

Это сработало для меня.

Чандан Шарма
источник
0

Просто:

dt = datetimeObject.strftime(format) # format = your datetime format ex) '%Y %d %m'
dt = datetime.datetime.strptime(dt,format)

Так что сделайте это:

start_time = challenge.datetime_start.strftime('%Y %d %m %H %M %S')
start_time = datetime.datetime.strptime(start_time,'%Y %d %m %H %M %S')

end_time = challenge.datetime_end.strftime('%Y %d %m %H %M %S')
end_time = datetime.datetime.strptime(end_time,'%Y %d %m %H %M %S')

а затем использовать start_timeиend_time

Harispy
источник