Thời gian JSON giữa Python và JavaScript


393

Tôi muốn gửi một đối tượng datetime.datetime ở dạng tuần tự từ Python bằng JSON và khử tuần tự hóa trong JavaScript bằng JSON. Cách tốt nhất để làm việc này là gì?


Bạn có thích sử dụng một thư viện hoặc bạn muốn tự viết mã này?
guettli

Câu trả lời:


370

Bạn có thể thêm tham số 'mặc định' vào json.dumps để xử lý việc này:

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

Đó là định dạng ISO 8601 .

Một hàm xử lý mặc định toàn diện hơn:

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))

Cập nhật: Đã thêm đầu ra của loại cũng như giá trị.
Cập nhật: Cũng xử lý ngày


11
Vấn đề là nếu bạn có một số đối tượng khác trong danh sách / dict thì mã này sẽ chuyển đổi chúng thành Không có.
Tomasz Wysocki

5
json.dumps sẽ không biết làm thế nào để chuyển đổi những cái đó, nhưng ngoại lệ đang bị áp chế. Đáng buồn thay, một sửa chữa lambda một dòng có thiếu sót. Nếu bạn muốn có một ngoại lệ được nêu lên trên những ẩn số (đó là một ý tưởng tốt), hãy sử dụng chức năng tôi đã thêm ở trên.
JT.

9
định dạng đầu ra đầy đủ cũng phải có múi giờ trên đó ... và isoformat () không cung cấp chức năng này ... vì vậy bạn nên đảm bảo nối thêm thông tin đó vào chuỗi trước khi quay lại
Nick Franceschina

3
Đây là cách tốt nhất để đi. Tại sao điều này không được chọn là câu trả lời?
Brendon Crawford

16
Lambda có thể được điều chỉnh để gọi triển khai cơ sở trên các loại không phải là thời gian, vì vậy TypeError có thể được nâng lên nếu cần:dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
Pascal Bourque

81

Đối với các dự án đa ngôn ngữ, tôi phát hiện ra rằng các chuỗi chứa RfC 3339 ngày là cách tốt nhất để đi. Một ngày RfC 3339 trông như thế này:

  1985-04-12T23:20:50.52Z

Tôi nghĩ rằng hầu hết các định dạng là rõ ràng. Điều duy nhất hơi bất thường có thể là "Z" ở cuối. Nó là viết tắt của GMT / UTC. Bạn cũng có thể thêm độ lệch múi giờ như +02: 00 cho CEST (Đức vào mùa hè). Cá nhân tôi thích giữ mọi thứ trong UTC cho đến khi nó được hiển thị.

Để hiển thị, so sánh và lưu trữ, bạn có thể để nó ở định dạng chuỗi trên tất cả các ngôn ngữ. Nếu bạn cần ngày để tính toán dễ dàng chuyển đổi nó trở lại một đối tượng ngày gốc trong hầu hết các ngôn ngữ.

Vì vậy, tạo JSON như thế này:

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

Thật không may, hàm tạo ngày của Javascript không chấp nhận chuỗi RfC 3339 nhưng có nhiều trình phân tích cú pháp có sẵn trên Internet.

huTools.hujson cố gắng xử lý các vấn đề mã hóa phổ biến nhất mà bạn có thể gặp phải trong mã Python bao gồm các đối tượng ngày / thời gian trong khi xử lý các múi giờ chính xác.


17
Cơ chế định dạng ngày này được hỗ trợ nguyên bản, cả bởi datetime: datetime.isoformat () và by simplejson, sẽ kết xuất datetimecác đối tượng dưới dạng isoformatchuỗi theo mặc định. Không cần strftimehack thủ công.
jrk

9
@jrk - Tôi không nhận được chuyển đổi tự động từ datetimecác đối tượng sang isoformatchuỗi. Đối với tôi, simplejson.dumps(datetime.now())sản lượngTypeError: datetime.datetime(...) is not JSON serializable
kostmo

6
json.dumps(datetime.datetime.now().isoformat())là nơi phép màu xảy ra.
jathanism

2
Cái hay của Simplejson là nếu tôi có cấu trúc dữ liệu phức tạp, nó sẽ phân tích cú pháp và biến nó thành JSON. Nếu tôi phải làm json.dumps (datetime.datetime.now (). Isoformat ()) cho mọi đối tượng datetime, tôi sẽ mất điều đó. Có cách nào để sửa lỗi này?
andrewrk

