ISO 8601形式の日付を解析するにはどうすればよいですか?


643

RFC 3339文字列"2008-09-03T20:56:35.450686Z"をPythonのdatetime型のように解析する必要があります。

私はstrptimePython標準ライブラリで見つけましたが、あまり便利ではありません。

これを行う最良の方法は何ですか?




3
明確にするために:ISO 8601が主要な標準です。RFC 3339はISO 8601の自己宣言された「プロファイル」であり、ISO 8601ルールの賢明でない上書きをいくつか行います。
バジルブルク

3
isoformat()を反転するための以下のpython3.7 +ソリューションをお見逃しなく
Brad M

2
この質問は、リンクされた投稿の説明と同じように閉じないでください。これはISO 8601時間文字列(3.7より前のpythonではネイティブにサポートされていなかった)を解析することを求めているため、もう1つは廃止されたメソッドを使用して日時オブジェクトをエポック文字列にフォーマットすることです
abccd '19

回答:


462

python-dateutilパッケージは、問題の1のようにだけでなく、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(thanks code3monk3y):

pip install python-dateutil

Python 3.7を使用している場合は、に関するこの回答をご覧くださいdatetime.datetime.fromisoformat


75
怠惰な場合、それpython-dateutilはnot を介してインストールされるためdateutil、次のようになりますpip install python-dateutil
cod3monk3y 2014年

29
dateutil.parserは意図的にハッキングされていることに注意してください。これは形式を推測しようとし、あいまいな場合には避けられない仮定(手動でのみカスタマイズ可能)を作成します。したがって、不明な形式の入力を解析する必要があり、時々の誤読を許容できる場合にのみ使用してください。
ivan_pozdeev 2015

2
同意した。たとえば、「日付」9999を渡すとします。これは、datetime(9999、現在の月、現在の日)と同じ結果を返します。私の見解では有効な日付ではありません。
timbo 2016年

1
@ivan_pozdeev推測しない解析にはどのパッケージをお勧めしますか?
bgusach '10年

2
@ivan_pozdeev ISO8601の日付を読み込み、モジュールへのアップデートがあります:dateutil.readthedocs.io/en/stable/...
theEpsilon

198

Python 3.7以降の新機能


datetime標準ライブラリは反転するための機能を導入しましたdatetime.isoformat()

classmethod datetime.fromisoformat(date_string)

and によって発行されたいずれかの形式でaにdatetime対応するを返しdate_stringますdate.isoformat()datetime.isoformat()

具体的には、この関数は次の形式の文字列をサポートしています。

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

where *は任意の1文字に一致します。

注意:これは、任意のISO 8601文字列の解析をサポートしていません。これは、の逆の操作としてのみ意図されていdatetime.isoformat()ます。

使用例:

from datetime import datetime

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

6
それは変だ。にはdatetimeが含まれている可能性があるため、tzinfoタイムゾーンが出力されますがdatetime.fromisoformat()、tzinfoは解析されません。バグのようです..
ヘンディイラワン

20
ドキュメントでその注記をお見逃しなく。これは、すべての有効なISO 8601文字列を受け入れるわけではなく、によって生成されisoformatたものだけを受け入れます"2008-09-03T20:56:35.450686Z"末尾Zが原因で問題の例を受け入れませんが、受け入れ"2008-09-03T20:56:35.450686"ます。
Flimm 2018

26
Z入力スクリプトを適切にサポートするには、を使用して変更できますdate_string.replace("Z", "+00:00")
jox

7
秒では、小数点以下0、3、または6桁のみを処理することに注意してください。入力データの小数点以下の桁数が1、2、4、5、7以上の場合、解析は失敗します!
Felk

1
@JDOaktownこの例では、dateutilのパーサーではなく、ネイティブPythonのdatetimeライブラリを使用しています。このアプローチでは、小数点以下が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は何にも一致しない可能性があります。
ダニーステープル2015

24
この回答(現在の編集された形式)は、特定のUTCオフセット(つまり、「Z」、つまり+00:00を意味します)をフォーマット文字列にハードコーディングすることに依存しています。異なるUTCオフセットで日時を解析できず、例外が発生するため、これは悪い考えです。RFC 3339での解析が実際には不可能である方法を説明する私の回答を参照しくださいstrptime
Mark Amery 2015年

1
私の場合、%fはZではなくマイクロ秒をキャッチしたdatetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f') ため、これでうまく
いきました

Py3KはPython 3000を意味しますか?
ロビノ2017年

2
@Robino IIRC、「Pythonの3000は、」今はPython 3として知られているものの古い名前です
アカウントアウェイスロー

161

ここでいくつかの回答 は、質問に示されているもののように、タイムゾーンを使用してRFC 3339またはISO 8601の日時を解析すること提案しています。 datetime.datetime.strptime

2008-09-03T20:56:35.450686Z

これは悪い考えです。

ゼロ以外の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

