Impossibile confrontare datetime.now () <= challenge.datetime_end ingenuo e consapevole


154

Sto cercando di confrontare la data e l'ora correnti con le date e gli orari specificati nei modelli utilizzando gli operatori di confronto:

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

Lo script si guasta con:

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

I modelli si presentano così:

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

Ho anche Django usando la data e l'ora della locale.

Quello che non sono stato in grado di trovare è il formato utilizzato da Django per DateTimeField (). È ingenuo o consapevole? E come posso ottenere datetime.now () per riconoscere la data datetime locale?




1
c'è una bella lib con cui giocare con la data: pendolo (non sono affiliato)
Thomas Decaux

Risposte:


137

Per impostazione predefinita, l' datetimeoggetto è naivein Python, quindi è necessario renderli entrambi datetimeoggetti ingenui o consapevoli . Questo può essere fatto usando:

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

Nota: questo genererebbe un ValueErrorif tzinfoè già impostato. Se non sei sicuro, usa semplicemente

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

A proposito, è possibile formattare un timestamp UNIX nell'oggetto datetime.datetime con le informazioni sul fuso orario come segue

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)

Dice: ValueError: Datetime non ingenuo (tzinfo è già impostato) quando tenta di calcolare: datetimeStart = utc.localize (challenge.datetime_start)
sccrthlt

sì, genera ValueError.
Dmitrii Mikhailov,

4
La sostituzione di tzinfonon esegue alcuna conversione, rendendo il confronto errato.
OrangeDog,

+1 per questo. E, usando utc = pytz.utcper prevenire l'errore del pylint No value for argument 'dt' in unbound method call (no-value-for-parameter). collegamento pytz
sam

90

datetime.datetime.now non è a conoscenza del fuso orario.

Django viene fornito con un aiuto per questo, che richiede pytz

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

Si dovrebbe essere in grado di confrontare nowachallenge.datetime_start


3
If USE_TZ=Truequindi timezone.now()restituisce un oggetto datetime sensibile al fuso orario anche se pytznon è installato (anche se potrebbe essere consigliato installarlo per altri motivi).
jfs

49

Una riga di soluzione di codice

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

Versione spiegata

# 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

Sommario

Devi aggiungere le informazioni sul fuso orario al tuo now()datetime.
Tuttavia, è necessario aggiungere lo stesso fuso orario della variabile di riferimento; ecco perché ho letto per la prima volta l' tzinfoattributo.


18

Disabilita fuso orario. Usochallenge.datetime_start.replace(tzinfo=None);

È inoltre possibile utilizzare replace(tzinfo=None)per altri datetime .

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

2

Quindi il modo in cui risolverei questo problema è quello di assicurarmi che i due orari siano nel giusto fuso orario.

Vedo che stai usando datetime.now()che restituirà l'ora attuale dei sistemi, senza tzinfo impostato.

tzinfo è l'informazione allegata a un datetime per fargli sapere in quale fuso orario si trova. Se stai usando un datetime ingenuo devi essere coerente attraverso tutto il tuo sistema. Consiglio vivamente di utilizzare solodatetime.utcnow()

visto che da qualche parte stai creando datetime a cui tzinfo è associato, ciò che devi fare è assicurarti che siano localizzati (a cui tzinfo è associato) nel fuso orario corretto.

Dai un'occhiata a Delorean , rende molto più facile gestire questo genere di cose.


8
Vedi anche questo problema con utcnow.
Andy Hayden,

0

Funziona da me. Qui sto geograficando la tabella creata datetime e aggiungendo 10 minuti sul datetime. successivamente, a seconda dell'ora corrente, vengono eseguite le operazioni di scadenza.

from datetime import datetime, time, timedelta
import pytz

Aggiunti 10 minuti sul datetime del database

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

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

Ha funzionato per me.


0

Appena:

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

Quindi fai questo:

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

e poi usa start_timeeend_time

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.