2015-12-14 6 views
5

Ich versuche, einen RSS-Feed zu analysieren. Einträge im Feed haben Datum Elemente wie:Datetime-Lokalisierung mit Python/Django

<dc:date>2016-09-21T16:00:00+02:00</dc:date> 

feedparser verwenden, ich versuche zu tun:

published_time = datetime.fromtimestamp(mktime(entry.published_parsed)) 

Aber das Problem ist, dass ich scheinen die falsche Zeit in der Datenbank gespeichert zu bekommen. In diesem speziellen Fall wird die Datetime wie folgt gespeichert:

2016-09-21 13:00:00 

... wenn ich 14:00 Uhr erwarte - die korrekte UTC-Zeit.

Ich nehme an, das Problem in unserer django Einstellungen ist, wo wir haben:

TIME_ZONE = 'Europe/Berlin' 

Denn wenn ich wechseln:

TIME_ZONE = 'UTC' 

... die Datatime die richtige UTC-Zeit gespeichert ist:

2016-09-21 14:00:00 

gibt es eine Möglichkeit, die django Einstellungen zu halten, wie sie sind, aber diese Datetime-c zu analysieren und zu speichern Oder, ohne dass die Django-Zeitzoneneinstellung dies beeinflusst?

EDIT: Vielleicht ist es mehr klar wie dieser ...

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 
+1

Sind Sie an einer Zeitzonenumwandlung interessiert oder möchten Sie einfach eine Stunde mit einer Datetime hinzufügen?Timedelta-Betrieb? – JwM

+0

Letztendlich möchte ich die korrekte Zeit in UTC haben. Eine Stunde weg zu nehmen (zwei Stunden in der Tagessparperiode) kann ein Weg sein zu gehen. Ich habe es aber noch nicht angeschaut. Ich habe mich gefragt, ob es einen anderen Weg gab. Ich habe zum Beispiel timezone.activate() und timezone.deactivate() versucht, die die current_timezone auf die richtige Weise zu ändern schien, aber das hat das Problem nicht behoben. – apiljic

+0

Sie können eine datetime zur Kenntnis nehmen oder die Zeitzone ändern, wenn sie bereits weiß, aber falsch ist. –

Antwort

2

feedparsers entry.published_parsed ist immer ein UTZ Zeit Tupel, was auch immer die Eingabe Zeit String ist. Um Zeitzone-aware datetime Objekt:

from datetime import datetime 

utc_time = datetime(*entry.published_parsed[:6], tzinfo=utc) 

wo utc ist ein Tzinfo Objekt wie datetime.timezone.utc, pytz.utc, oder einfach nur Ihre custom tzinfo (for older python versions).

Sie sollten keine UTC-Zeit an mktime() übergeben, die eine lokale Zeit erwartet. Gleicher Fehler: Have a correct datetime with correct timezone.

Stellen Sie sicher, USE_TZ=True, so dass Django verwendet bewusst datetime Objekte überall. Bei einem zeitzonenbewussten Datetime-Objekt sollte django es korrekt in db speichern, unabhängig davon, wie Ihre TIME_ZONE or timezone.get_current_timezone() are aussieht.

+0

Ich habe auch diese Lösung versucht. Es funktioniert auch. Vielen Dank! – apiljic

1

Haben Sie versucht datetime.utcfromtimestamp() statt datetime.fromtimestamp() verwenden?

Als sekundäre Lösung können Sie den nicht zerlegten Daten zu bekommen (ich glaube, es als entry.published verfügbar ist?) Und nur verwenden Python-dateutil die Zeichenfolge zu analysieren, dann wandelt es in pytz.utc Zeitzone wie folgt aus.

>>> 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>) 
+0

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 13:00 : 00 2016-09-21 13: 00: 00 + 00: 00 ... Dies ist die Ausgabe von utcfromtimestamp(). Die Zeitzone wurde geändert, aber die Zeit ist immer noch nicht korrekt. – apiljic

+0

Zweite Lösung könnte funktionieren. Meine einzige Sorge ist, dass es viele verschiedene Datumsformate gibt. Nach dem, was wir bisher erlebt haben, hatte Feed-Parser mit keinem von ihnen ein Problem. Ich frage mich, ob der von Ihnen vorgeschlagene Parser gleich gut funktioniert. Verwenden Sie es für viele verschiedene Datumsformate? – apiljic

