web-dev-qa-db-de.com

JSON-Datetime zwischen Python und JavaScript

Ich möchte ein datetime.datetime-Objekt in serialisierter Form von Python mithilfe von JSON senden und in JavaScript mit JSON de-serialize verwenden. Was ist der beste Weg, dies zu tun?

377
kevin

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 ist das Format ISO 8601

Eine umfassendere Standardhandlerfunktion:

def handler(obj):
    if hasattr(obj, 'isoformat'):
        return obj.isoformat()
    Elif isinstance(obj, ...):
        return ...
    else:
        raise TypeError, 'Object of type %s with value of %s is not JSON serializable' % (type(obj), repr(obj))

Update: Ausgabe von Typ und Wert hinzugefügt.
Update: Datum auch behandeln 

364
JT.

Für sprachübergreifende Projekte habe ich herausgefunden, dass Zeichenketten mit RfC 3339 -Daten der beste Weg sind. Ein RfC 3339-Datum sieht folgendermaßen aus:

  1985-04-12T23:20:50.52Z

Ich denke, das meiste ist offensichtlich. Das einzig Ungewöhnliche ist vielleicht das "Z" am Ende. Es steht für GMT/UTC. Sie können auch eine Zeitzonenverschiebung wie +02: 00 für MESZ (Deutschland im Sommer) hinzufügen. Ich persönlich ziehe es vor, alles in UTC aufzubewahren, bis es angezeigt wird.

Zum Anzeigen, Vergleichen und Speichern können Sie es in allen Sprachen in einem String-Format belassen. Wenn Sie das Datum für Berechnungen benötigen, können Sie es einfach in ein natives Datumsobjekt in den meisten Sprachen konvertieren.

So generieren Sie die JSON so:

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

Leider akzeptiert der Date-Konstruktor von Javascript keine RfC 3339-Zeichenfolgen, aber im Internet sind viele Parser verfügbar.

huTools.hujson versucht, die häufigsten Codierungsprobleme zu behandeln, die im Python-Code auftreten können, einschließlich Datums-/Datums-/Zeitobjekten, während Zeitzonen korrekt verarbeitet werden.

75
max

Ich habe es geklärt.

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

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

Sie können es als ISO 8601-Datetime-String in JSON serialisieren:

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

Das Beispieldatetime-Objekt würde folgendermaßen serialisiert:

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

Dieser Wert kann, sobald er in der Javascript-Ebene empfangen wurde, ein Date-Objekt erstellen:

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

Seit Javascript 1.8.5 haben Date-Objekte eine toJSON-Methode, die eine Zeichenfolge in einem Standardformat zurückgibt. Um das obige Javascript-Objekt wieder in JSON zu serialisieren, würde der Befehl folgendermaßen lauten:

d.toJSON()

Was würde Ihnen geben:

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

Sobald diese Zeichenfolge in Python empfangen wurde, könnte sie wieder zu einem datetime-Objekt deserialisiert werden:

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

Dies führt zu dem folgenden datetime-Objekt, das dasselbe ist, mit dem Sie begonnen haben und daher korrekt sind:

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

Mit json können Sie JSONEncoder subclassieren und die default () -Methode ü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 kannst du es so nennen:

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

Hier finden Sie eine ziemlich vollständige Lösung für das rekursive Codieren und Decodieren von datetime.datetime- und datetime.date-Objekten mithilfe des Standardbibliothek json. Dies erfordert Python> = 2.6, da der Formatcode %f in der Formatzeichenfolge datetime.datetime.strptime () seitdem nur noch unterstützt wird. Wenn Sie Python 2.5 unterstützen, löschen Sie den %f und entfernen Sie die Mikrosekunden aus der ISO-Datumszeichenfolge, bevor Sie eine Konvertierung durchführen. Die Genauigkeit in Mikrosekunden geht jedoch verloren. Aus Gründen der Interoperabilität mit ISO-Datumszeichenfolgen aus anderen Quellen, die einen Zeitzonennamen oder einen UTC-Versatz enthalten können, müssen Sie möglicherweise auch einige Teile der Datumszeichenfolge vor der Konvertierung entfernen. Einen vollständigen Parser für ISO-Datumsfolgen (und viele andere Datumsformate) finden Sie im Fremdmodul dateutil .

Die Dekodierung funktioniert nur, wenn die ISO-Datumsfolgen Werte in einer JavaScript-Literal-Objektnotation oder in verschachtelten Strukturen innerhalb eines Objekts sind. ISO-Datumszeichen Zeichenfolgen, die Elemente eines Arrays der obersten Ebene sind, werden not decodiert.

D.h. das funktioniert:

date = 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)}]

Das funktioniert aber 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:
                # The %f format code is only supported in Python >= 2.6.
                # For Python <= 2.5 strip off microseconds
                # 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 __== '__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))
