Eu estou tentando analisar um feed RSS. As entradas no feed têm elementos de data como:
<dc:date>2016-09-21T16:00:00+02:00</dc:date>
Usando o feedparser, tento fazer:
published_time = datetime.fromtimestamp(mktime(entry.published_parsed))
Mas o problema é que pareço estar recebendo o horário errado armazenado no banco de dados. Nesse caso específico, o datetime é armazenado como:
2016-09-21 13:00:00
... quando eu esperaria 14:00 - o horário UTC correto.
Eu assumo que o problema está nas configurações do django, onde temos:
TIME_ZONE = "Europe/Berlin"
Porque quando eu mudo para:
TIME_ZONE = "UTC"
... o datatime é armazenado como horário UTC correto:
2016-09-21 14:00:00
Existe alguma maneira de manter as configurações do django como estão, mas para analisar e armazenar este datetime corretamente, sem que a configuração do fuso horário do django o afete?
EDITAR: Talvez seja mais claro assim ...
print entry.published_parsed
published_time = datetime.fromtimestamp(mktime(entry.published_parsed))
print published_time
localized_time = pytz.timezone(settings.TIME_ZONE).localize(published_time, is_dst=None)
print localized_time
time.struct_time(tm_year=2016, tm_mon=9, tm_mday=21, tm_hour=14, tm_min=0, tm_sec=0, tm_wday=2, tm_yday=265, tm_isdst=0)
2016-09-21 15:00:00
2016-09-21 15:00:00+02:00
Respostas:
2 para resposta № 1feedparser "s entry.published_parsed
é sempre uma tupla de tempo utc, seja qual for a string de tempo de entrada. Para obter reconhecimento de fuso horário datetime
objeto:
from datetime import datetime
utc_time = datetime(*entry.published_parsed[:6], tzinfo=utc)
Onde utc
é um objeto tzinfo como datetime.timezone.utc
, pytz.utc
ou apenas o seu tzinfo personalizado (para versões mais antigas do python).
Você não deve passar o tempo para mktime()
que espera um horário local. Mesmo erro: Tenha um datetime correto com fuso horário correto.
Certificar-se de que USE_TZ=True
para que o django use objetos datetime conscientes em todos os lugares. Dado um objeto datetime com reconhecimento de fuso horário, o django deve salvá-lo em db corretamente TIME_ZONE
ou timezone.get_current_timezone()
estamos.
1 para resposta № 2
Você já tentou usar datetime.utcfromtimestamp()
ao invés de datetime.fromtimestamp()
?
Como uma solução secundária, você pode obter os dados não analisados (acredito que estejam disponíveis como entry.published
?) e apenas use o python-dateutil para analisar a string e convertê-la em pytz.utc
fuso horário como este.
>>> import pytz
>>> from dateutil import parser
>>> dt = parser.parse("2016-09-21T16:00:00+02:00")
>>> dt
datetime.datetime(2016, 9, 21, 16, 0, tzinfo=tzoffset(None, 7200))
>>> dt.astimezone(pytz.utc)
datetime.datetime(2016, 9, 21, 14, 0, tzinfo=<UTC>)
1 para resposta № 3
Usar
published_time = pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed)))
O Feedparser pode analisar uma grande variedade de formatos de data, você pode encontrá-los Aqui.
Como você pode ver em feedparser/feedparser/datetimes/__init__.py
, a função interna do Feedparser _parse_date
faz o seguinte:
Analisa vários formatos de data em uma 9-tupla no GMT
Isso significa que parsed_entry.published_parsed
você tem um time.struct_time
objeto no fuso horário GMT.
Quando você converte para um datetime
objeto usando
published_time = datetime.fromtimestamp(mktime(parsed_entry.published_parsed))
o problema é que mktime
assume que a tupla passada está em horário local, que não é, é "GMT / UTC! Diferente do que você não" corretamente localizar o datetime
objeto no final da conversão.
Você precisa substituir essa conversão pelo seguinte, lembrando que o Feedparser retorna um GMT struct_time
e localize isso com o fuso horário que você gosta (UTC para simplificar).
- Você usa
calendar.timegm
, que dá o número de segundos entre a época e a data passada como um parâmetro, assumindo que o objeto passado está em UTC / GMT (sabemos que é do Feedparser) - Você usa
utcfromtimestamp
para obter um ingênuodatetime
objeto (que sabemos representa um datetime no UTC, mas o Python não faz isso no momento) - Com
pytz.utc.localize
você localiza corretamente no UTC odatetime
objeto.
Exemplo:
import calendar
from datetime import datetime
import pytz
localized_dt = pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed)))
Contanto que você seja consistente, não importa se você usa fromtimestamp
ou utcfromtimestamp
. Se você usar fromtimestamp
você precisa dizer ao Python que o datetime
O objeto que você criou tem o fuso horário local. Supondo que você esteja na Europa / Berlim, isso também é bom:
pytz.timezone("Europe/Berlin").localize(datetime.fromtimestamp(calendar.timegm(parsed_entry.published_parsed)))
Estavam parsed_entry.published_parsed
também no fuso horário local, mktime
deve ser usado no lugar de calendar.timegm
.
Como alternativa, você pode analisar a sequência de dados que recebe do Feedparser parsed_entry["published"]
from dateutil import parser
localized_dt = parser.parse(parsed_entry["published"])
Você pode verificar se os seguintes retornos True
:
parser.parse(parsed_entry["published"]) == pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed)))
O Django TIME_ZONE
A configuração não importa, pois é usada apenas para fins de visualização ou para converter automaticamente tempos de dados ingênuos.
Quando USE_TZ é True, este é o fuso horário padrão que o Django utilizará para exibir os datetimes nos templates e para interpretar os datetimes inseridos nos formulários.
O importante é sempre usar tempos de dados adequadamente localizados, independentemente do fuso horário usado. Desde que não estejam em formato ingênuo, serão manipulados adequadamente pelo Django.