ISO 8601 형식의 날짜를 어떻게 구문 분석합니까?


642

RFC 3339 문자열 "2008-09-03T20:56:35.450686Z"을 Python datetime유형 으로 구문 분석해야 합니다.

strptime파이썬 표준 라이브러리에서 찾았 지만 그리 편리하지는 않습니다.

가장 좋은 방법은 무엇입니까?




3
명확하게 말하면 : ISO 8601 이 주요 표준입니다. RFC 3339 는 ISO 8601의 자체 선언 된 "프로파일" 로서 ISO 8601 규칙을 현명하게 무시 합니다.
Basil Bourque

3
isoformat () 반전을위한 아래의 python3.7 + 솔루션을 놓치지 마십시오
Brad M

2
이 질문은 링크 된 게시물에 속하여 닫혀서는 안됩니다. 이 중 하나는 ISO 8601 시간 문자열 (python 3.7 이전에는 기본적으로 지원되지 않음) 을 구문 분석 하고 다른 하나는 사용되지 않는 메소드를 사용하여 날짜 시간 오브젝트를 에포크 문자열로 형식화 하는 것입니다.
abccd

답변:


462

파이썬 dateutil 패키지는 문제의 하나처럼뿐만 아니라 RFC 3339 날짜 문자열을 구문 분석 할 수 있지만, 다른 ISO 8601 없는 UTC와 사람 오프셋 RFC 3339을 준수하지 않는 날짜와 시간 문자열 (예, 또는 사람이 표현하는 날짜 만).

>>> import dateutil.parser
>>> dateutil.parser.isoparse('2008-09-03T20:56:35.450686Z') # RFC 3339 format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())
>>> dateutil.parser.isoparse('2008-09-03T20:56:35.450686') # ISO 8601 extended format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.isoparse('20080903T205635.450686') # ISO 8601 basic format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.isoparse('20080903') # ISO 8601 basic format, date only
datetime.datetime(2008, 9, 3, 0, 0)

참고 dateutil.parser.isoparse더 해키보다는 아마 엄격 dateutil.parser.parse하지만, 둘 다 아주 용서하고 당신이 전달하는 문자열을 해석하려고 시도합니다. 당신이 어떤 오독의 가능성을 제거하려면 다음 중 하나를보다 뭔가 엄격한을 사용할 필요가 기능.

Pypi 이름은 python-dateutil아닙니다 dateutil(감사합니다 code3monk3y ).

pip install python-dateutil

Python 3.7을 사용하는 경우에 대한 답변을 살펴 보십시오 datetime.datetime.fromisoformat.


75
게으른 경우 python-dateutilnot을 통해 설치 dateutil되므로 다음을 수행하십시오 pip install python-dateutil.
cod3monk3y 2016 년

29
(가) 있다고 경고 dateutil.parser의도적으로 해키 : 그것은 형식을 추측하려고하고 모호한 경우 (손에 의해서만 정의) 피할 수없는 가정을합니다. 알 수없는 형식의 입력을 구문 분석해야하고 가끔 오해를 용인해도 괜찮을 경우에만 사용하십시오.
ivan_pozdeev

2
동의했다. 예를 들어 "날짜"9999를 전달하면 datetime (9999, 현재 월, 현재 날짜)과 동일하게 반환됩니다. 내 관점에서 유효한 날짜가 아닙니다.
timbo

1
비 추측 파싱을 위해 어떤 패키지를 추천 하시겠습니까?
bgusach

2
@ivan_pozdeev iso8601 날짜를 읽는 모듈에 대한 업데이트가 있습니다 : dateutil.readthedocs.io/en/stable/…
TheEpsilon

196

Python 3.7+의 새로운 기능


datetime표준 라이브러리 반전하는 기능을 도입 datetime.isoformat().

classmethod datetime.fromisoformat(date_string):

및에서 방출되는 형식 중 하나에서에 datetime해당 하는를 반환합니다 .date_stringdate.isoformat()datetime.isoformat()

특히이 함수는 다음 형식으로 문자열을 지원합니다.

YYYY-MM-DD[*HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]]]

여기서 *하나의 문자를 일치시킬 수 있습니다.

주의 : 이것은 임의의 ISO 8601 문자열 구문 분석을 지원하지 않습니다. 이는 역 동작으로 만 사용됩니다 datetime.isoformat().

사용 예 :

from datetime import datetime

date = datetime.fromisoformat('2017-01-01T12:30:59.000000')

6
이상 하네. a datetime가 포함되어 tzinfo시간대를 출력하지만 datetime.fromisoformat()tzinfo?를 구문 분석하지 않습니까? 버그처럼 보인다 ..
Hendy Irawan

20
설명서에서 해당 메모를 놓치지 마십시오. 유효한 ISO 8601 문자열을 모두 허용하지는 않습니다 isoformat. 그것은 문제의 예를 허용하지 않습니다 "2008-09-03T20:56:35.450686Z"때문에 후행의 Z,하지만 동의 않습니다 "2008-09-03T20:56:35.450686".
Flimm

