413 Stimmen

JSON Datum- und Uhrzeit zwischen Python und JavaScript

Ich möchte ein datetime.datetime-Objekt in serialisierter Form von Python aus mithilfe von JSON senden und in JavaScript unter Verwendung von JSON de-serialisieren. Wie ist der beste Weg, dies zu tun?

0 Stimmen

Bevorzugen Sie die Verwendung einer Bibliothek oder möchten Sie dies selbst codieren?

377voto

JT. Punkte 623

Sie können den Parameter 'default' zu json.dumps hinzufügen, um dies zu behandeln:

date_handler = lambda obj: (
    obj.isoformat()
    if isinstance(obj, (datetime.datetime, datetime.date))
    else None
)
json.dumps(datetime.datetime.now(), default=date_handler)
'"2010-04-20T20:08:21.634121"'

Welches im ISO 8601 Format ist.

Eine umfassendere Standardhandler-Funktion:

def handler(obj):
    if hasattr(obj, 'isoformat'):
        return obj.isoformat()
    elif isinstance(obj, ...):
        return ...
    else:
        raise TypeError, 'Objekt des Typs %s mit dem Wert %s ist nicht JSON-serialisierbar' % (type(obj), repr(obj))

Aktualisierung: Ausgabe des Typs sowie des Werts hinzugefügt.
Aktualisierung: Auch mit Datum umgehen

1 Stimmen

Diese Lösung ist viel einfacher als die von mdorseif. Funktioniert super in meiner Django-App.

11 Stimmen

Das Problem besteht darin, dass dieser Code andere Objekte in der Liste/dem Dictionary in None umwandelt.

5 Stimmen

Json.dumps weiß nicht, wie man diese auch konvertiert, aber die Ausnahme wird unterdrückt. Leider hat ein Einzeiler-Lambda-Fix seine Nachteile. Wenn Sie lieber eine Ausnahme bei den Unbekannten auslösen möchten (was eine gute Idee ist), verwenden Sie die von mir oben hinzugefügte Funktion.

90voto

max Punkte 27697

Für länderübergreifende Projekte habe ich herausgefunden, dass Zeichenketten, die RFC 3339-Daten enthalten, der beste Weg sind. Ein RFC 3339-Datum sieht so aus:

  1985-04-12T23:20:50.52Z

Ich denke, der größte Teil des Formats ist offensichtlich. Das einzige etwas ungewöhnliche könnte das "Z" am Ende sein. Es steht für GMT/UTC. Sie könnten auch einen Zeitzonenoffset wie +02:00 für CEST (Deutschland im Sommer) hinzufügen. Ich persönlich ziehe es vor, alles in UTC zu behalten, bis es angezeigt wird.

Zum Anzeigen, Vergleichen und Speichern können Sie es in Zeichenkettenformat in allen Sprachen belassen. Wenn Sie das Datum für Berechnungen benötigen, ist es einfach, es in den meisten Sprachen zurück in ein natives Datumobjekt zu konvertieren.

Generieren Sie also das JSON so:

  json.dump(datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))

Leider akzeptiert der Date-Konstruktor von JavaScript keine RFC 3339-Zeichenketten, aber im Internet sind viele Parser verfügbar.

huTools.hujson versucht, die häufigsten Codierungsprobleme zu lösen, auf die Sie in Python-Code stoßen könnten, einschließlich Datum-/Datumzeitobjekten, während es mit Zeitzonen korrekt umgeht.

17 Stimmen

Dieser Datumsformatierungsmechanismus wird nativ unterstützt, sowohl von datetime: datetime.isoformat() als auch von simplejson, das datetime-Objekte standardmäßig als isoformat-Zeichenfolgen ablegt. Kein manuelles strftime-Hacken erforderlich.

9 Stimmen

@jrk - Ich erhalte keine automatische Konvertierung von datetime Objekten in den isoformat String. Für mich liefert simplejson.dumps(datetime.now()) TypeError: datetime.datetime(...) ist nicht JSON-serialisierbar.

