Python 和 JavaScript 之间的 JSON 日期时间

Posted

技术标签:

【中文标题】Python 和 JavaScript 之间的 JSON 日期时间【英文标题】:JSON datetime between Python and JavaScript 【发布时间】:2010-10-02 02:51:06 【问题描述】:

我想使用 JSON 从 Python 以序列化形式发送 datetime.datetime 对象,并使用 JSON 在 javascript 中反序列化。最好的方法是什么?

【问题讨论】:

您更喜欢使用库还是自己编写代码? 【参考方案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))

更新:添加了类型和值的输出。 更新:也处理日期

【讨论】:

问题是,如果您在 list/dict 中有一些其他对象,此代码会将它们转换为 None。 json.dumps 也不知道如何转换它们,但异常被抑制了。可悲的是,单行 lambda 修复程序有其缺点。如果您希望在未知数上引发异常(这是一个好主意),请使用我在上面添加的函数。 完整的输出格式也应该有时区...并且 isoformat() 不提供此功能...因此您应该确保在返回之前将该信息附加到字符串上跨度> 这是最好的方法。为什么没有选择这个作为答案? 可以调整 lambda 以调用非日期时间类型的基本实现,因此如果需要可以引发 TypeError:dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)【参考方案2】:

对于跨语言项目,我发现包含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 字符串,但互联网上有很多 parsers 可用。

huTools.hujson 尝试处理您在 Python 代码中可能遇到的最常见编码问题,包括正确处理时区时的日期/日期时间对象。

【讨论】:

datetime: datetime.isoformat() 和simplejson 都原生支持这种日期格式化机制,默认情况下会将datetime 对象转储为isoformat 字符串。无需手动strftime hacking。 @jrk - 我没有得到从 datetime 对象到 isoformat 字符串的自动转换。对我来说,simplejson.dumps(datetime.now()) 产生 TypeError: datetime.datetime(...) is not JSON serializable json.dumps(datetime.datetime.now().isoformat()) 是魔法发生的地方。 simplejson 的美妙之处在于,如果我有一个复杂的数据结构,它会解析它并将其转换为 JSON。如果我必须为每个 datetime 对象执行 json.dumps(datetime.datetime.now().isoformat()),我就会失去它。有没有办法解决这个问题? superjoe30:请参阅***.com/questions/455580/… 了解如何做到这一点【参考方案3】:

我已经解决了。

假设您有一个使用 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(2011, 5, 25, 20, 34, 5, 787000)

【讨论】:

时区会搞砸这一点。让我们假设您在 python 中以 UTC 工作(只有一个疯狂的人会这样做) - python 的 JSON 输出没有时区,因此 JavaScript 会将其解释为本地时区。 JavaScript d.toJSON 将再次转换为 UTC。因此,对于英国浏览器上的示例日期(2011-04-25)(夏令时,UTC+1),python 输出 13:34 - JS 将其解释为本地时区或 UTC 12:34 - JS 然后输出 UTC 所以12:34。 Python 会将其解释为 12:34。你已经失去了一个小时。 (或者一整天,如果你只处理日期而不是时间)。冬天除外。【参考方案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"]'

【讨论】:

小幅增强 - 使用 obj.isoformat()。您还可以使用更常见的dumps() 调用,它接受其他有用的参数(如indent):simplejson.dumps(myobj, cls=JSONEncoder, ...) 因为那会调用 JSONEncoder 的父方法,而不是 DateTimeJSONEncoder 的父方法。 IE,你会上升两个级别。【参考方案5】:

这是一个相当完整的解决方案,用于使用标准库 json 模块对 datetime.datetime 和 datetime.date 对象进行递归编码和解码。这需要 Python >= 2.6,因为此后仅支持 datetime.datetime.strptime() 格式字符串中的 %f 格式代码。对于 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))

【讨论】:

如果您像datetime.datetime.utcnow().isoformat()[:-3]+"Z" 那样打印日期,它将与 JSON.stringify() 在 javascript 中生成的完全一样【参考方案6】:

如果您确定只有 Javascript 会使用 JSON,我更喜欢直接传递 Javascript Date 对象。

datetime 对象上的ctime() 方法将返回 Javascript Date 对象可以理解的字符串。

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

Javascript 很乐意将其用作对象字面量,并且您已经内置了 Date 对象。

【讨论】:

技术上无效的 JSON,但它是一个有效的 JavaScript 对象字面量。 (为了原则起见,我会将 Content-Type 设置为 text/javascript 而不是 application/json。)如果消费者将 alwaysforeveronly 一个 JavaScript 实现,那么是的,这非常优雅。我会用它。 .ctime() 是一种非常糟糕的传递时间信息的方式,.isoformat() 要好得多。 .ctime() 所做的是抛弃时区和夏令时,就像它们不存在一样。该函数应该被杀死。 多年后:请不要考虑这样做。这只有在你 eval() Javascript 中的 json 时才有效,而你真的不应该......【参考方案7】:

游戏后期... :)

一个非常简单的解决方案是修补 json 模块默认值。 例如:

import json
import datetime

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

现在,您可以像一直支持 datetime 一样使用 json.dumps()...

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

如果您需要对 json 模块的此扩展始终启动并且不希望更改您或其他人使用 json 序列化的方式(无论是否在现有代码中),这是有道理的。

请注意,有些人可能认为以这种方式修补库是不好的做法。 如果您希望以多种方式扩展您的应用程序,则需要特别小心——在这种情况下,我建议使用 ramen 或 JT 的解决方案,并在每种情况下选择适当的 json 扩展。

【讨论】:

这会默默地吃掉不可序列化的对象并将它们变成None。你可能想抛出一个异常。【参考方案8】:

除了 timestamp 之外,没有太多可添加到社区 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"

【讨论】:

【参考方案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

【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review【参考方案11】:

对于 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 doc。

【讨论】:

有没有办法像 python for arrays 那样做?我有一组日期时间对象,想缩短时间【参考方案12】:

显然The “right” JSON (well JavaScript) date format 是 2012-04-23T18:25:43.511Z - UTC 和“Z”。如果没有这个 JavaScript 将在从字符串创建 Date() 对象时使用 Web 浏览器的本地时区。

对于“幼稚”时间(Python 称之为没有时区的时间,这假定是本地时间),下面将 force local timezone 以便它可以正确转换为 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://***.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 之间的 JSON 日期时间的主要内容,如果未能解决你的问题,请参考以下文章

我如何在python和javascript之间传输数据?

Python 和 JavaScript 之间的 JSON 日期时间

使用 JSON 在 python 和 javascript 之间传递数据

请使用Javascript生成10个20~200之间的随机数,要确保生成的随机数不重复.

在 GWT 中,我们如何在 javascript 和 java 之间共享对象?

用python实现:找出1到101 之间的质数