Неможливо порівняти наївний та обізнаний 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 () розпізнавати локальний час?



2
Можливий дублікат не вдається відняти
натрівні

1
є дуже приємна гра з датою: маятник (я не пов'язаний)
Thomas Decaux

Відповіді:


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: Не наївний час (tzinfo вже встановлено), коли він намагається обчислити: datetimeStart = utc.localize (challenge.datetime_start)
sccrthlt

так, це підвищує ValueError.
Дмитро Михайлов

4
Заміна tzinfoне робить перетворення, що робить порівняння неправильним.
OrangeDog

+1 для цього. І, використовуючи utc = pytz.utcдля запобігання помилки пілінта 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
Якщо USE_TZ=Trueпотім timezone.now()повертає об'єкт дати, відомий часовій зоні, навіть якщо 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атрибут.


18

Вимкнути часовий пояс. Використовуйтеchallenge.datetime_start.replace(tzinfo=None);

Ви можете також використовувати replace(tzinfo=None)для інших DateTime .

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 , це значно спрощує справу з подібними речами.


8
Ви також бачите це питання з utcnow.
Енді Хейден

0

Це працює у мене. Тут я збираю таблицю, створену датою, і додаю 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 ")

Це працювало для мене.


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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.