Não é possível comparar datetime.now () ingênuo e consciente <= challenge.datetime_end

153

Estou tentando comparar a data e hora atuais com datas e horas especificadas nos modelos usando operadores de comparação:

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

O script apresenta erros com:

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

Os modelos são assim:

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

Eu também tenho django usando data e hora da localidade.

O que não consegui encontrar é o formato que o django usa para DateTimeField (). É ingênuo ou consciente? E como faço para que o datetime.now () reconheça o datetime da localidade?

sccrthlt
fonte
1
há um muito bom lib para brincar com a data: pêndulo (Eu não sou afiliado)
Thomas Decaux

Respostas:

136

Por padrão, o datetimeobjeto está naiveem Python, portanto, você precisa tornar os dois datetimeobjetos ingênuos ou conscientes . Isso pode ser feito 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: Isso geraria um ValueErrorif tzinfojá está definido. Se você não tiver certeza, basta usar

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

BTW, você pode formatar um timestamp UNIX no objeto datetime.datetime com informações de fuso horário da seguinte maneira

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
fonte
Ele diz: ValueError: datetime Não ingênuo (tzinfo já está definido) quando ele tenta calcular: datetimeStart = utc.localize (challenge.datetime_start)
sccrthlt
sim, gera ValueError.
Dmitrii Mikhailov
4
Substituir o tzinfonão faz nenhuma conversão, tornando a comparação incorreta.
OrangeDog
+1 para isso. E, usando utc = pytz.utcpara evitar o erro de pilão No value for argument 'dt' in unbound method call (no-value-for-parameter). pytz link
sam
90

datetime.datetime.now não está ciente do fuso horário.

O Django vem com um ajudante para isso, o que requer pytz

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

Você deve poder comparar nowcomchallenge.datetime_start

Alfredo Aguirre
fonte
3
Se, em USE_TZ=Trueseguida, timezone.now()retorna um objeto de data e hora com reconhecimento de fuso horário, mesmo que pytznão esteja instalado (embora seja recomendável instalar por outros motivos).
JFS
49

Solução de uma linha de código

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

Versão 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

Resumo

Você deve adicionar as informações do fuso horário à sua now()data e hora.
No entanto, você deve adicionar o mesmo fuso horário da variável de referência; é por isso que li o tzinfoatributo pela primeira vez .

ePi272314
fonte
18

Desativar fuso horário. Usarchallenge.datetime_start.replace(tzinfo=None);

Você também pode usar replace(tzinfo=None)para outra data e hora .

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

Portanto, a maneira como eu resolveria esse problema é garantir que as duas datas estejam no fuso horário certo.

Percebo que você está usando o datetime.now()que retornará a hora atual do sistema, sem o tzinfo definido.

tzinfo é a informação anexada a um datetime para informá-lo em que fuso horário está. Se você estiver usando um datetime ingênuo, precisará ser consistente em todo o sistema. Eu recomendo apenas usardatetime.utcnow()

Como em algum lugar em que você está criando data e hora que o tzinfo está associado a elas, o que você precisa fazer é garantir que elas estejam localizadas (tem o tzinfo associado) no fuso horário correto.

Dê uma olhada no Delorean , isso facilita muito o tratamento desse tipo de coisa.

myusuf3
fonte
8
Você também vê esse problema no utcnow.
Andy Hayden
0

Está funcionando de mim. Aqui eu estou geeting a tabela criada datetime e adicionando 10 minutos no datetime. posteriormente, dependendo da hora atual, as Operações de expiração são concluídas.

from datetime import datetime, time, timedelta
import pytz

Adicionados 10 minutos no datetime do banco de dados

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

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

Funcionou para mim.

Chandan Sharma
fonte
0

Somente:

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

Então faça o seguinte:

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 depois use start_timeeend_time

Harispy
fonte