26
Z입력 스크립트 를 올바르게 지원하려면을 (를) 사용 하여 수정할 수 있습니다 date_string.replace("Z", "+00:00").
jox

7
몇 초 동안 정확히 0, 3 또는 6 개의 소수점 이하 자릿수 만 처리합니다. 입력 데이터에 소수점 이하 1, 2, 4, 5, 7 개 이상이 있으면 구문 분석에 실패합니다!
Felk

1
@JDOaktown이 예제는 dateutil의 파서가 아닌 기본 Python의 날짜 / 시간 라이브러리를 사용합니다. 이 방법으로 소수 자릿수가 0, 3 또는 6이 아닌 경우 실제로 실패합니다.
abccd

174

Python 2.6 이상 및 Py3K에서 % f 문자는 마이크로 초를 포착합니다.

>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")

여기에서 이슈를 보십시오


4
참고-Naive 날짜 시간을 사용하는 경우-TZ가 전혀 없다고 생각합니다-Z가 일치하지 않을 수 있습니다.
Danny Staple

24
이 답변 (현재 편집 된 양식)은 특정 UTC 오프셋 (즉 "0"을 의미하는 "Z")을 형식 문자열로 하드 코딩합니다. 다른 UTC 오프셋으로 날짜 시간을 구문 분석하고 예외를 발생시키지 않기 때문에 이것은 나쁜 생각입니다. RFC 3339를 구문 분석하는 것이 실제로 불가능한 방법을 설명하는 내 대답 을 참조하십시오 strptime.
Mark Amery

1
필자의 경우 % f는 Z가 아닌 마이크로 초를 잡았 datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f')
으므로이 방법

Py3K는 Python 3000을 의미합니까?!?
Robino

2
@Robino IIRC, "Python 3000"은 현재 Python 3으로 알려진 이름입니다.
Throw Away Account

161

여기에 몇 가지 대답 은 질문에 표시된 것과 같이 시간대로 RFC 3339 또는 ISO 8601 날짜 시간을 구문 분석 하는 데 사용 하는 것이 좋습니다 . datetime.datetime.strptime

2008-09-03T20:56:35.450686Z

이것은 나쁜 생각입니다.

0이 아닌 UTC 오프셋에 대한 지원을 포함하여 전체 RFC 3339 형식을 지원한다고 가정하면이 답변에서 제안하는 코드가 작동하지 않습니다. 실제로 RFC 3339 구문을 사용하여 구문 분석하기 때문에 작동 하지 않습니다.strptime 것은 불가능 . Python의 datetime 모듈에서 사용하는 형식 문자열은 RFC 3339 구문을 설명 할 수 없습니다.

문제는 UTC 오프셋입니다. RFC 3339 인터넷 날짜 / 시간 형식은 모든 날짜와 시간은 UTC 오프셋을 포함, 그 오프셋이 될 수 있어야합니다 Z(짧은 "줄루 시간"에 대한) 또는에서 +HH:MM또는 -HH:MM형식, 같은 +05:00-10:30.

결과적으로 다음은 유효한 RFC 3339 날짜 / 시간입니다.

  • 2008-09-03T20:56:35.450686Z
  • 2008-09-03T20:56:35.450686+05:00
  • 2008-09-03T20:56:35.450686-10:30

아아, 형식 문자열에 사용 strptime하고 strftime어떤 지침이 없다는 RFC 3339 형식의 UTC 오프셋에 해당합니다. 지원하는 지시문의 전체 목록은 https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior 에서 찾을 수 있으며 목록에 포함 된 유일한 UTC 오프셋 지시문은 %z다음과 같습니다.

%지

+ HHMM 또는 -HHMM 형식의 UTC 오프셋 (객체가 순진한 경우 빈 문자열).

예 : (비어 있음), +0000, -0400, +1030

이것은 RFC 3339 오프셋의 형식과 일치하지 않으며 실제로 %z형식 문자열에서 사용 하고 RFC 3339 날짜를 구문 분석하면 실패합니다.

