私は RFC 3339"2008-09-03T20:56:35.450686Z"
のような文字列をPythonのdatetime
型に解析する必要があります。
Python標準ライブラリに strptime
がありますが、あまり便利ではありません。
これを行うための最良の方法は何ですか?
python-dateutil パッケージは、問題になっているもののようなRFC 3339日時文字列だけでなく、RFC 3339に準拠していない他の ISO 8601 日時文字列も解析できます。 UTCオフセットのないものや日付のみを表すものなど).
>>> import dateutil.parser
>>> dateutil.parser.parse('2008-09-03T20:56:35.450686Z') # RFC 3339 format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())
>>> dateutil.parser.parse('2008-09-03T20:56:35.450686') # ISO 8601 extended format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.parse('20080903T205635.450686') # ISO 8601 basic format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.parse('20080903') # ISO 8601 basic format, date only
datetime.datetime(2008, 9, 3, 0, 0)
dateutil.parser
は意図的にハッキングされていることに注意してください。それはフォーマットを推測しようとし、あいまいな場合に避けられない仮定(手動でのみカスタマイズ可能)を作ります。そのため、未知のフォーマットの入力を解析する必要があり、時折の誤読を許容するのに問題がない場合にのみ使用してください。 (ありがとう ivan_pozdeev )
Pypi名は python-dateutil
であり、dateutil
ではありません(ありがとう code3monk3y )。
pip install python-dateutil
Python 3.7を使っているなら、datetime.datetime.fromisoformat
について この回答 をご覧ください。
Python 2.6以降とPy3Kでは、%f文字はマイクロ秒をキャッチします。
>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
Issueを参照してください ここ
datetime
標準ライブラリはdatetime.isoformat()
を反転するための関数を導入しました。
classmethod
datetime.fromisoformat(date_string)
:
date.isoformat()
およびdatetime.isoformat()
が出力する形式の1つで、date_string
に対応するdatetime
を返します。具体的には、この関数はフォーマットの文字列をサポートします。
YYYY-MM-DD[*HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]]]
*
は任意の1文字に一致します。注意 :これは任意のISO 8601文字列の解析をサポートしていません - これは
datetime.isoformat()
の逆演算としてのみ意図されています。
使用例
from datetime import datetime
date = datetime.fromisoformat('2017-01-01T12:30:59.000000')
iso8601 モジュールを試してください。まさにこれです。
Python.org wikiの WorkingWithTime ページには他にもいくつかのオプションがあります。
import re、datetime s = "2008-09-03T20:56:35.450686Z" d = datetime.datetime(* map(int、re.split( '[ ^\d] '、s)[: - 1]))
正確なエラーは何ですか?次のようなものですか?
>>> 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)
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')
最近では、 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())
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)
Djangoを使っているのであれば、タイムゾーンを含むISOフォーマットに似た多くのフォーマットを受け付ける dateparseモジュール が提供されています。
Djangoを使用しておらず、ここで言及している他のライブラリを使用したくない場合は、おそらく dateparse用のDjangoソースコード をプロジェクトに適合させることができます。
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())
私は ciso8601 がISO 8601タイムスタンプをパースする最速の方法であることを発見しました。名前が示すように、それはCで実装されています。
import ciso8601
ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')
GitHub Repo README は、他の回答に記載されている他のすべてのライブラリと比較して、10倍以上の高速化を示しています。
私の個人的なプロジェクトは、ISO 8601の解析をたくさん含んでいました。通話を切り替えて10倍速くなることができてよかったです。 :)
編集: ciso8601のメンテナになりました。今まで以上に高速です。
サードパーティ製のモジュールをインストールせずに、サポートされているすべてのPythonバージョンでISO 8601風の日付文字列をUNIXタイムスタンプまたはdatetime.datetime
オブジェクトに変換する簡単な方法の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規格のパーサをコーディングしてGitHubに置きました: https://github.com/boxed/iso8601 。この実装は、期間、間隔、定期的な間隔、そしてPythonのdatetimeモジュールのサポートされている日付範囲外の日付を除いて、仕様のすべてをサポートします。
テストが含まれています! :P
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')
これは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.X標準ライブラリで動作するものについては試してみてください。
calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))
calendar.timegmはtime.mktimeの不足しているGMバージョンです。
今日では Maya:Humans™の日付時刻 、人気のRequests:HTTP 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>)
無効な日付文字列を解析すると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
/のおかげで Mark Ameryの答え 私はdatetimeのすべての可能な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))
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
を使用して解析できます。
最初に試してみました:
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)