Я хочу надіслати об’єкт datetime.datetime в серіалізованому вигляді з Python за допомогою JSON та де-серіалізувати в JavaScript за допомогою JSON. Який найкращий спосіб зробити це?
Я хочу надіслати об’єкт datetime.datetime в серіалізованому вигляді з Python за допомогою JSON та де-серіалізувати в JavaScript за допомогою JSON. Який найкращий спосіб зробити це?
Відповіді:
Ви можете додати параметр 'за замовчуванням' до 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))
Оновлення: Додано вихід типу, а також значення.
Оновлення: також обробляти дату
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
Для мовних проектів я з'ясував, що рядки, що містять дати 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'))
На жаль, конструктор дати Javascript не приймає рядки RfC 3339, але в Інтернеті є багато парсерів .
huTools.hujson намагається вирішити найпоширеніші проблеми кодування, які ви можете зіткнутися в коді Python, включаючи об'єкти дати / дати, при правильній обробці часових поясів.
datetimetimetime.isoformat (), так і користувачем simplejson, який скидає datetimeоб'єкти як isoformatрядки за замовчуванням. Немає необхідності в ручному strftimeзлому.
datetimeоб'єктів у isoformatрядок. Для мене simplejson.dumps(datetime.now())врожайністьTypeError: datetime.datetime(...) is not JSON serializable
json.dumps(datetime.datetime.now().isoformat())там, де відбувається магія.
Я це розробив.
Скажімо, у вас є об'єкт Python datetime, 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.strptime('2011-05-25T20:34:05.787Z', '%Y-%m-%dT%H:%M:%S.%fZ')
Це призводить до появи наступного об'єкта datetime, який є тим самим, з якого ви почали, і тому правильний:
datetime.datetime(2011, 5, 25, 20, 34, 5, 787000)
Використовуючи 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"]'
obj.isoformat(). Ви також можете скористатись звичайнішим dumps()викликом, який займає інші корисні аргументи (наприклад indent): simplejson.dumps (myobj, cls = JSONEncoder, ...)
Ось досить повне рішення для рекурсивного кодування та декодування об’єктів datetime.datetime та datetime.date за допомогою стандартного jsonмодуля бібліотеки . Для цього потрібен Python> = 2.6, оскільки %fкод формату в рядку формату datetime.datetime.strptime () підтримується лише з того часу. Для підтримки Python 2.5 опустіть %fі зніміть мікросекунди з рядка дати ISO перед тим, як спробувати перетворити її, але, звичайно, ви втратите точність мікросекунд. Для сумісності з рядками дати ISO з інших джерел, які можуть включати назву часового поясу або зміщення UTC, вам також може знадобитися зняти деякі частини рядка дати перед перетворенням. Повний аналіз для рядків дат ISO (та багатьох інших форматів дати) див. У сторонньому модулі datautil .
Розшифровка працює лише тоді, коли рядки дати 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))
datetime.datetime.utcnow().isoformat()[:-3]+"Z"вона буде точно схожа на те, що виробляє
Якщо ви впевнені, що JSON споживає лише Javascript, я вважаю за краще передавати Dateоб’єкти Javascript безпосередньо.
ctime()Метод на datetimeоб'єктах повертає рядок , що дата об'єкт Javascript може зрозуміти.
import datetime
date = datetime.datetime.today()
json = '{"mydate":new Date("%s")}' % date.ctime()
Javascript з радістю буде використовувати це як буквальний об'єкт, і ви отримали свій об'єкт Date вбудований прямо.
.ctime()ДУЖЕ поганий спосіб передавати інформацію про час, .isoformat()набагато краще. Що .ctime()полягає в тому, щоб викинути часовий пояс і літній час, як їх немає. Ця функція повинна бути вбита.
Пізно в грі ... :)
Дуже просте рішення - виправити модуль 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 у кожному конкретному випадку.
None. Ви можете замість цього викинути виняток.
Не багато для додання відповіді у вікі спільноти, крім часової позначки !
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, рамки прямої лінії, такі як кутова, не можуть відображати дату в локальному часовому поясі браузера:
> $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"
Моя порада - використовувати бібліотеку. На pypi.org доступні декілька.
Я використовую цей, він працює добре: https://pypi.python.org/pypi/asjson
Мабуть, "правильний" формат дати JSON (добре JavaScript) - 2012-04-23T18: 25: 43.511Z - UTC та "Z". Без цього JavaScript буде використовувати локальний часовий пояс веб-браузера під час створення об’єкта Date () з рядка.
У "наївний" час (те, що Python називає часом без часового поясу, і це передбачає місцеве), нижче буде застосовано локальний часовий пояс, щоб потім його можна було правильно перетворити на 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)
чому це так важко.
Для перетворення дати Python в JavaScript об'єкт дати повинен бути у визначеному форматі ISO, тобто у форматі ISO або UNIX. Якщо у форматі ISO бракує деякої інформації, то ви можете спочатку перетворити на номер Unix за допомогою Date.parse. Більше того, Date.parse працює з React, а нова дата може викликати виняток.
Якщо у вас є об'єкт DateTime без мілісекунд, необхідно врахувати наступне. :
var unixDate = Date.parse('2016-01-08T19:00:00')
var desiredDate = new Date(unixDate).toLocaleDateString();
Прикладна дата може бути однаковою мірою змінною в об'єкті result.data після виклику API.
Щоб отримати параметри відображення дати у бажаному форматі (наприклад, для відображення довгих буднів), перегляньте документ MDN .