7 Stimmen

json.dumps(datetime.datetime.now().isoformat()) ist wo die Magie passiert.

73voto

user240515 Punkte 2766

Ich habe es herausgearbeitet.

Angenommen, Sie haben ein Python-Datumsobjekt d, das mit datetime.now() erstellt wurde. Sein Wert ist:

datetime.datetime(2011, 5, 25, 13, 34, 5, 787000)

Sie können es als JSON in einen ISO 8601 Datumszeichenfolge serialisieren:

import json
json.dumps(d.isoformat())

Das Beispiel-Datumsobjekt würde als folgendes serialisiert werden:

'"2011-05-25T13:34:05.787000"'

Dieser Wert kann dann in der JavaScript-Ebene ein Date-Objekt konstruieren:

var d = new Date("2011-05-25T13:34:05.787000");

Ab JavaScript 1.8.5 enthalten Date-Objekte eine toJSON-Methode, die eine Zeichenfolge in einem standardisierten Format zurückgibt. Um das obige JavaScript-Objekt also zurück nach JSON zu serialisieren, wäre der Befehl:

d.toJSON()

Dies würde Ihnen geben:

'2011-05-25T20:34:05.787Z'

Diese Zeichenfolge kann, sobald sie in Python empfangen wurde, zurück in ein Datumsobjekt deserialisiert werden:

datetime.strptime('2011-05-25T20:34:05.787Z', '%Y-%m-%dT%H:%M:%S.%fZ')

Dies führt zu dem folgenden Datumsobjekt, das dasselbe ist wie das, mit dem Sie angefangen haben und folglich korrekt:

datetime.datetime(2011, 5, 25, 20, 34, 5, 787000)

6 Stimmen

Zeitzonen werden dies vollkommen durcheinander bringen. Nehmen wir an, Sie arbeiten in Python in UTC (nur eine verrückte Person tut dies anders) - JSON-Ausgabe aus Python hat keine Zeitzone, also wird JavaScript sie als lokale Zeitzone interpretieren. JavaScript d.toJSON wird in UTC konvertieren. Also für Ihr Beispiel Datum (2011-04-25) auf einem Browser im Vereinigten Königreich (Sommerzeit also UTC+1) gibt Python 13:34 aus - JS interpretiert dies als lokale Zeitzone oder UTC 12:34 - JS gibt dann UTC aus, also 12:34. Python würde dies als 12:34 interpretieren. Sie haben eine Stunde verloren (oder einen ganzen Tag, wenn Sie nur mit Daten und nicht mit Zeiten arbeiten). Außer im Winter.

52voto

ramen Punkte 619

Unter Verwendung von json können Sie JSONEncoder untergeordnet machen und die Methode default() überschreiben, um Ihre eigenen benutzerdefinierten Serialisierer bereitzustellen:

import json
import datetime

class DateTimeJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        else:
            return super(DateTimeJSONEncoder, self).default(obj)

Dann können Sie es so aufrufen:

>>> DateTimeJSONEncoder().encode([datetime.datetime.now()])
'["2010-06-15T14:42:28"]'

7 Stimmen

Kleine Verbesserung - verwenden Sie obj.isoformat(). Sie können auch den häufigeren dumps()-Aufruf verwenden, der andere nützliche Argumente (wie indent) akzeptiert: simplejson.dumps(myobj, cls=JSONEncoder, ...)

3 Stimmen

Weil das die Methode des Elternteils von JSONEncoder aufrufen würde, nicht die Methode des Elternteils von DateTimeJSONEncoder. Das heißt, du würdest zwei Ebenen höher gehen.

31voto

Chris Arndt Punkte 1970