30
Chris Arndt

Wenn Sie sicher sind, dass nur Javascript die JSON verwendet, ziehe ich es vor, Javascript Date-Objekte direkt zu übergeben.

Die ctime()-Methode für datetime-Objekte gibt eine Zeichenfolge zurück, die das Javascript-Date-Objekt verstehen kann.

import datetime
date = datetime.datetime.today()
json = '{"mydate":new Date("%s")}' % date.ctime()

Javascript verwendet dies gerne als Objektliteral, und Sie haben Ihr Date-Objekt richtig eingebaut.

23
Triptych

Spät im Spiel ... :) 

Eine sehr einfache Lösung besteht darin, den Standard des Json-Moduls zu patchen. Zum Beispiel:

import json
import datetime

json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)

Jetzt können Sie json.dumps () verwenden, als hätte es datetime immer unterstützt ...

json.dumps({'created':datetime.datetime.now()})

Dies ist sinnvoll, wenn Sie diese Erweiterung des json-Moduls immer benötigen, um die Art und Weise zu ändern, wie Sie oder andere Benutzer die Json-Serialisierung verwenden (entweder in vorhandenem Code oder nicht). 

Beachten Sie, dass manche das Patchen von Bibliotheken auf diese Weise als schlechte Praxis betrachten. __ Besondere Vorsicht ist geboten, wenn Sie Ihre Anwendung auf mehrere Arten erweitern möchten - in einem solchen Fall empfehle ich die Lösung von Ramen oder JT und wählen Sie jeweils die richtige Json-Erweiterung. 

11
davidhadas

Nicht viel zur Community-Wiki-Antwort hinzuzufügen, außer für timestamp !

Javascript verwendet das folgende Format:

new Date().toJSON() // "2016-01-08T19:00:00.123Z"

Python-Seite (für den json.dumps-Handler siehe die anderen Antworten):

>>> from datetime import datetime
>>> d = datetime.strptime('2016-01-08T19:00:00.123Z', '%Y-%m-%dT%H:%M:%S.%fZ')
>>> d
datetime.datetime(2016, 1, 8, 19, 0, 0, 123000)
>>> d.isoformat() + 'Z'
'2016-01-08T19:00:00.123000Z'

Wenn Sie dieses Z nicht angeben, können Frontend-Frameworks wie "angle" das Datum nicht in der lokalen Zeitzone des Browsers anzeigen:

> $filter('date')('2016-01-08T19:00:00.123000Z', 'yyyy-MM-dd HH:mm:ss')
"2016-01-08 20:00:00"
> $filter('date')('2016-01-08T19:00:00.123000', 'yyyy-MM-dd HH:mm:ss')
"2016-01-08 19:00:00"
6
user1338062

Auf der Python-Seite:

import time, json
from datetime import datetime as dt
your_date = dt.now()
data = json.dumps(time.mktime(your_date.timetuple())*1000)
return data # data send to javascript

Auf der Javascript Seite:

var your_date = new Date(data)

wo Daten aus Python resultieren

4
Sank

Mein Rat ist, eine Bibliothek zu benutzen. Es gibt mehrere auf pypi.org.

Ich benutze dieses, es funktioniert gut: https://pypi.python.org/pypi/asjson

4
guettli

Anscheinend Das "richtige" JSON (gut JavaScript) Datumsformat ist 2012-04-23T18: 25: 43.511Z - UTC und "Z". Andernfalls verwendet JavaScript die lokale Zeitzone des Webbrowsers, wenn Sie ein Date () - Objekt aus der Zeichenfolge erstellen.

Für eine "naive" Zeit (was Python eine Zeit ohne Zeitzone nennt und dies ist lokal), wird die untere lokale Zeitzone erzwingen , so dass sie korrekt in UTC konvertiert werden kann:

def default(obj):
    if hasattr(obj, "json") and callable(getattr(obj, "json")):
        return obj.json()
    if hasattr(obj, "isoformat") and callable(getattr(obj, "isoformat")):
        # date/time objects
        if not obj.utcoffset():
            # add local timezone to "naive" local time
            # https://stackoverflow.com/questions/2720319/python-figure-out-local-timezone
            tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
            obj = obj.replace(tzinfo=tzinfo)
        # convert to UTC
        obj = obj.astimezone(timezone.utc)
        # strip the UTC offset
        obj = obj.replace(tzinfo=None)
        return obj.isoformat() + "Z"
    Elif hasattr(obj, "__str__") and callable(getattr(obj, "__str__")):
        return str(obj)
    else:
        print("obj:", obj)
        raise TypeError(obj)

def dump(j, io):
    json.dump(j, io, indent=2, default=default)

warum ist das so schwer?.

0
cagney