>>> from datetime import datetime
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
  File "", line 1, in 
  File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
    tt, fraction = _strptime(data_string, format)
  File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
    (data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686Z' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
>>> datetime.strptime("2008-09-03T20:56:35.450686+05:00", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
  File "", line 1, in 
  File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
    tt, fraction = _strptime(data_string, format)
  File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
    (data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686+05:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'

(실제로 위의 내용은 Python 3에서 볼 수있는 내용입니다. Python 2에서는 더 간단한 이유로 실패합니다. 즉, Python 2에서 지시문을 전혀 strptime구현하지 않습니다.%z .)

여기에 여러 답변 strptimeZ형식 문자열에 리터럴을 포함 하여이 문제를 해결 하는 것이 좋습니다 Z. 질문의 예 날짜 시간 문자열 과 일치하고 datetime시간대가없는 객체를 생성합니다 .

>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)

이렇게하면 원래 날짜 / 시간 문자열에 포함 된 시간대 정보가 삭제되므로이 결과조차도 올바른 것으로 간주해야하는지 의심됩니다. 그러나 더 중요한 것은이 접근법에는 특정 UTC 오프셋을 형식 문자열로 하드 코딩하는 것이 포함되므로 RFC 3339 날짜 시간을 다른 UTC 오프셋으로 구문 분석하려고하는 순간을 질식시킵니다.

>>> datetime.strptime("2008-09-03T20:56:35.450686+05:00", "%Y-%m-%dT%H:%M:%S.%fZ")
Traceback (most recent call last):
  File "", line 1, in 
  File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
    tt, fraction = _strptime(data_string, format)
  File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
    (data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686+05:00' does not match format '%Y-%m-%dT%H:%M:%S.%fZ'

있는 거 당신이하지 않는 특정 경우에만 RFC에게 다른 시간대 오프셋 (offset) 3339 줄루 시간에서 날짜 시간, 그리고 사람들을 지원하는 데 필요한 사용하지 마십시오 strptime. 대신 답변에 설명 된 다른 많은 접근법 중 하나를 사용하십시오.


79
strptime에 ISO 형식 시간대 정보에 대한 지시문이없는 이유와 구문 분석 할 수없는 이유에 대해 염두에 두어야합니다. 놀랄 만한.
Csaba Toth

2
@CsabaToth 완전히 동의했습니다. 죽일 시간이 있다면 언어에 추가하려고 노력할 것입니다. 또는 당신이 그렇게 기울어 졌다면 그렇게 할 수 있습니다. 저와는 달리 C 경험이있는 것 같습니다.
Mark Amery

1
@CsabaToth-왜 놀라운가? 대부분의 사람들에게 충분하게 작동하거나 쉽게 해결 방법을 찾았습니다. 기능이 필요한 경우 해당 기능은 오픈 소스이므로 추가 할 수 있습니다. 또는 다른 사람을 대신하여 비용을 지불하십시오. 특정 문제를 해결하기 위해 자신의 자유 시간을 자원 봉사해야하는 이유는 무엇입니까? 소스를 당신과 함께하십시오.
Peter M.-

2
@PeterMasiar 인크레더블은 보통 파이썬의 것들이 신중하고 완벽하게 구현되었다는 것을 발견하기 때문입니다. 우리는이 세세한 부분까지주의를 기울여 왔기 때문에 "언피 토닉"이라는 언어로 무언가를 우연히 발견하면 장난감을 유모차 밖으로 내 보냅니다. Whaaaaaaaaaa Whaa wahaaaaa :-(
Robino

2
strptime()Python 3.7에서는 이제이 답변에서 불가능하다고 설명 된 모든 것을 지원합니다 (표준 시간대 오프셋의 'Z'리터럴 및 ':'). 불행히도 RFC 3339가 ISO 8601과 근본적으로 호환되지 않는 또 다른 코너 케이스가 있습니다. 즉, 전자는 음의 null 시간대 오프셋 -00 : 00을 허용하고 나중에는 그렇지 않습니다.
SergiyKolesnikov

75

iso8601 모듈을 사용해보십시오 . 정확히이 작업을 수행합니다.

python.org 위키 의 WorkingWithTime 페이지에 언급 된 몇 가지 다른 옵션이 있습니다 .


간단한iso8601.parse_date("2008-09-03T20:56:35.450686Z")
Pakman

3
문제는 "ISO 8601 날짜를 구문 분석하는 방법"이 아니라 "이 정확한 날짜 형식을 구문 분석하는 방법"이었습니다.
니콜라스 라일리

3
@tiktak OP는 "X와 같은 문자열을 구문 분석해야합니다"라고 물었고 두 라이브러리를 모두 시도한 후에는 iso8601에 여전히 중요한 문제가 있기 때문에 두 라이브러리를 모두 사용하는 것이 좋습니다. 그러한 프로젝트에 대한 나의 참여 또는 부족은 그 답과 완전히 관련이 없습니다.
Tobia

2
iso8601의 pip 버전은 2007 년 이후로 업데이트되지 않았으며 눈에 띄는 심각한 버그가 있습니다. 나는 자신을 패치의 몇 가지 중요한을 적용 권장하거나 아직 한 많은 GitHub의 포크 중 하나를 찾을 수 github.com/keithhackbarth/pyiso8601-strict
keithhackbarth

6
iso8601 (일명 pyiso8601 )은 최근 2014 년 2 월로 업데이트되었습니다. 최신 버전은 훨씬 광범위한 ISO 8601 문자열 세트를 지원합니다. 나는 내 프로젝트 중 일부에서 좋은 효과를 내기 위해 사용하고 있습니다.
Dave Hein

34
다시 가져 오기, 날짜
s = "2008-09-03T20 : 56 : 35.450686Z"
d = datetime.datetime (* map (int, re.split ( '[^ \ d]', s) [:-1]))

73
동의하지 않습니다. 실제로 읽을 수 없으며 시간대 데이터가 제공되었지만이 날짜 시간을 순진하게 만드는 Zulu (Z)를 고려하지 않는 한 말할 수 없습니다.
엄 브레

14
나는 그것을 아주 읽기 쉽다는 것을 안다. 실제로 추가 패키지를 설치하지 않고 변환을 수행하는 가장 쉽고 성능이 좋은 방법 일 것입니다.
Tobia

2
이것은 d = datetime.datetime (* map (int, re.split ( '\ D', s) [:-1]))과 같습니다.
Xuan

4
변형 :datetime.datetime(*map(int, re.findall('\d+', s))
jfs

3
시간대가없는 순진한 datetime 객체가 생깁니다. 그래서 UTC 비트는 번역에서 손실됩니까?
w00t 2016 년

32

당신이 얻는 정확한 오류는 무엇입니까? 다음과 같은가요?

>>> datetime.datetime.strptime("2008-08-12T12:20:30.656234Z", "%Y-%m-%dT%H:%M:%S.Z")
ValueError: time data did not match format:  data=2008-08-12T12:20:30.656234Z  fmt=%Y-%m-%dT%H:%M:%S.Z

그렇다면 입력 문자열을 "."로 나누고 날짜 시간에 마이크로 초를 추가 할 수 있습니다.

이 시도:

>>> def gt(dt_str):
        dt, _, us= dt_str.partition(".")
        dt= datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S")
        us= int(us.rstrip("Z"), 10)
        return dt + datetime.timedelta(microseconds=us)

>>> gt("2008-08-12T12:20:30.656234Z")
datetime.datetime(2008, 8, 12, 12, 20, 30, 656234)

10
시간대를 의미하고 다를 수 있기 때문에 .Z 만 제거 할 수는 없습니다. 날짜를 UTC 시간대로 변환해야합니다.
Alexander Artemenko

일반 날짜 / 시간 개체에는 표준 시간대 개념이 없습니다. 모든 시간이 "Z"로 끝나는 모든 날짜 시간은 UTC (Zulu 시간)입니다.
tzot

시간대가 ""또는 이외의 시간대 인 경우 "Z"시간 / 분 단위의 오프셋이어야하며 날짜 / 시간 객체에서 직접 추가 / 빼기 할 수 있습니다. 당신은 를 처리하는 서브 클래스 tzinfo을 만들 수 있지만, 아마도 reccomended 아니에요.
SingleNegationElimination

8
또한 "% f"는 마이크로 초 지정자이므로 시간대가 지정되지 않은 strptime 문자열은 "% Y- % m- % dT % H : % M : % S. % f"와 같습니다.
quodlibetor

1
주어진 날짜 / 시간 문자열에 "Z"이외의 UTC 오프셋이있는 경우 예외가 발생합니다. 전체 RFC 3339 형식을 지원하지 않으며 UTC 오프셋을 올바르게 처리하는 다른 사람들에게는 열등한 답변입니다.
Mark Amery

24

Python 3.7부터 strptime은 UTC 오프셋 ( source )으로 콜론 구분 기호를 지원합니다 . 따라서 다음을 사용할 수 있습니다.

import datetime
datetime.datetime.strptime('2018-01-31T09:24:31.488670+00:00', '%Y-%m-%dT%H:%M:%S.%f%z')

편집하다:

Martijn이 지적한 것처럼 isoformat ()을 사용하여 datetime 객체를 만든 경우 간단히 datetime.fromisoformat ()을 사용할 수 있습니다


4
그러나 3.7에서는 입력과 같은 문자열을 자동으로 처리하는 것도 있습니다 . datetime.fromisoformat()datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00')
Martijn Pieters

2
좋은 지적. 동의, 내가 사용하는 것이 좋습니다 datetime.fromisoformat()datetime.isoformat()
안드레아스 Profous에게

19

요즘 Arrow 는 타사 솔루션으로도 사용할 수 있습니다.

>>> import arrow
>>> date = arrow.get("2008-09-03T20:56:35.450686Z")
>>> date.datetime
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())

6
Arrow는 ISO8601을 제대로 지원하지 않습니다 : github.com/crsmithdev/arrow/issues/291
박스

1
python-dateutil을 사용하십시오-화살표에는 python-dateutil이 필요합니다.
danizen

Arrow는 이제 ISO8601을 지원합니다. 참조 된 문제는 이제 종결되었습니다.
Altus

17

python-dateutil모듈을 사용하십시오 .

>>> import dateutil.parser as dp
>>> t = '1984-06-02T19:05:00.000Z'
>>> parsed_t = dp.parse(t)
>>> print(parsed_t)
datetime.datetime(1984, 6, 2, 19, 5, tzinfo=tzutc())

선적 서류 비치


1
정확히 @Flimms가 위의 답변이 아닙니까?
leo

1
몇 초 안에 그가 파싱하는 것을 어디에서 봅니까? 나는 획기적인 시간을 갖기 위해이 기사를 찾았으므로 다른 사람도있을 것이라고 생각했습니다.
Blairg23

1
이것은 내 시스템에서 UTC가 아닙니다 . 오히려 초 단위의 출력은 마치 날짜가 현지 시간대에 있었던 것처럼 유닉스 시대입니다.
엘리엇

1
이 답변은 버그가 있으므로 받아 들여서는 안됩니다. 아마도 전체 질문은 stackoverflow.com/questions/11743019/…
tripleee

@tripleee 실제로 방금 코드를 455051100확인했는데 뭔가 빠지지 않는 한 정답 : ( epochconverter.com 에서 확인) 을 반환하는 것 같습니다 .
블레어 23

13

dateutil을 사용하지 않으려면이 기능을 사용해보십시오.

def from_utc(utcTime,fmt="%Y-%m-%dT%H:%M:%S.%fZ"):
    """
    Convert UTC time string to time.struct_time
    """
    # change datetime.datetime to time, return time.struct_time type
    return datetime.datetime.strptime(utcTime, fmt)

테스트:

from_utc("2007-03-04T21:08:12.123Z")

결과:

datetime.datetime(2007, 3, 4, 21, 8, 12, 123000)

5
이 답변은에 전달 된 형식 문자열에 특정 UTC 오프셋 (즉, "0"을 의미하는 "Z")을 하드 코딩하는 데 의존합니다 strptime. 이것은 다른 UTC 오프셋으로 날짜 시간을 구문 분석하고 예외를 발생시키지 않기 때문에 나쁜 생각입니다. strptime으로 RFC 3339를 구문 분석하는 것이 실제로 불가능한 방법을 설명하는 내 대답 을 참조하십시오 .
Mark Amery

1
하드 코딩되어 있지만 줄루 만 구문 분석 해야하는 경우에 충분합니다.
사샤

1
@alexander yes-예를 들어 날짜 문자열이 JavaScript의 toISOString메소드 로 생성 된 것을 알고있는 경우에 해당 될 수 있습니다 . 그러나이 답변에 Zulu 시간 날짜에 대한 제한에 대한 언급은 없으며, 그것이 필요한 전부이며, 사용하는 dateutil것이 일반적으로 똑같이 편리하고 구문 분석 할 수있는 범위가 좁다 는 질문도 나타내지 않았습니다 .
마크 애 머리

11

Django로 작업하는 경우 시간대를 포함하여 ISO 형식과 유사한 여러 형식을 허용하는 dateparse 모듈 을 제공합니다 .

Django를 사용하지 않고 여기에 언급 된 다른 라이브러리 중 하나를 사용하지 않으려는 경우 프로젝트 에 날짜 분석 용 Django 소스 코드를 적용 할 수 있습니다.


장고 DateTimeField는 문자열 값을 설정할 때 이것을 사용합니다.
djvg

11

ciso8601 이 ISO 8601 타임 스탬프를 구문 분석하는 가장 빠른 방법 인 것으로 나타 났습니다 . 이름에서 알 수 있듯이 C로 구현됩니다.

import ciso8601
ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')

GitHub의 리포 README 다른 답변에 나와있는 다른 모든 라이브러리에 비해 자신의> 10 배의 속도 향상을 보여줍니다.

내 개인 프로젝트에는 많은 ISO 8601 파싱이 포함되었습니다. 통화를 전환하고 10 배 빠르게 진행할 수있어서 좋았습니다. :)

편집 : 이후 ciso8601의 관리자가되었습니다. 이제 그 어느 때보 다 빨라졌습니다!


이것은 훌륭한 도서관처럼 보입니다! 안타깝게도 Google App Engine에서 ISO8601 구문 분석을 최적화하려는 경우 C 라이브러리이기 때문에 사용할 수 없지만 벤치 마크는 네이티브 datetime.strptime()가 다음으로 가장 빠른 솔루션 이라는 것을 보여줍니다 . 모든 정보를 모아 주셔서 감사합니다!
hamx0r

3
@ hamx0r datetime.strptime()은 완전한 ISO 8601 파싱 라이브러리가 아닙니다. Python 3.7을 사용하는 datetime.fromisoformat()경우 좀 더 유연한 메소드를 사용할 수 있습니다 . 곧 ciso8601 README에 병합해야 할 보다 완전한 파서 목록에 관심이 있을 것 입니다.
무버 마이어

ciso8601은 꽤 훌륭하게 작동하지만 먼저 "pip install pytz"를 수행해야합니다. pytz 종속성없이 시간대 정보로 타임 스탬프를 구문 분석 할 수 없기 때문입니다. 예를 들면 다음과 같습니다. dob = ciso8601.parse_datetime (result [ 'dob'] [ 'date'])
Dirk

2
@Dirk, 파이썬 2에서만 . 그러나 다음 릴리스에서는 제거해야합니다 .
movermeyer

8

이것은 파이썬 3.2 이후 stdlib에서 작동합니다 (모든 타임 스탬프가 UTC라고 가정) :

from datetime import datetime, timezone, timedelta
datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ").replace(
    tzinfo=timezone(timedelta(0)))

예를 들어

>>> datetime.utcnow().replace(tzinfo=timezone(timedelta(0)))
... datetime.datetime(2015, 3, 11, 6, 2, 47, 879129, tzinfo=datetime.timezone.utc)

2
이 답변은에 전달 된 형식 문자열에 특정 UTC 오프셋 (즉, "0"을 의미하는 "Z")을 하드 코딩하는 데 의존합니다 strptime. 이것은 다른 UTC 오프셋으로 날짜 시간을 구문 분석하고 예외를 발생시키지 않기 때문에 나쁜 생각입니다. strptime으로 RFC 3339를 구문 분석하는 것이 실제로 불가능한 방법을 설명하는 내 대답 을 참조하십시오 .
Mark Amery

1
이론적으로, 이것은 실패합니다. 실제로 Zulu 시간이 아닌 ISO 8601 형식의 날짜를 본 적이 없습니다. 매우 간혹 필요한 경우에는 이것이 효과적이며 일부 외부 라이브러리에 의존하지 않습니다.
Benjamin Riggs

4
timezone.utc대신에 사용할 수 있습니다 timezone(timedelta(0)). 또한, 파이썬 코드 작품 2.6+ 당신이 경우에 (적어도) 공급 utctzinfo 개체
JFS

문제가 발생하더라도 중요하지 않습니다. 사양과 일치하지 않습니다.
아나운서

%Z최신 버전의 Python에서 시간대를 사용할 수 있습니다 .
sventechie

7

저는 iso8601 utils의 저자입니다. GitHub 또는 PyPI 에서 찾을 수 있습니다 . 예제를 파싱하는 방법은 다음과 같습니다.

>>> from iso8601utils import parsers
>>> parsers.datetime('2008-09-03T20:56:35.450686Z')
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)

6

datetime.datetime타사 모듈을 설치하지 않고 지원되는 모든 Python 버전에서 ISO 8601과 같은 날짜 문자열을 UNIX 타임 스탬프 또는 객체 로 변환하는 간단한 방법 중 하나 는 SQLite날짜 파서 를 사용하는 것 입니다.

#!/usr/bin/env python
from __future__ import with_statement, division, print_function
import sqlite3
import datetime

testtimes = [
    "2016-08-25T16:01:26.123456Z",
    "2016-08-25T16:01:29",
]
db = sqlite3.connect(":memory:")
c = db.cursor()
for timestring in testtimes:
    c.execute("SELECT strftime('%s', ?)", (timestring,))
    converted = c.fetchone()[0]
    print("%s is %s after epoch" % (timestring, converted))
    dt = datetime.datetime.fromtimestamp(int(converted))
    print("datetime is %s" % dt)

산출:

2016-08-25T16:01:26.123456Z is 1472140886 after epoch
datetime is 2016-08-25 12:01:26
2016-08-25T16:01:29 is 1472140889 after epoch
datetime is 2016-08-25 12:01:29

11
감사. 역겨워 요. 나는 그것을 좋아한다.
wchargin

1
정말 놀랍고 멋진 해킹입니다! 감사!
Havok

6

ISO 8601 표준에 대한 파서를 코딩하여 GitHub에 넣었습니다 : https://github.com/boxed/iso8601 . 이 구현은 지속 기간, 간격, 주기적 간격 및 Python의 datetime 모듈의 지원되는 날짜 범위를 벗어난 날짜를 제외하고 사양의 모든 것을 지원합니다.

시험이 포함되어 있습니다! :피



6

Django의 parse_datetime () 함수는 UTC 오프셋이있는 날짜를 지원합니다.

parse_datetime('2016-08-09T15:12:03.65478Z') =
datetime.datetime(2016, 8, 9, 15, 12, 3, 654780, tzinfo=<UTC>)

따라서 전체 프로젝트 내의 필드에서 ISO 8601 날짜를 구문 분석하는 데 사용할 수 있습니다.

from django.utils import formats
from django.forms.fields import DateTimeField
from django.utils.dateparse import parse_datetime

class DateTimeFieldFixed(DateTimeField):
    def strptime(self, value, format):
        if format == 'iso-8601':
            return parse_datetime(value)
        return super().strptime(value, format)

DateTimeField.strptime = DateTimeFieldFixed.strptime
formats.ISO_INPUT_FORMATS['DATETIME_INPUT_FORMATS'].insert(0, 'iso-8601')

4

ISO 8601은 기본적으로 옵션 콜론과 대시의 다양한 변형을 허용하므로 CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]. strptime을 사용하려면 먼저 이러한 변형을 제거해야합니다.

