簡體   English   中英

JSON 日期時間介於 Python 和 JavaScript 之間

[英]JSON datetime between Python and JavaScript

我想使用 JSON 從 Python 發送序列化形式的 datetime.datetime object,並使用JSON在 JavaScript 中反序列化。執行此操作的最佳方法是什么?

您可以將 '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))

更新:添加了類型和值的輸出。
更新:也處理日期

對於跨語言項目,我發現包含RfC 3339日期的字符串是最好的方法。 RFC 3339 日期如下所示:

  1985-04-12T23:20:50.52Z

我認為大部分格式是顯而易見的。 唯一有點不尋常的可能是末尾的“Z”。 它代表 GMT/UTC。 您還可以為 CEST(夏季為德國)添加時區偏移量,例如 +02:00。 我個人更喜歡將所有內容保留在 UTC 中,直到顯示為止。

為了顯示、比較和存儲,您可以將其保留為所有語言的字符串格式。 如果您需要日期進行計算,可以輕松地將其轉換回大多數語言中的本機日期對象。

所以像這樣生成JSON:

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

不幸的是,Javascript 的 Date 構造函數不接受 RfC 3339 字符串,但 Internet 上有許多解析器可用。

huTools.hujson嘗試處理您在 Python 代碼中可能遇到的最常見的編碼問題,包括日期/日期時間對象,同時正確處理時區。

我已經解決了。

假設您有一個使用 datetime.now() 創建的 Python 日期時間對象d 它的價值是:

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

您可以將其序列化為 JSON 作為 ISO 8601 日期時間字符串:

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

示例日期時間對象將被序列化為:

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

這是使用標准庫json模塊遞歸編碼和解碼 datetime.datetime 和 datetime.date 對象的相當完整的解決方案。 這需要 Python >= 2.6,因為從那時起才支持 datetime.datetime.strptime() 格式字符串中的%f格式代碼。 對於 Python 2.5 支持,在嘗試轉換 ISO 日期字符串之前,先刪除%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))

如果您確定只有 Javascript 將使用 JSON,我更喜歡直接傳遞 Javascript Date對象。

datetime對象上的ctime()方法將返回一個 Javascript Date 對象可以理解的字符串。

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

Javascript 很樂意將它用作對象文字,並且您已經內置了 Date 對象。

游戲后期... :)

一個非常簡單的解決方案是修補 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 擴展名。

除了時間戳之外,沒有太多可添加到社區 wiki 答案中!

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"

在蟒蛇方面:

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的結果

我的建議是使用圖書館。 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
            # https://stackoverflow.com/questions/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 格式缺少一些信息,那么您可以先使用 Date.parse 轉換為 Unix 數字。 此外,Date.parse 也適用於 React,而 new Date 可能會觸發異常。

如果您有一個沒有毫秒的 DateTime 對象,則需要考慮以下事項。

  var unixDate = Date.parse('2016-01-08T19:00:00') 
  var desiredDate = new Date(unixDate).toLocaleDateString();

示例日期同樣可以是 API 調用后 result.data 對象中的一個變量。

有關以所需格式顯示日期的選項(例如顯示長工作日),請查看MDN 文檔

只需這樣做:

r = json.dumps(your_json_data, default=str)
your_json_data = json.loads(r)

在此處輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM