No se puede comparar datetime.now () <= challenge.datetime_end ingenuo y consciente

154

Estoy tratando de comparar la fecha y hora actual con las fechas y horas especificadas en los modelos que utilizan operadores de comparación:

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

Los errores de script con:

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

Los modelos se ven así:

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

También tengo django usando la fecha y hora locales.

Lo que no he podido encontrar es el formato que usa django para DateTimeField (). ¿Es ingenuo o consciente? ¿Y cómo obtengo datetime.now () para reconocer la fecha y hora local?

sccrthlt
fuente
1
hay una muy buena lib para jugar con la fecha: péndulo (no estoy afiliado)
Thomas Decaux

Respuestas:

137

Por defecto, el datetimeobjeto está naiveen Python, por lo que debe hacer que ambos sean datetimeobjetos ingenuos o conscientes . Esto se puede hacer 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: Esto generaría un ValueErrorif si tzinfoya está configurado. Si no está seguro de eso, simplemente use

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

Por cierto, podría formatear una marca de tiempo UNIX en el objeto datetime.datetime con información de zona horaria de la siguiente manera

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
fuente
Dice: ValueError: no ingenuo datetime (tzinfo ya está configurado) cuando intenta calcular: datetimeStart = utc.localize (challenge.datetime_start)
sccrthlt
sí, aumenta ValueError.
Dmitrii Mikhailov
44
Reemplazar el tzinfono hace ninguna conversión, haciendo que la comparación sea incorrecta.
OrangeDog
+1 por esto. Y, utilizando utc = pytz.utcpara evitar el error de pylint No value for argument 'dt' in unbound method call (no-value-for-parameter). enlace pytz
sam
90

datetime.datetime.now no es consciente de la zona horaria.

Django viene con un ayudante para esto, que requiere pytz

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

Deberías poder comparar nowconchallenge.datetime_start

Alfredo Aguirre
fuente
3
Si USE_TZ=Trueluego timezone.now()devuelve un objeto de fecha y hora con reconocimiento de zona horaria, incluso si pytzno está instalado (aunque podría recomendarse instalarlo por otros motivos).
jfs
49

Una línea de solución de código

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

Versión explicada

# 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

Resumen

Debe agregar la información de la zona horaria a su now()fecha y hora.
Sin embargo, debe agregar la misma zona horaria de la variable de referencia; Es por eso que primero leí el tzinfoatributo.

ePi272314
fuente
18

Deshabilitar zona horaria. Utilizarchallenge.datetime_start.replace(tzinfo=None);

También puede usar replace(tzinfo=None)para otra fecha y hora .

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

Entonces, la forma en que resolvería este problema es asegurándome de que las dos fechas y horas estén en la zona horaria correcta.

Puedo ver que está utilizando datetime.now()lo que devolverá la hora actual del sistema, sin tzinfo configurado.

tzinfo es la información adjunta a una fecha y hora para hacerle saber en qué zona horaria se encuentra. Si está utilizando una fecha y hora ingenua, debe ser coherente en todo el sistema. Recomiendo encarecidamente solo usardatetime.utcnow()

ya que estás creando un datetime que tiene tzinfo asociado con ellos, lo que debes hacer es asegurarte de que estén localizados (tiene tzinfo asociado) en la zona horaria correcta.

Eche un vistazo a Delorean , hace que lidiar con este tipo de cosas sea mucho más fácil.

myusuf3
fuente
8
También ve este problema con utcnow.
Andy Hayden
0

Está trabajando de mí. Aquí estoy obteniendo la tabla de fecha y hora creada y agregando 10 minutos en la fecha y hora. más tarde, dependiendo de la hora actual, se realizan operaciones de vencimiento.

from datetime import datetime, time, timedelta
import pytz

Se agregaron 10 minutos en la fecha y hora de la base de datos

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

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

Funcionó para mi.

Chandan Sharma
fuente
0

Sólo:

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

Entonces haz esto:

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

y luego usar start_timeyend_time

Harispy
fuente