Python과 JavaScript 사이의 JSON 날짜 시간


393

JSON을 사용하여 Python에서 직렬화 된 형식으로 datetime.datetime 객체를 보내고 JSON을 사용하여 JavaScript 에서 직렬화 해제하고 싶습니다 . 가장 좋은 방법은 무엇입니까?


라이브러리를 사용 하시겠습니까? 아니면 직접 코딩 하시겠습니까?
guettli

답변:


370

json.dumps에 'default'매개 변수를 추가하여이를 처리 할 수 ​​있습니다.

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
문제는 list / dict에 다른 객체가 있으면이 코드가 None으로 변환된다는 것입니다.
Tomasz Wysocki

5
json.dumps는 그것들을 변환하는 방법을 알지 못하지만 예외는 억제되고 있습니다. 슬프게도 한 줄 람다 수정에는 단점이 있습니다. 알 수없는 것들에 대해 예외를 제기하려면 (좋은 생각입니다) 위에서 추가 한 기능을 사용하십시오.
JT.

9
전체 출력 형식에도 시간대가 있어야하고 isoformat ()은이 기능을 제공하지 않습니다. 따라서 반환하기 전에 문자열에 해당 정보를 추가해야합니다.
Nick Franceschina

3
이것이 가장 좋은 방법입니다. 이것이 답변으로 선택되지 않은 이유는 무엇입니까?
Brendon Crawford

16
람다는 날짜가 아닌 유형에 대한 기본 구현을 호출하도록 조정할 수 있으므로 필요한 경우 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 문자열을 허용하지 않지만 인터넷 에는 많은 파서가 있습니다.

huTools.hujson 은 시간대를 올바르게 처리하면서 날짜 / 날짜 / 시간 객체를 포함하여 Python 코드에서 발생할 수있는 가장 일반적인 인코딩 문제를 처리하려고합니다.


17
이 날짜 형식화 메커니즘은 기본적으로 datetimedatetime.isoformat () 및 by로 지원되며 기본적으로 객체를 문자열로 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

2
simplejson의 장점은 복잡한 데이터 구조가 있으면 구문 분석하여 JSON으로 변환한다는 것입니다. 모든 datetime 객체에 대해 json.dumps (datetime.datetime.now (). isoformat ())을 수행 해야하는 경우 손실됩니다. 이 문제를 해결하는 방법이 있습니까?
andrewrk

1
superjoe30 : 이를 수행하는 방법에 대해서는 stackoverflow.com/questions/455580/… 을 참조하십시오
max

67

나는 그것을 해결했다.

datetime.now ()로 만든 파이썬 datetime 객체 d 가 있다고 가정 해 봅시다 . 그 가치는 다음과 같습니다.

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

ISO 8601 날짜 / 시간 문자열로 JSON에 직렬화 할 수 있습니다.

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 메서드가 있습니다. 위의 자바 스크립트 객체를 다시 JSON으로 직렬화하려면 명령은 다음과 같습니다.

d.toJSON()

어느 것이 당신에게 줄 것입니까?

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

이 문자열은 일단 파이썬으로 수신되면 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

를 사용하여 jsonJSONEncoder를 서브 클래 싱하고 default () 메서드를 재정 의하여 고유 한 사용자 지정 serializer를 제공 할 수 있습니다.

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

3
DateTimeJSONEncoder의 부모 메서드가 아닌 JSONEncoder의 부모 메서드를 호출하기 때문입니다. IE, 당신은 두 가지 수준으로 올라갈 것입니다.
브라이언 Arsuaga

30

다음은 표준 라이브러리 json모듈을 사용하여 datetime.datetime 및 datetime.date 객체를 재귀 적으로 인코딩하고 디코딩하기위한 매우 완벽한 솔루션입니다 . %fdatetime.datetime.strptime () 형식 문자열 의 형식 코드는 그 이후로만 지원 되므로 Python> = 2.6이 필요합니다 . Python 2.5 지원의 경우, %f변환하기 전에 ISO 날짜 문자열에서 마이크로 초를 제거하고 제거하십시오. 물론 마이크로 초 정밀도는 느슨합니다. 표준 시간대 이름 또는 UTC 오프셋을 포함 할 수있는 다른 소스의 ISO 날짜 문자열과의 상호 운용성을 위해 변환 전에 날짜 문자열의 일부를 제거해야 할 수도 있습니다. 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 ()가 자바 스크립트에서 생성하는 것과 정확히 같습니다
w00t

24

Javascript만이 JSON을 소비한다고 확신하는 경우 Javascript Date객체를 직접 전달하는 것이 좋습니다.

객체 의 ctime()메소드 datetime는 Javascript Date 객체가 이해할 수있는 문자열을 반환합니다.

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

Javascript는이를 행복하게 객체 리터럴로 사용하며 Date 객체가 내장되어 있습니다.


12
기술적으로 유효한 JSON은 아니지만 유효한 JavaScript 객체 리터럴입니다. (원칙을 위해서 나는 텍스트 / 자바 스크립트 대신 응용 프로그램 / JSON에 콘텐츠 형식을 설정합니다.) 소비자가됩니다 항상 그리고 영원히 있을 경우에만 자바 스크립트 구현, 다음 네,이 매우 우아한. 나는 그것을 사용할 것입니다.
시스템 일시

13
.ctime()시간 정보를 전달하는 매우 나쁜 방법 .isoformat()이며 훨씬 좋습니다. 어떤 .ctime()일은 그들이 존재하지 않는 것처럼 절약 시간대와 일광을 던져입니다. 이 기능은 종료되어야합니다.
Evgeny

몇 년 후 :이 작업을 고려하지 마십시오. 이것은 당신이 정말로하지 말아야 할 자바 스크립트로 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 () 를 항상 datetime을 지원 한 것처럼 사용할 수 있습니다 ...

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

이것은 항상 확장하기 위해 json 모듈에 대한이 확장이 필요하고 귀하 또는 다른 사람들이 json 직렬화를 사용하는 방식 (기존 코드에서 또는 아닌)을 변경하지 않으려는 경우에 적합합니다.

어떤 사람들은 그런 방식으로 라이브러리 패치를 나쁜 습관으로 생각할 수 있습니다. 응용 프로그램을 여러 가지 방법으로 확장하려는 경우 특별한주의가 필요합니다.이 경우라면라면 또는 JT로 솔루션을 사용하고 각 경우에 적절한 json 확장을 선택하는 것이 좋습니다.


6
이것은 직렬화 할 수없는 객체를 자동으로 먹고로 바꿉니다 None. 대신 예외를 던질 수도 있습니다.
Blender

6

타임 스탬프를 제외하고 커뮤니티 위키 답변에 추가하지 않아도됩니다 !

자바 스크립트는 다음 형식을 사용합니다.

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

파이썬 측 ( 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

파이썬 측면에서 :

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

자바 스크립트 측면에서 :

var your_date = new Date(data)

파이썬에서 데이터가 생성되는 곳



0

분명히 “올바른”JSON (잘 JavaScript) 날짜 형식 은 2012-04-23T18 : 25 : 43.511Z-UTC 및 "Z"입니다. 이 JavaScript가 없으면 문자열에서 Date () 객체를 만들 때 웹 브라우저의 현지 시간대를 사용합니다.

"순진한"시간 (파이썬이 시간대가없는 시간을 호출하고 이것이 현지인이라고 가정)의 경우 아래는 현지 시간대강제 로 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 문서를 확인하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.