web-dev-qa-db-ja.com

ISO 8601形式の日付を解析する方法

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

Python標準ライブラリに strptime がありますが、あまり便利ではありません。

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

524

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について この回答 をご覧ください。

370
Flimm

Python 2.6以降とPy3Kでは、%f文字はマイクロ秒をキャッチします。

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

Issueを参照してください ここ

143
sethbc

Python 3.7以降の新機能


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

classmethoddatetime.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')
79
abccd

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

Python.org wikiの WorkingWithTime ページには他にもいくつかのオプションがあります。

69
Nicholas Riley
 import re、datetime 
 s = "2008-09-03T20:56:35.450686Z" 
 d = datetime.datetime(* map(int、re.split( '[ ^\d] '、s)[: -  1]))
34
Ted

正確なエラーは何ですか?次のようなものですか?

>>> 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)
28
tzot

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')
19
Andreas Profous

最近では、 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())
19
Ilker Kesen

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)
12
enchanter

Djangoを使っているのであれば、タイムゾーンを含むISOフォーマットに似た多くのフォーマットを受け付ける dateparseモジュール が提供されています。

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

11
Don Kirkby

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

ドキュメンテーション

11
Blairg23

私は 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のメンテナになりました。今まで以上に高速です。

8
movermeyer

私はiso 8601 utilsの作者です。 GitHub または PyPI にあります。例を解析する方法は次のとおりです。

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

サードパーティ製のモジュールをインストールせずに、サポートされているすべての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
6
Damian Yerrick

私はISO 8601規格のパーサをコーディングしてGitHubに置きました: https://github.com/boxed/iso8601 。この実装は、期間、間隔、定期的な間隔、そしてPythonのdatetimeモジュールのサポートされている日付範囲外の日付を除いて、仕様のすべてをサポートします。

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

6
boxed

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')
5
Artem Vasilev

これは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)
5
Benjamin Riggs

2.X標準ライブラリで動作するものについては試してみてください。

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

calendar.timegmはtime.mktimeの不足しているGMバージョンです。

2
Gordon Wrigley

今日では 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>)
2
jrc

無効な日付文字列を解析すると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
user2646026

/のおかげで 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))
1
omikron
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
Denny Weinberg

最初に試してみました:

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)
0
A T