Hier ist eine ziemlich vollständige Lösung für die rekursive Codierung und Decodierung von datetime.datetime- und datetime.date-Objekten unter Verwendung des Standardbibliotheksmoduls json. Dies erfordert Python >= 2.6, da der Formatcode %f im datetime.datetime.strptime() Formatstring erst seitdem unterstützt wird. Für Python 2.5-Unterstützung entfernen Sie %f und Strippen der Mikrosekunden aus der ISO 8601 Datumsschnur, bevor Sie versuchen, sie umzuwandeln, aber Sie verlieren natürlich die Mikrosekundenpräzision. Für die Interoperabilität mit ISO 8601-Datumsschnüren aus anderen Quellen, die einen Zeitzonennamen oder UTC-Versatz enthalten können, müssen Sie möglicherweise auch einige Teile der Datumsschnur vor der Konvertierung entfernen. Für einen vollständigen Parser für ISO 8601-Datumsschnüre (und viele andere Datumsformate) sehen Sie das externe Modul dateutil.

Die Dekodierung funktioniert nur, wenn die ISO 8601-Datumsschnüre Werte in einer JavaScript Literalobjektdarstellung oder in verschachtelten Strukturen innerhalb eines Objekts sind. ISO 8601-Datumsschnüre, die Elemente eines Arrays der obersten Ebene sind, werden nicht dekodiert.

D.h. das funktioniert:

Datum = datetime.datetime.now()
>>> json = dumps(dict(foo='bar', innerdict=dict(date=date)))
>>> json
'{"innerdict": {"date": "2010-07-15T13:16:38.365579"}, "foo": "bar"}'
>>> loads(json)
{u'innerdict': {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)},
u'foo': u'bar'}

Und das auch:

>>> json = dumps(['foo', 'bar', dict(date=date)])
>>> json
'["foo", "bar", {"date": "2010-07-15T13:16:38.365579"}]'
>>> loads(json)
[u'foo', u'bar', {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)}]

Aber das funktioniert nicht wie erwartet:

>>> json = dumps(['foo', 'bar', date])
>>> json
'["foo", "bar", "2010-07-15T13:16:38.365579"]'
>>> loads(json)
[u'foo', u'bar', u'2010-07-15T13:16:38.365579']

Hier ist der Code:

__all__ = ['dumps', 'loads']

import datetime

try:
    import json
except ImportError:
    import simplejson as json

class JSONDateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (datetime.date, datetime.datetime)):
            return obj.isoformat()
        else:
            return json.JSONEncoder.default(self, obj)

def datetime_decoder(d):
    if isinstance(d, list):
        pairs = enumerate(d)
    elif isinstance(d, dict):
        pairs = d.items()
    result = []
    for k,v in pairs:
        if isinstance(v, basestring):
            try:
                # Der Formatcode %f wird nur in Python >= 2.6 unterstützt.
                # Für Python <= 2.5, Mikrosekunden abschneiden
                # v = datetime.datetime.strptime(v.rsplit('.', 1)[0],
                #     '%Y-%m-%dT%H:%M:%S')
                v = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S.%f')
            except ValueError:
                try:
                    v = datetime.datetime.strptime(v, '%Y-%m-%d').date()
                except ValueError:
                    pass
        elif isinstance(v, (dict, list)):
            v = datetime_decoder(v)
        result.append((k, v))
    if isinstance(d, list):
        return [x[1] for x in result]
    elif isinstance(d, dict):
        return dict(result)

def dumps(obj):
    return json.dumps(obj, cls=JSONDateTimeEncoder)

def loads(obj):
    return json.loads(obj, object_hook=datetime_decoder)

if __name__ == '__main__':
    mytimestamp = datetime.datetime.utcnow()
    mydate = datetime.date.today()
    data = dict(
        foo = 42,
        bar = [mytimestamp, mydate],
        date = mydate,
        timestamp = mytimestamp,
        struct = dict(
            date2 = mydate,
            timestamp2 = mytimestamp
        )
    )

    print repr(data)
    jsonstring = dumps(data)
    print jsonstring
    print repr(loads(jsonstring))

0 Stimmen

Wenn Sie das Datum drucken wie datetime.datetime.utcnow().isoformat()[:-3]+"Z", wird es genau wie das aussehen, was JSON.stringify() in JavaScript produziert.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X