JSON datetime между Python и JavaScript - PullRequest
383 голосов
/ 18 января 2009

Я хочу отправить объект datetime.datetime в сериализованной форме из Python с использованием JSON и десериализовать в JavaScript с использованием JSON. Каков наилучший способ сделать это?

Ответы [ 11 ]

366 голосов
/ 21 апреля 2010

Вы можете добавить параметр 'default' в json.dumps для обработки этого:

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

Это формат ISO 8601 .

Более полная функция обработчика по умолчанию:

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

Обновление: добавлен вывод типа и значения.
Обновление: также обрабатывать дату

76 голосов
/ 19 января 2009

Для кросс-языковых проектов я обнаружил, что строки, содержащие RfC 3339 , являются лучшим способом. Дата RfC 3339 выглядит следующим образом:

  1985-04-12T23:20:50.52Z

Я думаю, что большая часть формата очевидна. Единственной необычной вещью может быть буква «Z» в конце. Это означает GMT / UTC. Вы также можете добавить смещение часового пояса, например, +02: 00 для CEST (Германия летом). Лично я предпочитаю хранить все в UTC, пока оно не отобразится.

Для отображения, сравнения и хранения вы можете оставить его в строковом формате на всех языках. Если вам нужна дата для вычислений, легко преобразовать ее обратно в собственный объект даты на большинстве языков.

Итак, сгенерируйте JSON так:

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

К сожалению, конструктор Date в Javascript не принимает строки RfC 3339, но в Интернете доступно множество анализаторов .

huTools.hujson пытается решить наиболее распространенные проблемы кодирования, с которыми вы можете столкнуться в коде Python, включая объекты даты / даты и времени, при правильной обработке часовых поясов.

63 голосов
/ 26 мая 2011

Я разобрался.

Допустим, у вас есть объект datetime Python, d , созданный с помощью datetime.now (). Его значение:

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

Вы можете сериализовать его в JSON в виде строки даты и времени ISO 8601:

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

Пример объекта datetime будет сериализован как:

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

Это значение, полученное в слое Javascript, может создать объект Date:

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

Начиная с Javascript 1.8.5, объекты Date имеют метод toJSON, который возвращает строку в стандартном формате. Поэтому, чтобы сериализовать вышеуказанный объект Javascript обратно в JSON, команда будет иметь следующий вид:

d.toJSON()

Что даст вам:

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

Эта строка, полученная в Python, может быть десериализована обратно в объект datetime:

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

Это приводит к следующему объекту datetime, который тот же самый, с которого вы начали, и поэтому правильный:

datetime.datetime(2011, 5, 25, 20, 34, 5, 787000)
49 голосов
/ 16 июня 2010

Используя json, вы можете создать подкласс JSONEncoder и переопределить метод default () для предоставления собственных настраиваемых сериализаторов:

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)

Затем вы можете назвать это так:

>>> DateTimeJSONEncoder().encode([datetime.datetime.now()])
'["2010-06-15T14:42:28"]'
30 голосов
/ 13 июля 2010

Вот довольно полное решение для рекурсивного кодирования и декодирования объектов datetime.datetime и datetime.date с использованием стандартного модуля json библиотеки. Для этого требуется Python> = 2.6, поскольку код формата %f в строке формата datetime.datetime.strptime () поддерживается только с тех пор. Для поддержки Python 2.5 удалите %f и удалите микросекунды из строки даты ISO, прежде чем пытаться преобразовать ее, но, конечно, вы потеряете точность в микросекундах. Для обеспечения совместимости со строками даты ISO из других источников, которые могут включать имя часового пояса или смещение UTC, вам также может потребоваться удалить некоторые части строки даты перед преобразованием. Полный анализатор для строк даты ISO (и многих других форматов даты) см. В стороннем модуле dateutil .

Декодирование работает только тогда, когда строки даты ISO являются значениями в JavaScript буквенное обозначение объекта или вложенные структуры внутри объекта. Дата ISO строки, которые являются элементами массива верхнего уровня, будут не декодированы.

т.е. это работает:

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

И это тоже:

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

Но это не работает, как ожидалось:

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

Вот код:

__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))
23 голосов
/ 18 января 2009

Если вы уверены, что только Javascript будет использовать JSON, я предпочитаю передавать объекты Javascript Date напрямую.

Метод ctime() для объектов datetime возвращает строку, понятную объекту Javascript Date.

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

Javascript с радостью использует его в качестве литерала объекта, и у вас уже есть встроенный объект Date.

11 голосов
/ 26 августа 2015

Поздно в игре ... :)

Очень простое решение - установить модуль json по умолчанию. Например:

import json
import datetime

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

Теперь вы можете использовать json.dumps () , как если бы он всегда поддерживал datetime ...

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

Это имеет смысл, если вам требуется, чтобы это расширение модуля json всегда включалось и вы не хотели менять способ, которым вы или другие используете сериализацию json (в существующем коде или нет).

Обратите внимание, что некоторые могут расценивать исправление библиотек таким образом как плохую практику. Особая осторожность должна быть предпринята, если вы захотите расширить свое приложение более чем одним способом - в таком случае я предлагаю использовать решение с помощью ramen или JT и выбрать правильное расширение json в каждом случае.

6 голосов
/ 08 января 2016

Не так много, чтобы добавить в вики ответ сообщества, кроме отметка времени !

Javascript использует следующий формат:

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

Сторона Python (для обработчика json.dumps см. Другие ответы):

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

Если вы не укажете этот Z, рамки внешнего интерфейса, такие как angular, не смогут отображать дату в часовом поясе локального браузера:

> $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"
4 голосов
/ 20 апреля 2015

Мой совет - использовать библиотеку. Есть несколько доступных на pypi.org.

Я использую этот, он работает хорошо: https://pypi.python.org/pypi/asjson

4 голосов
/ 16 декабря 2013

На стороне питона:

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

На стороне JavaScript:

var your_date = new Date(data)

где данные являются результатом Python

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...