Quiero enviar un objeto datetime.datetime en forma serializada desde Python usando JSON y deserializar en JavaScript usando JSON. ¿Cuál es la mejor manera de hacer esto?
Quiero enviar un objeto datetime.datetime en forma serializada desde Python usando JSON y deserializar en JavaScript usando JSON. ¿Cuál es la mejor manera de hacer esto?
Respuestas:
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"'
Cuál es el formato ISO 8601 .
Una función de controlador predeterminada 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: salida agregada de tipo y valor.
Actualización: también maneja la fecha
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
Para proyectos en varios idiomas, descubrí que las cadenas que contienen fechas RfC 3339 son la mejor opción . Una fecha RfC 3339 se ve así:
1985-04-12T23:20:50.52Z
Creo que la mayoría del formato es obvio. La única cosa algo 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, las comparaciones y el almacenamiento, puede dejarlo en formato de cadena en todos los idiomas. Si necesita la fecha para realizar los cálculos, vuelva a convertirla en un objeto de fecha nativo en la mayoría de los idiomas.
Así que genera el JSON así:
json.dump(datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))
Desafortunadamente, el constructor Fecha de Javascript no acepta cadenas RfC 3339 pero hay muchos analizadores disponibles en Internet.
huTools.hujson intenta manejar los problemas de codificación más comunes que puede encontrar en el código de Python, incluidos los objetos de fecha / fecha y hora, mientras maneja las zonas horarias correctamente.
datetime
: datetime.isoformat () como por simplejson
, que volcará los datetime
objetos como isoformat
cadenas de forma predeterminada. No hay necesidad de strftime
piratería manual .
datetime
objetos a la isoformat
cadena. Para mí, los simplejson.dumps(datetime.now())
rendimientosTypeError: datetime.datetime(...) is not JSON serializable
json.dumps(datetime.datetime.now().isoformat())
Es donde sucede la magia.
Lo he resuelto.
Digamos que tiene un objeto de fecha y hora 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 de fecha y hora ISO 8601:
import json
json.dumps(d.isoformat())
El objeto de fecha y hora de ejemplo se serializaría como:
'"2011-05-25T13:34:05.787000"'
Este valor, una vez recibido en la capa 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 de nuevo 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)
Utilizando json
, puede subclasificar 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"]'
obj.isoformat()
. También puede usar la dumps()
llamada más común , que toma otros indent
argumentos útiles (como ): simplejson.dumps (myobj, cls = JSONEncoder, ...)
Aquí hay una solución bastante completa para codificar y decodificar recursivamente los objetos datetime.datetime y datetime.date utilizando el json
módulo de biblioteca estándar . Esto necesita Python> = 2.6 ya que el %f
código de formato en la cadena de formato datetime.datetime.strptime () solo es compatible desde entonces. Para el soporte de Python 2.5, suelte %f
y quite los microsegundos de la cadena de fecha ISO antes de intentar convertirlo, pero perderá precisión de microsegundos, por supuesto. Para la interoperabilidad con las cadenas de fecha ISO de otras fuentes, que pueden incluir un nombre de zona horaria o desplazamiento UTC, es posible que también necesite quitar 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 dateutil de terceros módulo .
La decodificación 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 decodificará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))
datetime.datetime.utcnow().isoformat()[:-3]+"Z"
si fuera exactamente como lo que JSON.stringify () produce en javascript
Si está seguro de que solo Javascript consumirá el JSON, prefiero pasar Javascript Date
objetos directamente.
El ctime()
método en los datetime
objetos devolverá una cadena que el objeto Fecha de Javascript puede entender.
import datetime
date = datetime.datetime.today()
json = '{"mydate":new Date("%s")}' % date.ctime()
Javascript lo usará felizmente como un objeto literal, y tienes tu objeto Date integrado.
.ctime()
es una manera MUY mala de pasar información de tiempo, .isoformat()
es mucho mejor. Lo que .ctime()
hace es tirar la zona horaria y el horario de verano como si no existieran. Esa función debería ser eliminada.
Tarde en el juego ... :)
Una solución muy simple es parchear el módulo json predeterminado. 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 admitido datetime ...
json.dumps({'created':datetime.datetime.now()})
Esto tiene sentido si necesita que esta extensión del módulo json se active siempre y no desee cambiar la forma en que usted u otros usan la serialización json (ya sea en el código existente o no).
Tenga en cuenta que algunos pueden considerar parchear bibliotecas de esa manera como una mala práctica. Se debe tener especial cuidado en caso de que desee extender su aplicación de más de una manera; en tal caso, sugiero usar la solución por ramen o JT y elegir la extensión json adecuada en cada caso.
None
. Es posible que desee lanzar una excepción en su lugar.
¡No hay mucho que agregar a la respuesta wiki de la comunidad, 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 json.dumps
controlador, vea 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 frontend 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 "correcto" (bien JavaScript) es 2012-04-23T18: 25: 43.511Z - UTC y "Z". Sin esto, JavaScript utilizará la zona horaria local del navegador web al crear un objeto Date () a partir de la cadena.
Por un tiempo "ingenuo" (lo que Python llama un tiempo sin zona horaria y esto supone que es local) lo siguiente forzará la zona horaria local para que luego se pueda convertir 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
# /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)
Por qué es tan difícil.
Para la conversión de fecha de Python a JavaScript, el objeto de fecha debe estar en formato ISO específico, es decir, formato ISO o número UNIX. Si el formato ISO carece de información, puede convertirlo al número de Unix con Date.parse primero. Además, Date.parse también funciona con React, mientras que la nueva Fecha podría desencadenar una excepción.
En caso de que tenga un objeto DateTime sin milisegundos, debe considerarse lo siguiente. :
var unixDate = Date.parse('2016-01-08T19:00:00')
var desiredDate = new Date(unixDate).toLocaleDateString();
La fecha de ejemplo también podría ser una variable en el objeto result.data después de una llamada a la API.
Para ver las opciones para mostrar la fecha en el formato deseado (por ejemplo, para mostrar los días de semana largos), consulte el documento MDN .