목표는 utc datetime 객체를 생성하는 것입니다.


UTC와 함께 Z 접미사로 작동하는 기본 사례를 원한다면 2016-06-29T19:36:29.3453Z:

datetime.datetime.strptime(timestamp.translate(None, ':-'), "%Y%m%dT%H%M%S.%fZ")


시간대 오프셋을 처리 2016-06-29T19:36:29.3453-0400하거나 2008-09-03T20:56:35.450686+05:00다음을 사용 하려는 경우 . 이렇게하면 모든 유사 콘텐츠 20080903T205635.450686+0500를 구문 분석을보다 일관성 있고 쉽게 만드는 것과 같이 가변 구분 기호없이 무언가로 변환 할 수 있습니다.

import re
# this regex removes all colons and all 
# dashes EXCEPT for the dash indicating + or - utc offset for the timezone
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', timestamp)
datetime.datetime.strptime(conformed_timestamp, "%Y%m%dT%H%M%S.%f%z" )


시스템이 %zstrptime 지시문을 지원하지 않는 경우 (와 같이 ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'표시됨) Z(UTC) 에서 시간을 수동으로 오프셋해야합니다 . 참고 %z파이썬 버전의 <당신의 시스템에서 작동하지 않을 수 있습니다 3은 시스템 / 파이썬 빌드 형식 (예 : 자이 썬, 사이 썬 등)에 걸쳐 다양 C 라이브러리의 지원에 의존한다.

import re
import datetime

# this regex removes all colons and all 
# dashes EXCEPT for the dash indicating + or - utc offset for the timezone
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', timestamp)

# split on the offset to remove it. use a capture group to keep the delimiter
split_timestamp = re.split(r"[+|-]",conformed_timestamp)
main_timestamp = split_timestamp[0]
if len(split_timestamp) == 3:
    sign = split_timestamp[1]
    offset = split_timestamp[2]
else:
    sign = None
    offset = None

# generate the datetime object without the offset at UTC time
output_datetime = datetime.datetime.strptime(main_timestamp +"Z", "%Y%m%dT%H%M%S.%fZ" )
if offset:
    # create timedelta based on offset
    offset_delta = datetime.timedelta(hours=int(sign+offset[:-2]), minutes=int(sign+offset[-2:]))
    # offset datetime with timedelta
    output_datetime = output_datetime + offset_delta

2

2.X 표준 라이브러리에서 작동하는 것을 찾으려면 다음을 시도하십시오.

calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))

