Pythonで-0400タイムゾーン文字列を使用して日付を解析する方法は?


81

'2009/05/13 19:19:30-0400'という形式の日付文字列があります。以前のバージョンのPythonは、後続のタイムゾーン仕様のstrptimeで%z形式のタグをサポートしていたようですが、2.6.xではそれが削除されたようです。

この文字列を日時オブジェクトに解析する正しい方法は何ですか?

回答:


117

dateutilから解析関数を使用できます。

>>> from dateutil.parser import parse
>>> d = parse('2009/05/13 19:19:30 -0400')
>>> d
datetime.datetime(2009, 5, 13, 19, 19, 30, tzinfo=tzoffset(None, -14400))

このようにして、使用できる日時オブジェクトを取得します。

回答済みのとおり、dateutil2.0はPython 3.0用に作成されており、Python2.xでは機能しません。Python 2.xの場合、dateutil1.5を使用する必要があります。


13
これはdateutil、Pythonを使用する私(2.1)では問題なく機能し2.7.2ます。Python3は必要ありません。pipからインストールする場合、パッケージ名はpython-dateutil。であることに注意してください。
BigglesZX 2013年

47

%z Python3.2以降でサポートされています。

>>> from datetime import datetime
>>> datetime.strptime('2009/05/13 19:19:30 -0400', '%Y/%m/%d %H:%M:%S %z')
datetime.datetime(2009, 5, 13, 19, 19, 30,
                  tzinfo=datetime.timezone(datetime.timedelta(-1, 72000)))

以前のバージョンの場合:

from datetime import datetime

date_str = '2009/05/13 19:19:30 -0400'
naive_date_str, _, offset_str = date_str.rpartition(' ')
naive_dt = datetime.strptime(naive_date_str, '%Y/%m/%d %H:%M:%S')
offset = int(offset_str[-4:-2])*60 + int(offset_str[-2:])
if offset_str[0] == "-":
   offset = -offset
dt = naive_dt.replace(tzinfo=FixedOffset(offset))
print(repr(dt))
# -> datetime.datetime(2009, 5, 13, 19, 19, 30, tzinfo=FixedOffset(-240))
print(dt)
# -> 2009-05-13 19:19:30-04:00

ここFixedOffsetに基づいてクラスであるドキュメントからのコードの例は

from datetime import timedelta, tzinfo

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)

1
これにより、ValueError: 'z' is a bad directive in format '%Y-%m-%d %M:%H:%S.%f %z'私の場合(Python 2.7)が発生します。
ジョナサンH

@Sheljohn Python2.7では動作しないはずです答えの一番上を見てください。
jfs 2017年

これがすべてではないが、Pythonの上で言及されていることを、方法によって、奇妙2.7:ドキュメントdocs.python.org/2.7/library/...
62mkv

22

"%z"Python2.7以前の問題の修正は次のとおりです

使用する代わりに:

datetime.strptime(t,'%Y-%m-%dT%H:%M %z')

次のように、timedeltaを使用してタイムゾーンを説明します。

from datetime import datetime,timedelta
def dt_parse(t):
    ret = datetime.strptime(t[0:16],'%Y-%m-%dT%H:%M')
    if t[18]=='+':
        ret-=timedelta(hours=int(t[19:22]),minutes=int(t[23:]))
    elif t[18]=='-':
        ret+=timedelta(hours=int(t[19:22]),minutes=int(t[23:]))
    return ret

日付はに変換されることに注意してくださいGMT。これにより、タイムゾーンを気にせずに日付の計算を実行できます。


'seconds ='を 'minutes ='に変更する必要がありますが、私はこれが好きです。
デイブ

1
注意点として、文字列でタイムゾーンを取得し、日時をUTCに変換する場合は、ここにリストされている反対のロジックを使用します。タイムゾーンに+がある場合は、タイムデルタを減算します。その逆も同様です。
セクター95 2017年

