Ich möchte ein datetime.datetime-Objekt in serialisierter Form von Python mit JSON senden und in JavaScript mit JSON de-serialisieren. Was ist der beste Weg, dies zu tun?
Ich möchte ein datetime.datetime-Objekt in serialisierter Form von Python mit JSON senden und in JavaScript mit JSON de-serialisieren. Was ist der beste Weg, dies zu tun?
Antworten:
Sie können json.dumps den Parameter 'default' hinzufügen, um dies zu handhaben:
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 ISO 8601 Format.
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: Behandle auch das Datum
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
Bei sprachübergreifenden Projekten habe ich herausgefunden, dass Zeichenfolgen mit RfC 3339- Daten der beste Weg sind. Ein RfC 3339-Datum sieht folgendermaßen aus:
1985-04-12T23:20:50.52Z
Ich denke, der größte Teil des Formats ist offensichtlich. Das einzig ungewöhnliche ist vielleicht das "Z" am Ende. Es steht für GMT / UTC. Sie können auch einen Zeitzonenversatz wie +02: 00 für MESZ (Deutschland im Sommer) hinzufügen. Ich persönlich bevorzuge es, alles in UTC zu behalten, bis es angezeigt wird.
Zum Anzeigen, Vergleichen und Speichern können Sie es in allen Sprachen im Zeichenfolgenformat belassen. Wenn Sie das Datum für Berechnungen benötigen, können Sie es in den meisten Sprachen einfach wieder in ein natives Datumsobjekt konvertieren.
Generieren Sie den JSON also wie folgt:
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 im Python-Code zu behandeln, einschließlich Datums- / Datums- / Uhrzeitobjekten, während Zeitzonen korrekt behandelt werden.
datetime
: datetime.isoformat () als auch von simplejson
, wodurch datetime
Objekte isoformat
standardmäßig als Zeichenfolgen ausgegeben werden . Kein manuelles strftime
Hacken erforderlich .
datetime
Objekten in die isoformat
Zeichenfolge. Für mich simplejson.dumps(datetime.now())
ergibtTypeError: datetime.datetime(...) is not JSON serializable
json.dumps(datetime.datetime.now().isoformat())
Hier geschieht die Magie.
Ich habe es herausgefunden.
Angenommen, Sie haben ein Python-Datum / Uhrzeit-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-Datums- / Uhrzeitzeichenfolge in JSON serialisieren:
import json
json.dumps(d.isoformat())
Das Beispiel-Datum / Uhrzeit-Objekt würde wie folgt serialisiert:
'"2011-05-25T13:34:05.787000"'
Dieser Wert kann nach dem Empfang in der Javascript-Ebene ein Datumsobjekt erstellen:
var d = new Date("2011-05-25T13:34:05.787000");
Ab Javascript 1.8.5 verfügen Date-Objekte über eine toJSON-Methode, die eine Zeichenfolge in einem Standardformat zurückgibt. Um das obige Javascript-Objekt wieder in JSON zu serialisieren, lautet der Befehl daher:
d.toJSON()
Welches würde Ihnen geben:
'2011-05-25T20:34:05.787Z'
Diese in Python empfangene Zeichenfolge kann wieder in ein Datum / Uhrzeit-Objekt deserialisiert werden:
datetime.strptime('2011-05-25T20:34:05.787Z', '%Y-%m-%dT%H:%M:%S.%fZ')
Dies führt zu dem folgenden Datum / Uhrzeit-Objekt, mit dem Sie begonnen haben und das daher korrekt ist:
datetime.datetime(2011, 5, 25, 20, 34, 5, 787000)
Mit json
können Sie JSONEncoder in Unterklassen unterteilen 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 können Sie es so nennen:
>>> DateTimeJSONEncoder().encode([datetime.datetime.now()])
'["2010-06-15T14:42:28"]'
obj.isoformat()
. Sie können auch den allgemeineren dumps()
Aufruf verwenden, der andere nützliche Argumente (wie indent
) benötigt: simplejson.dumps (myobj, cls = JSONEncoder, ...)
Hier ist eine ziemlich vollständige Lösung zum rekursiven Codieren und Decodieren von datetime.datetime- und datetime.date-Objekten unter Verwendung des Standardbibliotheksmoduls json
. Dies erfordert Python> = 2.6, da der %f
Formatcode in der Formatzeichenfolge datetime.datetime.strptime () nur seitdem unterstützt wird. Wenn Sie Python 2.5 unterstützen möchten, löschen Sie die %f
und entfernen Sie die Mikrosekunden von der ISO-Datumszeichenfolge, bevor Sie versuchen, sie zu konvertieren. Natürlich verlieren Sie jedoch die Genauigkeit von Mikrosekunden. Für die Interoperabilität mit ISO-Datumszeichenfolgen aus anderen Quellen, die einen Zeitzonennamen oder einen UTC-Versatz enthalten können, müssen Sie möglicherweise vor der Konvertierung auch einige Teile der Datumszeichenfolge entfernen. Einen vollständigen Parser für ISO-Datumszeichenfolgen (und viele andere Datumsformate) finden Sie im Dateutil- Modul eines Drittanbieters .
Die Dekodierung funktioniert nur, wenn die ISO-Datumszeichenfolgen Werte in einer JavaScript-Literalobjektnotation oder in verschachtelten Strukturen innerhalb eines Objekts sind. ISO-Datumszeichenfolgen, die Elemente eines Arrays der obersten Ebene sind, werden nicht dekodiert.
Dh 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)}]
Dies funktioniert jedoch 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 __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))
datetime.datetime.utcnow().isoformat()[:-3]+"Z"
es ist, entspricht es genau dem, was JSON.stringify () in Javascript erzeugt
Wenn Sie sicher sind, dass nur Javascript den JSON verbraucht, übergebe ich Javascript- Date
Objekte lieber direkt.
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 direkt eingebaut.
.ctime()
ist ein sehr schlechter Weg, um Zeitinformationen weiterzugeben, .isoformat()
ist viel besser. Was .ctime()
wegwirft, ist Zeitzone und Sommerzeit wegzuwerfen, als ob sie nicht existieren. Diese Funktion sollte beendet werden.
Spät im Spiel ... :)
Eine sehr einfache Lösung besteht darin, die Standardeinstellung 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 () so 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 benötigen, um immer aktiv zu werden, und die Art und Weise, wie Sie oder andere die JSON-Serialisierung verwenden (entweder im vorhandenen Code oder nicht), nicht ändern möchten.
Beachten Sie, dass einige 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 diesem Fall empfehle ich, die Lösung von Ramen oder JT zu verwenden und jeweils die richtige JSON-Erweiterung auszuwählen.
None
. Möglicherweise möchten Sie stattdessen eine Ausnahme auslösen.
Der Community-Wiki-Antwort gibt es nicht viel hinzuzufügen, außer dem Zeitstempel !
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 weglassen, können Frontend-Frameworks wie Angular das Datum nicht in der browser-lokalen Zeitzone 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"
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)
Dabei stammen die Daten aus Python
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
Anscheinend ist das "richtige" JSON-Datumsformat (auch JavaScript) 2012-04-23T18: 25: 43.511Z - UTC und "Z". Ohne dieses JavaScript wird beim Erstellen eines Date () -Objekts aus der Zeichenfolge die lokale Zeitzone des Webbrowsers verwendet.
Für eine "naive" Zeit (was Python eine Zeit ohne Zeitzone nennt und dies als lokal voraussetzt) erzwingt das Folgende eine lokale Zeitzone, damit sie dann 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
# /programming/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?
Für die Datumskonvertierung von Python in JavaScript muss das Datumsobjekt ein bestimmtes ISO-Format haben, dh ein ISO-Format oder eine UNIX-Nummer. Wenn dem ISO-Format einige Informationen fehlen, können Sie zuerst mit Date.parse in die Unix-Nummer konvertieren. Darüber hinaus funktioniert Date.parse auch mit React, während ein neues Datum möglicherweise eine Ausnahme auslöst.
Wenn Sie ein DateTime-Objekt ohne Millisekunden haben, muss Folgendes berücksichtigt werden. ::
var unixDate = Date.parse('2016-01-08T19:00:00')
var desiredDate = new Date(unixDate).toLocaleDateString();
Das Beispieldatum kann nach einem API-Aufruf auch eine Variable im Objekt result.data sein.
Optionen zum Anzeigen des Datums im gewünschten Format (z. B. zum Anzeigen langer Wochentage) finden Sie im MDN-Dokument .