calendar.timegm은 time.mktime의 누락 된 gm 버전입니다.


1
이것은 '2013-01-28T14 : 01 : 01.335612-08 : 00'시간대를 무시합니다.-> PDT가 아닌 UTC로 파싱
gatoatigrado

2

python-dateutil은 유효하지 않은 날짜 문자열을 구문 분석하는 경우 예외를 발생 시키므로 예외를 잡을 수 있습니다.

from dateutil import parser
ds = '2012-60-31'
try:
  dt = parser.parse(ds)
except ValueError, e:
  print '"%s" is an invalid date' % ds

2

요즘 인기있는 Requests : HTTP for Humans ™ 패키지 작성자 의 Maya : Datetimes for Humans ™ 가 있습니다.

>>> import maya
>>> str = '2008-09-03T20:56:35.450686Z'
>>> maya.MayaDT.from_rfc3339(str).datetime()
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=<UTC>)

2

ISO-8601에 특수 파서를 사용하는 또 다른 방법은 dateutil 파서의 isoparse 기능 을 사용 하는 것입니다.

from dateutil import parser

date = parser.isoparse("2008-09-03T20:56:35.450686+01:00")
print(date)

산출:

2008-09-03 20:56:35.450686+01:00

이 함수는 표준 파이썬 함수 datetime.fromisoformat 에 대한 문서 에서도 언급됩니다 .