悲しいかな、RFC 3339形式のUTCオフセットに対応するディレクティブを使用しstrptimestrftimeいるディレクティブはありません。それらがサポートするディレクティブの完全なリストはhttps://docs.python.org/3/library/datetime.html#strftime-and-strptime-behaviorにあり、リストに含まれる唯一のUTCオフセットディレクティブは%z次のとおりです。

%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で表示されるものにすぎません。Python2では、さらに単純な理由で失敗します。これは、Python 2でディレクティブをまったく実装しstrptimeいないため%zです。です。)

質問者の例の日時文字列のと一致strptimeするリテラルZをフォーマット文字列に含めることにより、これを回避することをすべて推奨するここでの複数の回答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オフセットをフォーマット文字列ハードコーディングする必要があるため、別のUTCオフセットを使用してRFC 3339の日時を解析しようとすると、次のようになります。

>>> 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 3339の日時をZulu時間でのみサポートする必要があり、他のタイムゾーンオフセットの日時をサポートする必要がないことが確実でない限り、は使用しないでくださいstrptime。代わりに、ここで回答に記載されている他の多くのアプローチの1つを使用してください。


79
strptimeにISO形式のタイムゾーン情報のディレクティブがないのはなぜか、なぜ解析できないのか、とんでもありません。信じられない。
Csaba Toth

2
@CsabaToth完全に同意しました-殺す時間があれば、それを言語に追加しようと思います。あるいは、そうしたいのであれば、そうすることができます。私とは異なり、Cの経験があると思います。
Mark Amery

1
@CsabaToth-なぜ信じられない?それはほとんどの人にとって十分にうまく機能するか、彼らは十分に簡単な回避策を見つけました。この機能が必要な場合はオープンソースであり、追加できます。または、あなたに代わって誰かに支払います。なぜ誰かがあなたの特定の問題を解決するために自分の自由時間を志願する必要があるのですか?ソースを一緒にしましょう。
Peter

2
@PeterMasiar信じられないほどです。通常、Pythonの機能が慎重かつ完全に実装されていることに気づくからです。私たちはこの細部へのこだわりに甘やかされてきたので、「unpythonic」である言語で何かに出会ったとき、私は今そうしようとしているときにおもちゃを乳母車から投げ出します。Whaaaaaaaaaa Whaa wahaaaaa :-(
Robino

2
strptime()Python 3.7では、この回答で不可能と記述されているすべてをサポートするようになりました(タイムゾーンオフセットの「Z」リテラルと「:」)。残念ながら、RFC 3339がISO 8601と根本的に互換性がない別のコーナーケースがあります。つまり、前者は負のnullタイムゾーンオフセット-00:00を許可し、後者は許可しません。
セルギコレスニコフ2018年

75

iso8601モジュールを試してください。それはまさにこれを行います。

python.org wikiのWorkingWithTimeページで言及されている他のいくつかのオプションがあります。


シンプルiso8601.parse_date("2008-09-03T20:56:35.450686Z")
Pakman

3
問題は、「ISO 8601の日付を解析する方法」ではなく、「この正確な日付形式を解析する方法」でした。
ニコラスライリー

3
@tiktak OPは「Xのような文字列を解析する必要があります」と尋ね、両方のライブラリを試した結果、iso8601にはまだ未解決の重要な問題があるため、別のライブラリを使用するという返事がありました。そのようなプロジェクトへの私の関与またはその欠如は、答えとはまったく無関係です。
トビア2013年

2
iso8601のpipバージョンは2007年以降更新されておらず、未解決の重大なバグがいくつかあることに注意してください。重要なパッチを自分で適用するか、すでに実行している多数のgithubフォークの1つを見つけることをお勧めします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)を考慮に入れていない限り、これは同意できません。
umbrae

14
かなり読みやすいと思います。実際、これはおそらく、追加のパッケージをインストールせずに変換を行う最も簡単で最もパフォーマンスの高い方法です。
トビア2012年

2
これは、d = datetime.datetime(* map(int、re.split( '\ D'、s)[:-1]))と同等です。

