Nie można porównać naiwnego i świadomego datetime.now () <= challenge.datetime_end

154

Próbuję porównać aktualną datę i godzinę z datami i godzinami określonymi w modelach za pomocą operatorów porównania:

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

Błąd skryptu:

TypeError: can't compare offset-naive and offset-aware datetimes

Modele wyglądają tak:

class Fundraising_Challenge(models.Model):
    name = models.CharField(max_length=100)
    datetime_start = models.DateTimeField()
    datetime_end = models.DateTimeField()

Mam też django używające daty i czasu locale.

Czego nie udało mi się znaleźć, to format używany przez django w funkcji DateTimeField (). Czy to naiwne czy świadome? I w jaki sposób mogę uzyskać datetime.now () rozpoznawać locale datetime?

sccrthlt
źródło
1
jest bardzo fajna biblioteka do zabawy z datą: wahadło (nie jestem powiązany)
Thomas Decaux

Odpowiedzi:

137

Domyślnie datetimeobiekt jest naivew Pythonie, więc musisz uczynić oba z nich datetimeobiektami naiwnymi lub świadomymi . Można to zrobić za pomocą:

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

Uwaga: spowodowałoby to podniesienie wartości ValueErrorif tzinfojest już ustawiona. Jeśli nie jesteś tego pewien, po prostu użyj

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

BTW, możesz sformatować znacznik czasu UNIX w obiekcie datetime.datetime z informacjami o strefie czasowej w następujący sposób

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)
Viren Rajput
źródło
Mówi: ValueError: Not naive datetime (tzinfo jest już ustawione) podczas próby obliczenia: datetimeStart = utc.localize (challenge.datetime_start)
sccrthlt
tak, podnosi ValueError.
Dmitrii Michajłow
4
Zastąpienie tzinfonie powoduje żadnej konwersji, przez co porównanie jest nieprawidłowe.
OrangeDog
+1 za to. I używającutc = pytz.utc aby zapobiec błędowi pylinta No value for argument 'dt' in unbound method call (no-value-for-parameter). pytz link
sam
90

datetime.datetime.now nie zna strefy czasowej.

Django zawiera pomocnika, który wymaga pytz

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

Powinieneś być w stanie porównać nowdochallenge.datetime_start

Alfredo Aguirre
źródło
3
Jeśli USE_TZ=Truenastępnie timezone.now()zwraca obiekt daty i godziny uwzględniający strefę czasową, nawet jeśli pytznie jest zainstalowany (chociaż może być zalecane zainstalowanie z innych powodów).
jfs
49

Jedna linia rozwiązania kodu

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

Wersja wyjaśniona

# 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

Podsumowanie

Musisz dodać informacje o strefie czasowej do daty i now()godziny.
Musisz jednak dodać tę samą strefę czasową zmiennej referencyjnej; dlatego najpierw przeczytałem tzinfoatrybut.

ePi272314
źródło
18

Wyłącz strefę czasową. Posługiwać sięchallenge.datetime_start.replace(tzinfo=None);

Możesz również użyć replace(tzinfo=None)dla innych dat i godzin .

if challenge.datetime_start.replace(tzinfo=None) <= datetime.now().replace(tzinfo=None) <= challenge.datetime_end.replace(tzinfo=None):
Amin Fathi
źródło
2

Więc sposobem na rozwiązanie tego problemu jest upewnienie się, że te dwa czasy są we właściwej strefie czasowej.

Widzę, że używasz datetime.now() która zwróci systemowi bieżący czas, bez ustawionego tzinfo.

tzinfo to informacja dołączona do daty i godziny, aby poinformować go, w jakiej strefie czasowej się znajduje. Jeśli używasz naiwnej daty i godziny, musisz zachować spójność w całym systemie. Gorąco polecam tylko używaniedatetime.utcnow()

widząc, że gdzieś tworzysz datę i godzinę z powiązanymi tzinfo, musisz upewnić się, że są one zlokalizowane (mają przypisane tzinfo) do właściwej strefy czasowej.

Spójrz na Delorean , to znacznie ułatwia radzenie sobie z tego typu rzeczami.

myusuf3
źródło
8
Ten problem jest również widoczny w przypadku utcnow.
Andy Hayden
0

U mnie to działa. Tutaj kieruję tabelę utworzoną datę i godzinę i dodaję 10 minut do daty i godziny. później, w zależności od aktualnego czasu, wykonywane są operacje wygaśnięcia.

from datetime import datetime, time, timedelta
import pytz

Dodano 10 minut do daty i godziny bazy danych

table_datetime = '2019-06-13 07: 49: 02.832969' (przykład)

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

U mnie to zadziałało.

Chandan Sharma
źródło
0

Właśnie:

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

Więc zrób to:

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

a następnie użyj start_timeiend_time

Harispy
źródło