1
superjoe30: xem stackoverflow.com/questions/455580/ trên cách làm điều đó
tối đa

67

Tôi đã làm việc đó.

Giả sử bạn có một đối tượng datetime Python, d , được tạo bằng datetime.now (). Giá trị của nó là:

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

Bạn có thể tuần tự hóa nó thành JSON dưới dạng chuỗi thời gian ISO 8601:

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

Đối tượng datetime ví dụ sẽ được tuần tự hóa thành:

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

Giá trị này, một khi nhận được trong lớp Javascript, có thể xây dựng một đối tượng Ngày:

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

Kể từ Javascript 1.8.5, các đối tượng Date có phương thức toJSON, trả về một chuỗi ở định dạng chuẩn. Do đó, để tuần tự hóa đối tượng Javascript ở trên trở lại JSON, lệnh sẽ là:

d.toJSON()

Mà sẽ cung cấp cho bạn:

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

Chuỗi này, một khi đã nhận được trong Python, có thể được giải tuần tự trở lại đối tượng datetime:

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

Điều này dẫn đến đối tượng datetime sau, giống với đối tượng bạn đã bắt đầu và do đó chính xác:

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

50

Khi sử dụng json, bạn có thể phân lớp JSONEncoder và ghi đè phương thức () mặc định để cung cấp các trình tuần tự tùy chỉnh của riêng bạn:

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)

Sau đó, bạn có thể gọi nó như thế này:

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

7
Tăng cường nhỏ - sử dụng obj.isoformat(). Bạn cũng có thể sử dụng dumps()cuộc gọi phổ biến hơn , trong đó có các đối số hữu ích khác (như indent): simplejson.dumps (myobj, cls = JSONEncoder, ...)
RCoup

3
Bởi vì điều đó sẽ gọi phương thức của cha mẹ của JSONEncoder chứ không phải phương thức của cha mẹ DateTimeJSONEncoder's. IE, bạn sẽ tăng hai cấp độ.
Brian Arsuaga

30

Đây là một giải pháp khá hoàn chỉnh để mã hóa và giải mã đệ quy các đối tượng datetime.datetime và datetime.date bằng jsonmô-đun thư viện chuẩn . Điều này cần Python> = 2.6 vì %fmã định dạng trong chuỗi định dạng datetime.datetime.strptime () chỉ được hỗ trợ kể từ đó. Đối với hỗ trợ Python 2.5, hãy bỏ %fvà tách các micro giây khỏi chuỗi ngày ISO trước khi thử chuyển đổi nó, nhưng dĩ nhiên, bạn sẽ mất độ chính xác của micro giây. Để có khả năng tương tác với các chuỗi ngày ISO từ các nguồn khác, có thể bao gồm tên múi giờ hoặc bù UTC, bạn cũng có thể cần phải loại bỏ một số phần của chuỗi ngày trước khi chuyển đổi. Để biết trình phân tích cú pháp hoàn chỉnh cho chuỗi ngày ISO (và nhiều định dạng ngày khác), hãy xem mô-đun dateutil của bên thứ ba .

Giải mã chỉ hoạt động khi các chuỗi ngày ISO là các giá trị trong ký hiệu đối tượng bằng chữ JavaScript hoặc trong các cấu trúc lồng nhau trong một đối tượng. Chuỗi ngày ISO, là các mục của mảng cấp cao nhất sẽ không được giải mã.

Tức là công việc này:

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'}

Và điều này cũng vậy:

>>> 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)}]

Nhưng điều này không hoạt động như mong đợi:

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

Đây là mã:

__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))

Nếu bạn in ngày giống như datetime.datetime.utcnow().isoformat()[:-3]+"Z"vậy, nó sẽ giống hệt như những gì JSON.opesify () tạo ra trong javascript
w00t

24

Nếu bạn chắc chắn rằng chỉ Javascript sẽ tiêu thụ JSON, tôi thích vượt qua Javascript Date trực tiếp các đối tượng .

Các ctime() phương pháp trên datetimeđối tượng sẽ trả về một chuỗi Javascript ngày đối tượng có thể hiểu được.

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

