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?




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)

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


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.


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

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.


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.


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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.