보다 완전한 기능을 갖춘 ISO 8601 파서 인 dateutil.parser.isoparse는 타사 패키지 dateutil에서 사용할 수 있습니다.


1

Mark Amery의 답변 덕분에 가능한 모든 ISO 시간 형식의 날짜 시간을 설명하는 기능을 고안했습니다.

class FixedOffset(tzinfo):
    """Fixed offset in minutes: `time = utc_time + utc_offset`."""
    def __init__(self, offset):
        self.__offset = timedelta(minutes=offset)
        hours, minutes = divmod(offset, 60)
        #NOTE: the last part is to remind about deprecated POSIX GMT+h timezones
        #  that have the opposite sign in the name;
        #  the corresponding numeric value is not used e.g., no minutes
        self.__name = '<%+03d%02d>%+d' % (hours, minutes, -hours)
    def utcoffset(self, dt=None):
        return self.__offset
    def tzname(self, dt=None):
        return self.__name
    def dst(self, dt=None):
        return timedelta(0)
    def __repr__(self):
        return 'FixedOffset(%d)' % (self.utcoffset().total_seconds() / 60)
    def __getinitargs__(self):
        return (self.__offset.total_seconds()/60,)

def parse_isoformat_datetime(isodatetime):
    try:
        return datetime.strptime(isodatetime, '%Y-%m-%dT%H:%M:%S.%f')
    except ValueError:
        pass
    try:
        return datetime.strptime(isodatetime, '%Y-%m-%dT%H:%M:%S')
    except ValueError:
        pass
    pat = r'(.*?[+-]\d{2}):(\d{2})'
    temp = re.sub(pat, r'\1\2', isodatetime)
    naive_date_str = temp[:-5]
    offset_str = temp[-5:]
    naive_dt = datetime.strptime(naive_date_str, '%Y-%m-%dT%H:%M:%S.%f')
    offset = int(offset_str[-4:-2])*60 + int(offset_str[-2:])
    if offset_str[0] == "-":
        offset = -offset
    return naive_dt.replace(tzinfo=FixedOffset(offset))