4
バリエーション:datetime.datetime(*map(int, re.findall('\d+', s))
jfs 2014年

3
これはタイムゾーンのない単純なdatetimeオブジェクトになりますよね?それで、UTCビットは翻訳で失われますか?
2014年

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 2008

プレーンなdatetimeオブジェクトにはタイムゾーンの概念がありません。すべての時間が「Z」で終わっている場合、取得するすべての日時はUTC(ズールー時間)です。
tzot 2008

タイムゾーン以外の何かである場合""、または"Z"、直接DateTimeオブジェクトへ/から減算添加することができ、時間/分でオフセットされなければなりません。tzinfoサブクラスを作成してそれを処理することもできますが、それはおそらくお勧めできません。
SingleNegationElimination 2011

8
さらに、「%f」はマイクロ秒指定子であるため、(timezone-naive)strptime文字列は「%Y-%m-%dT%H:%M:%S.%f」のようになります。
quodlibetor 2012

1
これは、指定された日時文字列に「Z」以外のUTCオフセットがある場合、例外を発生させます。RFC 3339形式全体をサポートしておらず、UTCオフセットを適切に処理する他の形式よりも劣っています。
Mark Amery、2015年

25

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
矢印は、適切にかかわらず、ISO8601をサポートしていません:github.com/crsmithdev/arrow/issues/291
箱入り

1
python-dateutilを使用するだけ-矢印にはpython-dateutilが必要です。
ダニゼン

ArrowはISO8601をサポートするようになりました。参照されている問題は現在クローズされています。
Altus

18

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の答えではありませんか?
レオ

1
彼が数秒で解析しているところをどこで見ますか?エポックタイムを取得しようとしてこの記事を見つけたので、他の誰かもそうだと考えました。
Blairg23 2017

1
これではない私のシステム上のUTC。むしろ、秒単位の出力は、日付が私のローカルタイムゾーンであるかのように、UNIXエポック時間です。
エリオット

1
この答えはバグが多く、受け入れるべきではありません。おそらく、質問全体がstackoverflow.com/questions/11743019/…の
tripleee

@tripleee実際にコードをチェックしたところ、正しい答えが返されたようです455051100epochconverter.comでチェック)。
Blairg23、18年

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オフセット(つまり、「Z」、つまり+00:00を意味します)を、に渡されるフォーマット文字列にハードコーディングすることに依存していますstrptime。異なるUTCオフセットで日時を解析できず、例外が発生するため、これは悪い考えです。RFC 3339をstrptimeで解析することが実際にどのように不可能であるかを説明する私の回答を参照しください。
Mark Amery、2015年

1
ハードコードされていますが、zuluのみを解析する必要がある場合に十分です。
サーシャ2015

1
@alexanderはい-たとえば、日付文字列がJavaScriptのtoISOStringメソッドで生成されたことがわかっている場合などです。しかし、この回答では、ズールー語の日付の制限についての言及はありません。また、質問でそれが必要なすべてであることも示されていませんdateutil
Mark Amery

11

Djangoを使用している場合は、タイムゾーンなど、ISO形式に類似した一連の形式を受け入れるdateparseモジュールが提供されます。

Djangoを使用しておらず、ここで説明した他のライブラリを使用したくない場合は、おそらくDjangoのソースコードをdateparse用にプロジェクトに適合させることができます。


Django DateTimeFieldは文字列値を設定するときにこれを使用します。
djvg 2018

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 2018

3
@ hamx0r、datetime.strptime()これは完全なISO 8601解析ライブラリではないことに注意してください。Python 3.7を使用しているdatetime.fromisoformat()場合は、このメソッドを使用できます。このメソッドは少し柔軟性があります。すぐにciso8601 READMEにマージされるパーサーのこのより完全なリストに興味があるかもしれません。
movermeyer

ciso8601はかなりうまく機能しますが、pytz依存関係がないとタイムゾーン情報を含むタイムスタンプを解析できないため、最初に「pip install pytz」を実行する必要があります。例は次のようになります:dob = ciso8601.parse_datetime(result ['dob'] ['date'])
Dirk

2
@ Dirk、Python 2のみ。でもそれでも次のリリースで削除さべきです。
movermeyer 2018

8

これはPython 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オフセット(つまり、「Z」、つまり+00:00を意味します)を、に渡されるフォーマット文字列にハードコーディングすることに依存していますstrptime。異なるUTCオフセットで日時を解析できず、例外が発生するため、これは悪い考えです。RFC 3339をstrptimeで解析することが実際にどのように不可能であるかを説明する私の回答を参照しください。
Mark Amery、2015年

1
理論的には、はい、これは失敗します。実際には、Zulu時間ではないISO 8601形式の日付に出会ったことはありません。私の非常に時折必要なもののために、これはうまく機能し、いくつかの外部ライブラリに依存していません。
ベンジャミンリッグス2015

4
timezone.utc代わりに使用できますtimezone(timedelta(0))。また、コードは2.6+ Pythonで動作します(少なくとも)あなたがあれば提供utctzinfoのオブジェクトを
JFS

遭遇したかどうかは関係ありませんが、仕様と一致していません。
アナウンサー

%Z最新バージョンのPythonでforタイムゾーンを使用できます。
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タイムスタンプまたはオブジェクトに変換する簡単な方法の1つは、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
なんて信じられないほど素晴らしい、素晴らしいハックでしょう!ありがとう!
ハボック

6

ISO 8601標準のパーサーをコード化し、GitHubに配置しました:https : //github.com/boxed/iso8601。この実装は、期間、間隔、定期的な間隔、およびPythonの日時モジュールのサポートされている日付範囲外の日付を除いて、仕様のすべてをサポートします。

テストが含まれています!:P



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オブジェクトを生成することです。


次のようなZサフィックスを持つUTCで機能する基本的なケースだけが必要な場合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-04002008-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)から手動でオフセットする必要があります。%zPythonバージョン3未満のシステムでは、システム/ Pythonビルドタイプ(Jython、Cythonなど)によって異なる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は、不足しているgmバージョンのtime.mktimeです。


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

この関数は、標準のPython関数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.