RFC 3339文字列"2008-09-03T20:56:35.450686Z"
を Python のdatetime
型のように解析する必要があります。
Python 標準ライブラリで見つけstrptime
たのですが、あまり便利ではありません。
これを行う最善の方法は何ですか?
isoparse
python-dateutil の関数python-dateutilパッケージは、問題のdateutil.parser.isoparse
ような RFC 3339 日時文字列だけでなく、RFC 3339 に準拠していない他のISO 8601日時文字列 (UTC オフセットがないものや、日付のみを表します)。
>>> 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)
python-dateutilパッケージにもdateutil.parser.parse
. と比べるとisoparse
、おそらくそれほど厳密ではありませんが、どちらも非常に寛容で、渡された文字列を解釈しようとします。読み間違いの可能性を排除したい場合は、これらの関数のいずれよりも厳密なものを使用する必要があります。 .
datetime.datetime.fromisoformat
dateutil.parser.isoparse
は完全な ISO-8601 形式のパーサーですが、fromisoformat
意図的にそうではありません。この注意事項については、後者の関数のドキュメントを参照してください。(この回答を参照してください)。
Python 2.6以降およびPy3Kでは、%f文字がマイクロ秒をキャッチすることに注意してください。
>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
ここで問題を参照してください
ここでのいくつかの回答 は、質問に示されているように、タイムゾーンを使用して 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 Internet Date/Time Formatでは、すべての日時に UTC オフセットが含まれている必要があり、これらのオフセットはZ
(「ズールー時間」の略) または inまたは+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 オフセットに対応するディレクティブがstrptime
ありstrftime
ません。それらがサポートするディレクティブの完全なリストは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 で表示されるものにすぎません。Python 2 では、さらに単純な理由で失敗します。それは、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 の日付時刻のみをサポートする必要があり、他のタイムゾーン オフセットの日付時刻をサポートする必要がないことが確実でない限り、 strptime
. 代わりに、こちらの回答で説明されている他の多くのアプローチのいずれかを使用してください。
iso8601モジュールを試してください。それはまさにこれを行います。
python.org wiki のWorkingWithTimeページには、他にもいくつかのオプションが記載されています。
あなたが得る正確なエラーは何ですか?以下のようなものでしょうか?
>>> 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)
import re
import datetime
s = "2008-09-03T20:56:35.450686Z"
d = datetime.datetime(*map(int, re.split(r'[^\d]', s)[:-1]))
最近では、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())
Django を使用している場合は、タイム ゾーンを含む、ISO 形式に似た一連の形式を受け入れるdateparse モジュールが提供されます。
Django を使用しておらず、ここに記載されている他のライブラリのいずれも使用したくない場合は、おそらくdateparse の Django ソース コードをプロジェクトに適合させることができます。
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)
ISO 8601 標準のパーサーをコード化し、GitHub に配置しました: https://github.com/boxed/iso8601。この実装は、期間、間隔、定期的な間隔、および Python の datetime モジュールでサポートされている日付範囲外の日付を除いて、仕様のすべてをサポートしています。
テストが含まれています!:P
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
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 で利用できます。
ISO 8601 では、オプションのコロンとダッシュのさまざまなバリエーションが存在することが許可されているため、基本的にCCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
. strptime を使用する場合は、最初にそれらのバリエーションを削除する必要があります。
目標は、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-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" )
%z
(次のように表示されます)、 (UTC)ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'
から手動で時刻をオフセットする必要があります。Z
注記%z
は、システム/Python ビルド タイプ (Jython、Cython など) によって異なる c ライブラリ サポートに依存するため、Python バージョン < 3 のシステムでは機能しない可能性があります。
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.X標準ライブラリで動作するものについては、次を試してください。
calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))
calendar.timegmは、time.mktimeの欠落しているgmバージョンです。
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
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
。