0
def parseISO8601DateTime(datetimeStr):
    import time
    from datetime import datetime, timedelta

    def log_date_string(when):
        gmt = time.gmtime(when)
        if time.daylight and gmt[8]:
            tz = time.altzone
        else:
            tz = time.timezone
        if tz > 0:
            neg = 1
        else:
            neg = 0
            tz = -tz
        h, rem = divmod(tz, 3600)
        m, rem = divmod(rem, 60)
        if neg:
            offset = '-%02d%02d' % (h, m)
        else:
            offset = '+%02d%02d' % (h, m)

        return time.strftime('%d/%b/%Y:%H:%M:%S ', gmt) + offset

    dt = datetime.strptime(datetimeStr, '%Y-%m-%dT%H:%M:%S.%fZ')
    timestamp = dt.timestamp()
    return dt + timedelta(hours=dt.hour-time.gmtime(timestamp).tm_hour)

문자열이로 끝나지 않으면을 Z사용하여 구문 분석 할 수 %z있습니다.


0

처음에는 다음과 같이 시도했습니다.

from operator import neg, pos
from time import strptime, mktime
from datetime import datetime, tzinfo, timedelta

class MyUTCOffsetTimezone(tzinfo):
    @staticmethod
    def with_offset(offset_no_signal, signal):  # type: (str, str) -> MyUTCOffsetTimezone
        return MyUTCOffsetTimezone((pos if signal == '+' else neg)(
            (datetime.strptime(offset_no_signal, '%H:%M') - datetime(1900, 1, 1))
          .total_seconds()))

    def __init__(self, offset, name=None):
        self.offset = timedelta(seconds=offset)
        self.name = name or self.__class__.__name__

    def utcoffset(self, dt):
        return self.offset

    def tzname(self, dt):
        return self.name

    def dst(self, dt):
        return timedelta(0)


