JSON datetime между Python и JavaScript

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

Ответ 1

Вы можете добавить параметр "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))

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

Ответ 2

Для проектов с перекрестными языками я узнал, что строки, содержащие 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:%S'))

К сожалению, конструктор Date Javascripts Date не принимает строки RfC 3339, но есть много парсеров, доступных в Интернете.

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

Ответ 3

Я проработал.

Скажем, у вас есть объект 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)

Ответ 4

Используя 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"]'

Ответ 5

Здесь достаточно полное решение для рекурсивного кодирования и декодирования объектов 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))

Ответ 6

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

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

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

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

Ответ 7

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

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

import json
import datetime

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

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

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

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

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

Ответ 8

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

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"

Ответ 9

На стороне 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

Со стороны javascript:

var your_date = new Date(data)

где данные получены из python

Ответ 10

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

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