+1

@apiljic: Verwenden Sie feedparser, um Eingabezeit-Strings zu parsen ('_parsed' Attribute). 'dateutil' akzeptiert zu viele Eingabezeitformate und kann deshalb ein falsches Ergebnis im Hintergrund zurückgeben. – jfs

1

Verwenden

published_time = pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed))) 

feedparser eine große Auswahl von Datumsformaten analysieren kann, können Sie sie here finden.

Wie Sie in feedparser/feedparser/datetimes/__init__.py, die eingebaute Funktion von feedparser _parse_date führt Folgendes sehen: Dieser

Parses a variety of date formats into a 9-tuple in GMT

bedeutet, dass in parsed_entry.published_parsed Sie haben eine time.struct_time Objekt in GMT Zeitzone.

Wenn Sie es zu einem datetime Objekt konvertieren mit

published_time = datetime.fromtimestamp(mktime(parsed_entry.published_parsed)) 

das Problem ist, dass mktime geht davon aus, dass die übergebene Tupel in Ortszeit ist, das nicht der Fall, es ist GMT/UTC! Ansonsten lokalisieren Sie das Objekt datetime am Ende der Konvertierung nicht ordnungsgemäß.

Sie müssen diese Konvertierung durch die folgenden ersetzen, wobei Sie daran denken, dass Feedparser eine GMT struct_time zurückgibt und diese mit der gewünschten Zeitzone lokalisiert (UTC der Einfachheit halber).

  • Sie verwenden calendar.timegm, die die Anzahl der Sekunden zwischen Epoche und das Datum als Parameter übergeben gibt, unter der Annahme, dass das übergebene Objekt in UTC ist/GMT (wir wissen aus feedparser es ist)
  • Sie utcfromtimestamp verwenden um ein naives datetime Objekt zu erhalten (von dem wir wissen, dass es ein Datetime in UTC ist, aber Python nicht in diesem Moment)
  • Mit pytz.utc.localize lokalisieren Sie in UTC das datetime Objekt richtig.

Beispiel:

import calendar 
from datetime import datetime 
import pytz 
localized_dt = pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed))) 

Solange Sie konsistent sind, spielt es keine Rolle, ob Sie fromtimestamp oder utcfromtimestamp verwenden. Wenn Sie fromtimestamp verwenden, müssen Sie Python mitteilen, dass das von Ihnen erstellte Objekt datetime über die lokale Zeitzone verfügt. Angenommen, Sie in Europa/Berlin sind, ist dies auch in Ordnung:

pytz.timezone('Europe/Berlin').localize(datetime.fromtimestamp(calendar.timegm(parsed_entry.published_parsed))) 

Waren parsed_entry.published_parsed auch in lokaler Zeitzone, mktime anstelle von calendar.timegm verwendet werden muss.

Als Alternative Sie sich die Datenkette analysieren können Sie von feedparser erhalten parsed_entry['published']

from dateutil import parser 
localized_dt = parser.parse(parsed_entry['published']) 

Sie können überprüfen, dass die folgenden Renditen True:

parser.parse(parsed_entry['published']) == pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed))) 

Die Django TIME_ZONE Einstellung tatsächlich nicht wichtig, weil es nur zu Visualisierungszwecken verwendet wird oder um naive Datumsangaben automatisch zu konvertieren.

When USE_TZ is True, this is the default time zone that Django will use to display datetimes in templates and to interpret datetimes entered in forms.

Es ist wichtig, immer genau lokalisierte Datumswerte zu verwenden, egal welche Zeitzone verwendet wird. Solange sie nicht naiv sind, werden sie von Django korrekt gehandhabt.

+0

ist es unnötig kompliziert. Hier ist eine [einfachere Lösung] (http://Stackoverflow.com/a/34292796/4279) – jfs

+0

Ich stimme zu, Sie brauchen diese Komplikation, wenn Sie das dst-Flag berücksichtigen müssen, was für eine lokale Zeit der Fall ist (das ist wo Sie verwenden mktime) und nicht für UTC, die es nicht hat. –

+0

Wenn die Zeit nicht UTC ist, dann ist der Code nicht nur kompliziert; es ist einfach falsch. – jfs