def to_datetime_tz(dt):  # type: (str) -> datetime
    fmt = '%Y-%m-%dT%H:%M:%S.%f'
    if dt[-6] in frozenset(('+', '-')):
        dt, sign, offset = strptime(dt[:-6], fmt), dt[-6], dt[-5:]
        return datetime.fromtimestamp(mktime(dt),
                                      tz=MyUTCOffsetTimezone.with_offset(offset, sign))
    elif dt[-1] == 'Z':
        return datetime.strptime(dt, fmt + 'Z')
    return datetime.strptime(dt, fmt)

그러나 부정적인 시간대에서는 효과가 없었습니다. 그러나 이것은 Python 3.7.3에서 잘 작동했습니다.

from datetime import datetime


def to_datetime_tz(dt):  # type: (str) -> datetime
    fmt = '%Y-%m-%dT%H:%M:%S.%f'
    if dt[-6] in frozenset(('+', '-')):
        return datetime.strptime(dt, fmt + '%z')
    elif dt[-1] == 'Z':
        return datetime.strptime(dt, fmt + 'Z')
    return datetime.strptime(dt, fmt)

일부 테스트에서는 출력이 마이크로 초의 정밀도에 의해서만 다르다는 점에 유의하십시오. 내 컴퓨터에서 6 자리의 정밀도를 얻었지만 YMMV는 다음과 같습니다.

for dt_in, dt_out in (
        ('2019-03-11T08:00:00.000Z', '2019-03-11T08:00:00'),
        ('2019-03-11T08:00:00.000+11:00', '2019-03-11T08:00:00+11:00'),
        ('2019-03-11T08:00:00.000-11:00', '2019-03-11T08:00:00-11:00')
    ):
    isoformat = to_datetime_tz(dt_in).isoformat()
    assert isoformat == dt_out, '{} != {}'.format(isoformat, dt_out)

내가 왜 그랬는지 물어봐도 frozenset(('+', '-'))될까요? 정상적인 튜플이 같은 ('+', '-')것을 성취 할 수 없어야 합니까?
Prahlad Yeri

물론, 완벽하게 해시 된 조회가 아니라 선형 스캔이 아닙니까?
AT
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.