Python和JavaScript之间的JSON日期时间


Answers:


370

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

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


11
问题是,如果列表/字典中还有其他对象,则此代码会将它们转换为“无”。
Tomasz Wysocki 2010年

5
json.dumps都不知道如何转换它们,但是该异常已被禁止。可悲的是,单行lambda修复程序有其缺点。如果您希望对未知数引发异常(这是个好主意),请使用上面添加的功能。
JT。

9
完整的输出格式也应具有时区...而isoformat()不提供此功能...,因此您应确保在返回之前在字符串上附加该信息
Nick Franceschina 2010年

3
这是最好的方法。为什么未选择此答案?
布伦登·克劳福德

16
lambda可以适应于在非datetime类型上调用基本实现,因此如果需要,可以引发TypeError:dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
Pascal Bourque

81

对于跨语言项目,我发现包含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代码中可能遇到的最常见的编码问题,包括日期/日期时间对象,同时正确处理时区。


17
datetimedatetime.isoformat()和都原生支持此日期格式设置机制,默认情况下simplejson会将日期datetime作为isoformat字符串转储。无需手动strftime黑客。
jrk

9
@jrk-我没有从datetime对象自动转换为isoformat字符串。对我来说,simplejson.dumps(datetime.now())收益TypeError: datetime.datetime(...) is not JSON serializable
kostmo

6
json.dumps(datetime.datetime.now().isoformat())是魔术发生的地方。
jathanism'4

2
simplejson的优点在于,如果我具有复杂的数据结构,它将对其进行解析并将其转换为JSON。如果我必须为每个datetime对象执行json.dumps(datetime.datetime.now()。isoformat()),那我就失去了。有没有办法来解决这个问题?
andrewrk

1
superjoe30:看stackoverflow.com/questions/455580/...如何做到这一点
最多

67

我已经解决了。

假设您有一个使用datetime.now()创建的Python datetime对象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对象:

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

这将导致以下日期时间对象,该对象与您开始时使用的对象相同,因此是正确的:

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

50

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

7
次要增强功能-使用obj.isoformat()。您还可以使用更常见的dumps()调用,该调用需要其他有用的参数(如indent):simplejson.dumps(myobj,cls = JSONEncoder,...)
rcoup 2010年

3
因为那样会调用JSONEncoder的父方法,而不是DateTimeJSONEncoder的父方法。IE,您将上升两个级别。
Brian Arsuaga 2012年

30

这是一个使用标准库json模块递归编码和解码datetime.datetime和datetime.date对象的完整解决方案。此后需要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))

如果您像打印日期一样datetime.datetime.utcnow().isoformat()[:-3]+"Z",它将完全像JSON.stringify()在javascript中生成的内容
w00t

24

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

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

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

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


12
从技术上讲,它不是有效的JSON,但它是有效的JavaScript对象文字。(为原则的缘故,我将设置内容类型为text / JavaScript的,而不是应用程序/ JSON)。如果消费者将始终永远唯一一个JavaScript实现,那么是的,这是非常优雅。我会用它。
系统暂停

13
.ctime()这是一种传递时间信息的非常糟糕的方法,.isoformat()它要好得多。什么.ctime()确实是扔掉时区和夏令就像他们不存在。该功能应被取消。
叶夫根尼(Evgeny)2012年

多年后:请永远不要考虑这样做。仅当您真正不应该使用Javascript中的json来对json进行eval()时,此方法才有效……
domenukk,

11

比赛后期... :)

一个非常简单的解决方案是修补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扩展名。


6
这样会静默地吃掉无法序列化的对象,并将它们变成None。您可能想抛出一个异常。
Blender 2016年

6

除了时间戳,没有什么可添加到社区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"

4

在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的地方



0

显然,“正确的” JSON(很好的JavaScript)日期格式是2012-04-23T18:25:43.511Z-UTC和“ Z”。如果没有此功能,则从字符串创建Date()对象时,JavaScript将使用Web浏览器的本地时区。

对于“天真的”时间(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)

为什么这么难。


0

对于从Python到JavaScript的日期转换,日期对象必须采用特定的ISO格式,即ISO格式或UNIX数字。如果ISO格式缺少某些信息,则可以先使用Date.parse转换为Unix数字。此外,Date.parse也可以与React一起使用,而新的Date可能会触发异常。

如果您有一个不带毫秒的DateTime对象,则需要考虑以下因素。:

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

在API调用之后,示例日期也可以是result.data对象中的变量。

有关以所需格式显示日期的选项(例如,显示较长的工作日),请查看MDN文档

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.