Python和JavaScript之间的JSON日期时间

JSON datetime between Python and JavaScript

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


您可以将"default"参数添加到json.dumps以处理此问题:

1
2
3
4
5
6
7
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格式。

更全面的默认处理程序函数:

1
2
3
4
5
6
7
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))

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


对于跨语言项目,我发现包含RFC3339日期的字符串是最好的方法。RFC3339日期如下:

1
  1985-04-12T23:20:50.52Z

我认为大多数格式都是显而易见的。唯一有点不寻常的可能是结尾的"Z"。它代表格林尼治标准时间/UTC。您还可以为CEST(德国,夏季)添加一个时区偏移量,如+02:00。我个人更喜欢将所有内容保留在UTC中,直到显示出来。

对于显示、比较和存储,可以将其保留为所有语言的字符串格式。如果需要计算的日期,可以很容易地将其转换回大多数语言中的本机日期对象。

所以生成这样的JSON:

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

不幸的是,JavaScript的日期构造函数不接受RFC3339字符串,但是Internet上有许多可用的解析器。

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


I've worked it out.

Let's say you have a Python datetime object, d, created with datetime.now(). Its value is:

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

您可以将其作为ISO 8601日期时间字符串序列化为JSON:

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

示例datetime对象将序列化为:

1
'"2011-05-25T13:34:05.787000"'

在javascript层中接收到该值后,可以构造一个日期对象:

1
var d = new Date("2011-05-25T13:34:05.787000");

从javascript 1.8.5开始,日期对象有一个tojson方法,它以标准格式返回一个字符串。因此,要将上述javascript对象序列化回json,命令将是:

1
d.toJSON()

这会给你:

1
'2011-05-25T20:34:05.787Z'

此字符串一旦在python中接收到,就可以反序列化回datetime对象:

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

这将产生以下datetime对象,该对象与您开始使用的对象相同,因此是正确的:

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


使用json可以将jsonEncoder子类化并重写default()方法以提供自己的自定义序列化程序:

1
2
3
4
5
6
7
8
9
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)

然后,你可以这样称呼它:

1
2
>>> DateTimeJSONEncoder().encode([datetime.datetime.now()])
'["2010-06-15T14:42:28"]'


这里有一个非常完整的解决方案,可以使用标准库json模块递归地编码和解码datetime.datetime和datetime.date对象。这需要python>=2.6,因为datetime.datetime.strptime()格式字符串中的%f格式代码仅在since then中受支持。对于python 2.5支持,在转换日期字符串之前,请先除去%f并去掉它的iso日期字符串中的微秒,当然,您会降低微秒的精度。为了与其他来源的ISO日期字符串(可能包括时区名称或UTC偏移量)的互操作性,您可能还需要在转换之前除去日期字符串的某些部分。有关ISO日期字符串(和许多其他日期格式)的完整分析器,请参阅第三方dateutil模块。

只有当iso日期字符串是javascript中的值时,解码才有效。文本对象表示法或在对象内的嵌套结构中。ISO日期作为顶级数组项的字符串将不会被解码。

即,本工程:

1
2
3
4
5
6
7
date = datetime.datetime.now()
>>> json = dumps(dict(foo='bar', innerdict=dict(date=date)))
>>> json
'{"innerdict": {"date":"2010-07-15T13:16:38.365579
<div class="suo-content">[collapse title=""]<ul><li>如果您像<wyn>datetime.datetime.utcnow().isoformat()[:-3]+"Z"</wyn>那样打印日期,它将与json.stringify()在javascript中生成的结果完全相同。</li></ul>[/collapse]</div><hr><P>如果您确定只有javascript会使用JSON,那么我更喜欢直接传递javascript <wyn>Date</wyn>对象。</P><P><wyn>datetime</wyn>对象上的<wyn>ctime()</wyn>方法将返回一个javascript日期对象可以理解的字符串。</P>[cc lang="javascript"]import datetime
date = datetime.datetime.today()
json = '
{"mydate":new Date("%s")}' % date.ctime()

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


游戏后期…:)

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

1
2
3
4
import json
import datetime

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

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

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

如果您需要对JSON模块进行此扩展以始终启动并希望不更改您或其他人使用JSON序列化的方式(无论是在现有代码中还是在现有代码中),那么这是有意义的。

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


除了时间戳之外,没有什么可添加到社区wiki答案中!

javascript使用以下格式:

1
new Date().toJSON() //"2016-01-08T19:00:00.123Z"

python端(对于json.dumps处理程序,请参见其他答案):

1
2
3
4
5
6
>>> 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)无法在浏览器本地时区中显示日期:

1
2
3
4
> $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


在python方面:

1
2
3
4
5
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方面:

1
var your_date = new Date(data)

其中数据来自python


显然,"正确"的json(well javascript)日期格式是2012-04-23t18:25:43.511z-utc和"z"。如果没有此javascript,则在从字符串创建日期()对象时,将使用Web浏览器的本地时区。

对于"幼稚"的时间(python称之为没有时区的时间,并假定为本地时间),下面将强制本地时区,以便将其正确转换为UTC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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)

为什么这么难。