ある場合はUTCへの変換は、間違っていた+はtimedeltaをしなければならない文字引き算、およびその逆。コードを編集して修正しました。
tomtastico 2017

7

dateutilを使用しての問題はdateutilはオプション(のみの書式限られているあなたは、シリアライズとデシリアライズの両方に同じフォーマット文字列を持つことができないということであるdayfirstyearfirst)。

私のアプリケーションでは、フォーマット文字列を.INIファイルに保存し、各デプロイメントは独自のフォーマットを持つことができます。したがって、私はdateutilアプローチが本当に好きではありません。

代わりにpytzを使用する別の方法を次に示します。

from datetime import datetime, timedelta

from pytz import timezone, utc
from pytz.tzinfo import StaticTzInfo

class OffsetTime(StaticTzInfo):
    def __init__(self, offset):
        """A dumb timezone based on offset such as +0530, -0600, etc.
        """
        hours = int(offset[:3])
        minutes = int(offset[0] + offset[3:])
        self._utcoffset = timedelta(hours=hours, minutes=minutes)

def load_datetime(value, format):
    if format.endswith('%z'):
        format = format[:-2]
        offset = value[-5:]
        value = value[:-5]
        return OffsetTime(offset).localize(datetime.strptime(value, format))

    return datetime.strptime(value, format)

def dump_datetime(value, format):
    return value.strftime(format)

value = '2009/05/13 19:19:30 -0400'
format = '%Y/%m/%d %H:%M:%S %z'

assert dump_datetime(load_datetime(value, format), format) == value
assert datetime(2009, 5, 13, 23, 19, 30, tzinfo=utc) \
    .astimezone(timezone('US/Eastern')) == load_datetime(value, format)

2

そこにある古いPython用の1つのライナー。次のように、+ /-符号に応じて、タイムデルタに1 / -1を掛けることができます。

datetime.strptime(s[:19], '%Y-%m-%dT%H:%M:%S') + timedelta(hours=int(s[20:22]), minutes=int(s[23:])) * (-1 if s[19] == '+' else 1)

-10

Linuxを使用している場合は、外部dateコマンドを使用して次のように操作できます。

import commands, datetime

def parsedate(text):
  output=commands.getoutput('date -d "%s" +%%s' % text )
  try:
      stamp=eval(output)
  except:
      print output
      raise
  return datetime.datetime.frometimestamp(stamp)

もちろん、これはdateutilよりも移植性が低くなりますが、date「昨日」や「昨年」などの入力も受け入れるため、少し柔軟性があります:-)


3
このために外部プログラムを呼び出すのは良いことではないと思います。そして次の弱点:eval():Webサーバーがこのコードを実行するようになった場合、サーバー上で任意のコードを実行できます。
guettli 2011年

5
それはすべてコンテキストに依存します。私たちが
求めているのが書き込みと破棄の

10
これに反対票を投じる理由は、1)些細なことをシステムコールする、2)シェル呼び出しに直接文字列を挿入する、3)eval()を呼び出す、4)例外キャッチオールがあるためです。基本的に、これは物事を行わない方法の例です。
2014

この場合、evalは悪であり、使用すべきではありませんが。外部呼び出しは、タイムゾーンが数値オフセットではないタイムゾーン対応の日付文字列からUNIXタイムスタンプを取得するための最も簡単で実用的な方法のようです。
レリエル2016

1
繰り返しになりますが、この「eval is evil」のモットーは、実際にはコンテキストによって異なります(OPでは説明されていません)。自分で使用するスクリプトを作成するときは、evalを自由に使用しますが、それはすばらしいことです。Pythonはグルースクリプトに最適な言語です!もちろん、上記のいくつかの回答のように、複雑な一般的なケースの過剰設計ソリューションを展開して、それがJavaの唯一の正しいやり方であると主張することもできます。しかし、多くのユースケースでは、迅速で汚い解決策も同様に優れています。
Gyom 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.