Javascript sẽ vui vẻ sử dụng nó như một đối tượng theo nghĩa đen và bạn đã xây dựng đối tượng Date của mình ngay trong đó.


12
Về mặt kỹ thuật không hợp lệ JSON, nhưng nó là một đối tượng JavaScript hợp lệ theo nghĩa đen. (Vì lợi ích của nguyên tắc tôi sẽ thiết lập Content-Type để text / javascript thay vì application / json). Nếu người tiêu dùng sẽ luôn luônmãi mãichỉ một thi JavaScript, sau đó yeah, điều này là khá thanh lịch. Tôi sẽ sử dụng nó.
hệ thống PAUSE

13
.ctime()là một cách RẤT xấu để truyền thông tin thời gian, .isoformat()tốt hơn nhiều. Điều gì .ctime()làm là vứt bỏ múi giờ và tiết kiệm ánh sáng ban ngày như chúng không tồn tại. Chức năng đó nên bị giết.
Evgeny

Nhiều năm sau: xin đừng bao giờ xem xét việc này. Điều này sẽ chỉ hoạt động nếu bạn eval () json của bạn trong Javascript mà bạn thực sự không nên ...
domenukk

11

Cuối trò chơi ... :)

Một giải pháp rất đơn giản là vá mặc định mô-đun json. Ví dụ:

import json
import datetime

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

Bây giờ, bạn có thể sử dụng json.dumps () như thể nó luôn hỗ trợ datetime ...

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

Điều này có ý nghĩa nếu bạn yêu cầu tiện ích mở rộng này cho mô-đun json luôn luôn khởi động và muốn không thay đổi cách bạn hoặc người khác sử dụng tuần tự hóa json (trong mã hiện có hoặc không).

Lưu ý rằng một số có thể coi việc vá các thư viện theo cách đó là thực tiễn xấu. Cần đặc biệt cẩn thận trong trường hợp bạn có thể muốn mở rộng ứng dụng của mình theo nhiều cách - là một trường hợp như vậy, tôi đề nghị sử dụng giải pháp bằng ramen hoặc JT và chọn phần mở rộng json thích hợp trong từng trường hợp.


6
Điều này âm thầm ăn các đối tượng không tuần tự hóa và biến chúng thành None. Bạn có thể muốn ném một ngoại lệ thay thế.
Máy xay sinh tố

6

Không có nhiều để thêm vào câu trả lời wiki cộng đồng, ngoại trừ dấu thời gian !

Javascript sử dụng định dạng sau:

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

Phía Python (đối với json.dumpstrình xử lý, xem các câu trả lời khác):

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

Nếu bạn bỏ Z đó ra, các khung giao diện như góc không thể hiển thị ngày theo múi giờ của trình duyệt cục bộ:

> $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

Về phía trăn:

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

Về phía javascript:

var your_date = new Date(data)

dữ liệu là kết quả từ python



0

Rõ ràng Định dạng ngày của JSON bên phải JSON JSON (cũng JavaScript) là 2012-04-23T18: 25: 43.511Z - UTC và "Z". Nếu không có JavaScript này sẽ sử dụng múi giờ cục bộ của trình duyệt web khi tạo đối tượng Date () từ chuỗi.

Đối với thời gian "ngây thơ" (cái mà Python gọi là thời gian không có múi giờ và giả định này là cục bộ), bên dưới sẽ buộc múi giờ cục bộ để sau đó nó có thể được chuyển đổi chính xác thành 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)

tại sao nó lại khó thế này.


0

Để chuyển đổi ngày Python sang JavaScript, đối tượng ngày cần phải ở định dạng ISO cụ thể, tức là định dạng ISO hoặc số UNIX. Nếu định dạng ISO thiếu một số thông tin, thì bạn có thể chuyển đổi sang số Unix bằng Date.parse trước. Hơn nữa, Date.parse cũng hoạt động với React trong khi Date mới có thể kích hoạt một ngoại lệ.

Trong trường hợp bạn có một đối tượng DateTime không có mili giây, những điều sau đây cần được xem xét. :

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

Ngày ví dụ có thể là một biến trong đối tượng result.data sau lệnh gọi API.

Để biết các tùy chọn hiển thị ngày ở định dạng mong muốn (ví dụ: để hiển thị các ngày trong tuần dài), hãy xem tài liệu MDN .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.