JSON datetime entre Python y JavaScript

Quiero enviar un objeto datetime.datetime en forma serializada desde Python usando JSON y des-serializar en JavaScript usando JSON. ¿Cuál es la mejor manera de hacer esto?

Puede agregar el parámetro ‘predeterminado’ a json.dumps para manejar esto:

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"' 

Que es el formato ISO 8601 .

Una función de manejador por defecto más completa:

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

Actualización: Se agregó salida de tipo así como valor.
Actualización: también manejar fecha

Para los proyectos en varios idiomas, descubrí que las cadenas que contienen fechas RfC 3339 son la mejor manera de hacerlo. Una fecha de RfC 3339 se ve así:

  1985-04-12T23:20:50.52Z 

Creo que la mayor parte del formato es obvio. La única cosa un tanto inusual puede ser la “Z” al final. Es sinónimo de GMT / UTC. También puede agregar un desplazamiento de zona horaria como +02: 00 para CEST (Alemania en verano). Personalmente prefiero mantener todo en UTC hasta que se muestre.

Para la visualización, comparaciones y almacenamiento, puede dejarlo en formato de cadena en todos los idiomas. Si necesita la fecha para los cálculos, puede volver a convertirla en un objeto de fecha nativo en la mayoría de los idiomas.

Así que genera el JSON de esta manera:

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

Desafortunadamente, el constructor de fecha de Javascript no acepta cadenas RfC 3339 pero hay muchos analizadores disponibles en Internet.

huTools.hujson trata de manejar los problemas de encoding más comunes que puede encontrar en el código Python, incluidos los objetos de fecha / fecha y hora, mientras se manejan las zonas horarias correctamente.

Lo he resuelto.

Digamos que tienes un objeto datetime de Python, d , creado con datetime.now (). Su valor es:

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

Puede serializarlo a JSON como una cadena datetime ISO 8601:

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

El objeto datetime de ejemplo se serializaría como:

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

Este valor, una vez recibido en la capa de Javascript, puede construir un objeto Date:

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

A partir de Javascript 1.8.5, los objetos Date tienen un método toJSON, que devuelve una cadena en un formato estándar. Para serializar el objeto Javascript anterior a JSON, por lo tanto, el comando sería:

 d.toJSON() 

Lo que te daría:

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

Esta cadena, una vez recibida en Python, podría deserializarse de nuevo a un objeto de fecha y hora:

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

Esto da como resultado el siguiente objeto de fecha y hora, que es el mismo con el que comenzó y, por lo tanto, correcto:

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

Usando json , puede crear una subclase de JSONEncoder y anular el método predeterminado () para proporcionar sus propios serializadores personalizados:

 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) 

Entonces, puedes llamarlo así:

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

Aquí hay una solución bastante completa para codificar y decodificar recursivamente objetos datetime.datetime y datetime.date usando el módulo json biblioteca estándar. Esto necesita Python> = 2.6 ya que el código de formato %f en la cadena de formato datetime.datetime.strptime () solo es compatible desde entonces. Para la compatibilidad con Python 2.5, elimine el %f y elimine los microsegundos de la cadena de fecha ISO antes de intentar convertirlo, pero perderá precisión de microsegundos, por supuesto. Para la interoperabilidad con cadenas de fecha ISO de otras fonts, que pueden incluir un nombre de zona horaria o un desplazamiento UTC, es posible que también deba eliminar algunas partes de la cadena de fecha antes de la conversión. Para obtener un analizador completo de cadenas de fecha ISO (y muchos otros formatos de fecha), consulte el módulo dateutil de terceros.

La deencoding solo funciona cuando las cadenas de fecha ISO son valores en una notación de objeto literal de JavaScript o en estructuras anidadas dentro de un objeto. Las cadenas de fecha ISO, que son elementos de una matriz de nivel superior, no se descodificarán.

Es decir, esto funciona:

 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'} 

Y esto también:

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

Pero esto no funciona como se esperaba:

 >>> 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'] 

Aquí está el código:

 __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)) 

Si está seguro de que solo Javascript consumirá el JSON, prefiero pasar los objetos de Date Javascript directamente.

El método ctime() en objetos de datetime devolverá una cadena que el objeto Fecha de Javascript puede comprender.

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

Con mucho gusto, Javascript lo usará como un objeto literal, y ya tienes tu objeto Date integrado.

Al final del juego … 🙂

Una solución muy simple es parchear el módulo json por defecto. Por ejemplo:

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

Ahora, puede usar json.dumps () como si siempre hubiera soportado datetime …

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

Esto tiene sentido si necesita que esta extensión del módulo json siempre se active y no desee cambiar la forma en que usted u otras personas utilizan la serialización json (ya sea en el código existente o no).

Tenga en cuenta que algunos pueden considerar parchear las bibliotecas de esa manera como una mala práctica. Se debe tener especial cuidado en caso de que desee extender su aplicación en más de una forma. En tal caso, le sugiero que utilice la solución de ramen o JT y elija la extensión json adecuada en cada caso.

No hay mucho que agregar a la comunidad wiki, a excepción de la marca de tiempo !

Javascript usa el siguiente formato:

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

Lado de Python (para el controlador json.dumps , consulte las otras respuestas):

 >>> 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' 

Si deja esa Z fuera, los marcos frontales como angular no pueden mostrar la fecha en la zona horaria local del navegador:

 > $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" 

En el lado de Python:

 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 

En el lado de javascript:

 var your_date = new Date(data) 

donde los datos son resultado de Python

Mi consejo es usar una biblioteca. Hay varios disponibles en pypi.org.

Yo uso este, funciona bien: https://pypi.python.org/pypi/asjson

Aparentemente, el formato de fecha JSON (bueno, JavaScript) es el 2012-04-23T18: 25: 43.511Z – UTC y “Z”. Sin este JavaScript, utilizará la zona horaria local del navegador web al crear un objeto Date () a partir de la cadena.

Para una hora “ingenua” (lo que Python llama una hora sin zona horaria y se supone que es local), lo que se indica a continuación forzará la zona horaria local para que luego pueda convertirse correctamente a UTC:

 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) 

Por qué es tan difícil.