JSON datetime entre Python et JavaScript

Je souhaite envoyer un objet datetime.datetime sous forme sérialisée à partir de Python en utilisant JSON et de-sérialiser en JavaScript via JSON. Quelle est la meilleure façon de procéder?

Vous pouvez ajouter le paramètre 'par défaut' à json.dumps pour gérer ceci:

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

Ce format est ISO 8601 .

Une fonction de gestion par défaut plus complète:

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

Mise à jour: sortie ajoutée du type ainsi que de la valeur.
Mise à jour: également gérer la date

Pour les projets de langue croisée, j'ai découvert que les chaînes contenant les dates RfC 3339 sont le meilleur moyen d'y aller. Une date RfC 3339 ressemble à ceci:

  1985-04-12T23:20:50.52Z 

Je pense que la plupart du format est évident. La seule chose quelque peu inhabituelle peut être le "Z" à la fin. Cela signifie GMT / UTC. Vous pouvez également ajouter un décalage horaire comme +02: 00 pour CEST (Allemagne en été). Personnellement, je préfère garder tout en UTC jusqu'à ce qu'il s'affiche.

Pour l'affichage, les comparaisons et le stockage, vous pouvez le laisser en format de chaîne dans toutes les langues. Si vous avez besoin de la date des calculs, il est facile de le convertir à un objet de date native dans la plupart des langues.

Donc générez le JSON comme ceci:

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

Malheureusement, Javascripts Date constructor n'accepte pas les chaînes RfC 3339, mais il existe de nombreux analyseurs disponibles sur Internet.

HuTools.hujson essaie de traiter les problèmes de codage les plus courants que vous pouvez rencontrer dans le code Python, y compris les objets date / datetime tout en traitant les fuseaux horaires correctement.

Je l'ai bien compris.

Disons que vous avez un objet Python datetime, d , créé avec datetime.now (). Sa valeur est la suivante:

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

Vous pouvez le sérialiser sur JSON en tant que chaîne datetime ISO 8601:

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

L'exemple d'objet datetime serait sérialisé comme suit:

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

Cette valeur, une fois reçue dans la couche Javascript, peut construire un objet Date:

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

À partir de Javascript 1.8.5, les objets Date ont une méthode toJSON, qui renvoie une chaîne dans un format standard. Pour sérialiser l'objet Javascript ci-dessus vers JSON, la commande sera donc:

 d.toJSON() 

Ce qui vous donnerait:

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

Cette chaîne, une fois reçue en Python, pourrait être désérialisée à un objet datetime:

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

Cela se traduit par l'objet datetime suivant, qui est le même que vous avez commencé et, par conséquent, corrigez:

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

À l'aide de json , vous pouvez sous-classer JSONEncoder et remplacer la méthode par défaut () pour fournir vos propres sérialisateurs personnalisés:

 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) 

Ensuite, vous pouvez l'appeler comme ceci:

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

Voici une solution assez complète pour l'encodage et le décodage récurrents des objets datetime.datetime et datetime.date à l'aide du module json bibliothèque standard. Cela nécessite Python> = 2.6 puisque le code de format %f dans la chaîne de format datetime.datetime.strptime () n'est pris en charge que depuis lors. Pour le support Python 2.5, supprimez le %f et dépassez les microsecondes de la chaîne de la date ISO avant d'essayer de la convertir, mais vous perdrez des microsecondes de précision, bien sûr. Pour l'interopérabilité avec les chaînes de dates ISO d'autres sources, qui peuvent inclure un nom de zone horaire ou un décalage UTC, vous devrez peut-être supprimer certaines parties de la chaîne de date avant la conversion. Pour un analyseur complet pour les chaînes de date ISO (et plusieurs autres formats de date), consultez le module dateutil tiers.

Le décodage ne fonctionne que lorsque les chaînes de date ISO sont des valeurs dans une notation d'objet littéral JavaScript ou dans des structures imbriquées dans un objet. Les chaînes de date ISO, qui sont des éléments d'un tableau de niveau supérieur, ne seront pas décodées.

C'est-à-dire que cela fonctionne:

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

Et ceci aussi:

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

Mais cela ne fonctionne pas comme prévu:

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

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

Si vous êtes certain que seul Javascript consommera le JSON, je préfère passer les objets JavaScript Date directement.

La méthode ctime() sur les objets datetime renverra une chaîne que l'objet Date Javascript peut comprendre.

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

Javascript utilisera cet objet comme un littéral d'objet, et vous avez intégré votre objet Date.

En retard dans le jeu … 🙂

Une solution très simple consiste à réparer le module json par défaut. Par exemple:

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

Maintenant, vous pouvez utiliser json.dumps () comme si elle avait toujours pris en charge les dates …

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

Cela est logique si vous avez besoin de cette extension pour le module json pour toujours démarrer et souhaitez ne pas changer la façon dont vous ou d'autres utilisent la sérialisation json (soit dans un code existant, soit non).

Notez que certains peuvent envisager de réparer les bibliothèques de cette façon comme une mauvaise pratique. Des précautions particulières doivent être prises au cas où vous souhaiteriez étendre votre demande de plusieurs façons. Dans ce cas, je suggère d'utiliser la solution par ramen ou JT et de choisir l'extension json correcte dans chaque cas.

Pas grand chose à ajouter à la réponse wiki de la communauté, à l'exception de l' horodatage !

Javascript utilise le format suivant:

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

Côté Python (pour le gestionnaire json.dumps , voir les autres réponses):

 >>> 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 vous quittez cette Z, les frameworks avancés comme angulaires ne peuvent pas afficher la date dans la fuseau horaire navigateur-local:

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

Sur le côté 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 

Côté javascript:

 var your_date = new Date(data) 

Où les données proviennent de Python

Mon conseil consiste à utiliser une bibliothèque. Il existe plusieurs disponibles sur pypi.org.

J'utilise celui-ci, ça marche bien: https://pypi.python.org/pypi/asjson