Impossible de comparer naïf et conscient datetime.now () <= challenge.datetime_end

154

J'essaie de comparer la date et l'heure actuelles avec les dates et heures spécifiées dans les modèles à l'aide d'opérateurs de comparaison:

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

Les erreurs de script avec:

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

Les modèles ressemblent à ceci:

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

J'ai aussi django en utilisant la date et les heures locales.

Ce que je n'ai pas pu trouver, c'est le format utilisé par django pour DateTimeField (). Est-ce naïf ou conscient? Et comment faire pour que datetime.now () reconnaisse la date et l'heure de la locale?

sccrthlt
la source
1
il y a une très belle lib pour jouer avec date: pendule (je ne suis pas affilié)
Thomas Decaux

Réponses:

137

Par défaut, l' datetimeobjet est naiveen Python, vous devez donc en faire des datetimeobjets naïfs ou conscients . Cela peut être fait en utilisant:

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

Remarque: cela soulèverait un ValueErrorif tzinfoest déjà défini. Si vous n'êtes pas sûr de cela, utilisez simplement

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

BTW, vous pouvez formater un horodatage UNIX dans l'objet datetime.datetime avec les informations de fuseau horaire comme suit

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
la source
Il dit: ValueError: datetime non naïf (tzinfo est déjà défini) quand il tente de calculer: datetimeStart = utc.localize (challenge.datetime_start)
sccrthlt
oui, cela soulève ValueError.
Dmitrii Mikhailov
4
Le remplacement de tzinfone fait aucune conversion, ce qui rend la comparaison incorrecte.
OrangeDog
+1 pour cela. Et, en utilisant utc = pytz.utcpour éviter l'erreur de pylint No value for argument 'dt' in unbound method call (no-value-for-parameter). pytz link
sam
90

datetime.datetime.now n'est pas conscient du fuseau horaire.

Django est livré avec une aide pour cela, qui nécessite pytz

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

Vous devriez pouvoir comparer nowàchallenge.datetime_start

Alfredo Aguirre
la source
3
If USE_TZ=Truethen timezone.now()renvoie un objet datetime prenant en charge le fuseau horaire même s'il pytzn'est pas installé (bien qu'il puisse être recommandé d'installer pour d'autres raisons).
jfs
49

Solution d'une ligne de code

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

Version expliquée

# 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

Résumé

Vous devez ajouter les informations de fuseau horaire à votre now()datetime.
Cependant, vous devez ajouter le même fuseau horaire de la variable de référence; c'est pourquoi j'ai d'abord lu l' tzinfoattribut.

ePi272314
la source
18

Désactivez le fuseau horaire. Utilisationchallenge.datetime_start.replace(tzinfo=None);

Vous pouvez également utiliser replace(tzinfo=None)pour d'autres datetime .

if challenge.datetime_start.replace(tzinfo=None) <= datetime.now().replace(tzinfo=None) <= challenge.datetime_end.replace(tzinfo=None):
Amin Fathi
la source
2

Donc, la façon dont je résoudrais ce problème est de m'assurer que les deux datetimes sont dans le bon fuseau horaire.

Je peux voir que vous utilisez datetime.now() ce qui renverra l'heure actuelle du système, sans tzinfo défini.

tzinfo est l'information attachée à un datetime pour lui faire savoir dans quel fuseau horaire il se trouve. Si vous utilisez un datetime naïf, vous devez être cohérent dans tout votre système. Je recommande vivement d'utiliser uniquementdatetime.utcnow()

vu que vous créez un datetime auquel tzinfo est associé, ce que vous devez faire est de vous assurer que ceux-ci sont localisés (a tzinfo associé) dans le bon fuseau horaire.

Jetez un œil à Delorean , cela facilite grandement la gestion de ce genre de choses.

myusuf3
la source
8
Vous voyez également ce problème avec utcnow.
Andy Hayden
0

Cela fonctionne de moi. Ici, j'obtiens la table créée datetime et j'ajoute 10 minutes sur la datetime. plus tard en fonction de l'heure actuelle, les opérations d'expiration sont effectuées.

from datetime import datetime, time, timedelta
import pytz

Ajout de 10 minutes sur la date et l'heure de la base de données

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

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

Cela a fonctionné pour moi.

Chandan Sharma
la source
0

Juste:

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

Alors fais ceci:

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

puis utilisez start_timeetend_time

Harispy
la source