[Python] datetime オブジェクト、Unix time、文字列の相互変換まとめ
Python で datetime オブジェクト 、 Unix time、文字列を相互変換する方法はいろいろあります。 中には直感に反するものもあり1、 過去に1度間違えたのに、再度間違えるということもしばしば。
そこで、2度と間違えないように、個人的にベストと思う方法をまとめてみました。
変換には様々な方法があるので、上記が唯一の方法というわけではありませんが、 なるべく、安全かつシンプルになるものを選んだつもりです。
また、変換可能でも、安全では無いと判断したものは矢印を引いていません2。
以下2点は Python のバージョンによっては使用できないので、代替案を掲載しておきました。
datetime.timestamp()
(3.3 以降で使用可)datetime.fromisoformat()
(3.7 以降で使用可)
なお、掲載コードは以下のように import 済みとします。
1 2 | from datetime import datetime import pytz |
1. Unix time と datetime オブジェクトの相互変換
Unix time → datetime (aware)
Unix time から datetime オブジェクトを生成するには、fromtimestamp() を使用します。
この時、tz
は必ず指定するようにしましょう。そうしないと、ローカルのタイムゾーンを適用した native オブジェクト が生成されてしまいます。
例外的に、datetime.now() 等でローカル時間を取得して処理する時は tz
を指定しなくても良いかもしれません。
1 2 3 4 5 6 7 8 9 | datetime.fromtimestamp( 1541030400 , tz = pytz.utc) # 2018-11-01 00:00:00+00:00 datetime.fromtimestamp( 1541030400 , tz = pytz.timezone( 'Asia/Tokyo' )) # 2018-11-01 09:00:00+09:00 datetime.fromtimestamp( 1541030400 ) # native # 2018-11-01 09:00:00 (+09:00 のタイムゾーン設定されているマシンで実行した場合) # 2018-10-31 20:00:00 (-05:00 のタイムゾーン設定されているマシンで実行した場合) |
Unix time → datetime (native)
Unix Time から native オブジェクトを生成したい場合は、utcfromtimestamp() が使えます。
1 2 | datetime.utcfromtimestamp( 1541030400 ) # 2018-11-01 00:00:00 |
直感的には、UTC な aware オブジェクトが生成されて欲しいところ。 間違えないように気をつけましょう。
datetime (aware) → Unix time
datetime オブジェクトから Unix time に変換する場合は、timestamp() を使用します。
この時、native オブジェクトから変換すると、ローカルのタイムゾーンを適用して変換されてしまいます。 こちらも、fromtimestamp() と同様、datetime.now() 等で取得したローカル時間の native オブジェクトを処理する時は良いかもしれませんが、基本的には使わないようにした方が良いでしょう。
1 2 3 4 5 6 7 8 9 10 11 12 | dt = datetime.fromtimestamp( 1541030400 , tz = pytz.utc) int (dt.timestamp()) # 1541030400 dt = datetime.fromtimestamp( 1541030400 , tz = pytz.timezone( 'Asia/Tokyo' )) int (dt.timestamp()) # 1541030400 dt = datetime.utcfromtimestamp( 1541030400 ) # native int (dt.timestamp()) # 1540998000 (+09:00 のタイムゾーン設定されているマシンで実行した場合) # 1541044800 (-05:00 のタイムゾーン設定されているマシンで実行した場合) |
timestamp() が使えない時
timestamp() は Python 3.3 以降で導入されました。 それ以前のバージョンで Unix time に変換したい場合は、以下のように calendar を使うと良いようです。 この時、UTC の aware に変換しないと想定外の値に変換されてしまうので注意が必要です。
1 2 3 4 | import calendar dt = datetime.fromtimestamp( 1541030400 , tz = pytz.timezone( 'Asia/Tokyo' )) calendar.timegm(dt.astimezone(pytz.utc).timetuple()) # 1541030400 |
参考
2. aware と native の相互変換
datetime (aware) → datetime (native)
aware から native にする場合は replace(tzinfo=None)
を使用します。
1 2 3 4 5 | dt = datetime.fromtimestamp( 1541030400 , tz = pytz.timezone( 'Asia/Tokyo' )) # 2018-11-01 09:00:00+09:00 dt.replace(tzinfo = None ) # 2018-11-01 09:00:00 |
datetime (native) → datetime (aware)
native から aware に変換する時は replace(tzinfo=xxx)
を使ってはいけません。
代わりに pytz.localize()
を使用します。
1 2 3 4 5 | dt = datetime.utcfromtimestamp( 1541030400 ) # 2018-11-01 00:00:00 pytz.timezone( 'Asia/Tokyo' ).localize(dt) # 2018-11-01 00:00:00+09:00 |
タイムゾーンは日付によってオフセットが変わるので3、このような仕様になっているのですが、 かなり紛らわしく間違いやすいので注意が必要です。
参考
3. aware のタイムゾーンを変更する
同じ Unix time を持つ別のタイムゾーンに変換するには astimezone() を使用します。
1 2 3 4 5 | dt = datetime.fromtimestamp( 1541030400 , tz = pytz.timezone( 'Asia/Tokyo' )) # 2018-11-01 09:00:00+09:00 dt.astimezone(pytz.timezone( 'America/New_York' )) # 2018-10-31 20:00:00-04:00 |
なお、astimezone(None)
は native に変換するのではなく、ローカルのタイムゾーンに変更します。
最初、メリットがわからなかったのですが、これを使ってローカルタイムゾーンを取得するという技が紹介されていました4。
参考
datetime - Python: Figure out local timezone - Stack Overflow
4. 文字列と datetime オブジェクトの相互変換
書式文字列を使用して、任意の文字列と datetime を相互変換することが出来ます。
書式文字列のディレクティブ一覧は以下
文字列 → datetime
文字列から datetime オブジェクトに変換する場合は strptime() を使用します。
%z
を含む場合は aware になり、含まない場合は native になります。
なお、%Z
(大文字)は strptime()
では使用できないようです。
1 2 3 4 5 | datetime.strptime( "2018/11/01 09:00" , "%Y/%m/%d %H:%M" ) # 2018-11-01 09:00:00 datetime.strptime( "2018/11/01 09:00+0900" , "%Y/%m/%d %H:%M%z" ) # 2018-11-01 09:00:00+09:00 |
datetime → 文字列
datetime から文字列に変換するには strftime() を使用します。
native に対し %z
を指定しても何も表示されません。
1 2 3 4 5 6 7 8 9 10 11 | dt = datetime.utcfromtimestamp( 1541030400 ) # 2018-11-01 00:00:00 dt.strftime( "%Y/%m/%d %H:%M%z" ) # 2018/11/01 00:00 dt = datetime.fromtimestamp( 1541030400 , tz = pytz.timezone( 'Asia/Tokyo' )) # 2018-11-01 09:00:00+09:00 dt.strftime( "%Y/%m/%d %H:%M%z" ) # 2018/11/01 09:00+0900 dt.strftime( "%Y/%m/%d %H:%M(%Z)" ) # 2018/11/01 09:00(JST) |
5. ISO フォーマット文字列と datetime オブジェクトの相互変換
ISO 8601 形式 と datetime の相互変換も提供されています。
ただ、ISO 8601 にも基本と拡張が存在し、拡張形式にしか対応していない様子。 さらに UTC を表す "Z" には対応していないなど、使い勝手はイマイチです。
個人的には strptime() と strftime() を使用した方が柔軟性が高く、便利かと思います。
ISO フォーマット文字列 → datetime オブジェクト
ISO フォーマット文字列から datetime に変換するには fromisoformat() を使用します。
上述のように、拡張形式にしか対応していません5。
1 2 3 | iso_string = "2018-11-01T09:00:00+09:00" datetime.fromisoformat(iso_string) # 2018-11-01 09:00:00+09:00 |
fromisoformat() は Python 3.7 で導入されたので、 それ以前の場合は strptime() で代用することになります。
厄介なのは ISO フォーマットと strptime()
のタイムゾーンの記述方法が異なる点です。
ISO は +09:00
なのに対し、 strptime()
は +0900
でコロンがありません。
あまり良い方法は提供されていないようなので、愚直に置換してやるのがよさそうです
参考
1 2 3 4 5 6 7 8 | iso_string = "2018-11-01T09:00:00+09:00" # タイムゾーンのフォーマットを変更 dt_string = iso_string if ":" ! = iso_string[ - 3 : - 2 ] else iso_string[: - 3 ] + iso_string[ - 2 :] # 2018-11-01T09:00:00+0900 datetime.strptime(dt_string, "%Y-%m-%dT%H:%M:%S%z" ) # 2018-11-01 09:00:00+09:00 |
datetime オブジェクト → ISO フォーマット文字列
datetime から IOSフォーマット文字列に変換するには isoformat() を使用します。
1 2 3 4 5 6 7 8 | dt = datetime.fromtimestamp( 1541030400 , tz = pytz.timezone( 'Asia/Tokyo' )) # 2018-11-01 09:00:00+09:00 dt.isoformat() # 2018-11-01T09:00:00+09:00 dt.isoformat(timespec = 'hours' ) # 2018-11-01T09+09:00 |
timespec
や sep
を用いて多少フォーマットを変更できますが、上述のように全ての ISO 8601 形式をカバーしているわけではないので、使用できるシーンは限られそうです。
6. コンストラクタ
コンストラクタでタイムゾーンを指定すると、native に対し replace()
したのと同等になってしまいます。
コンストラクタで tzinfo
を指定するのではなく、native で作成してから pytz.localize()
で aware に変換するようにしましょう。
1 2 3 4 5 6 7 8 | # こちらが正解 dt = datetime( 2018 , 9 , 1 ) # native pytz.timezone( 'Asia/Tokyo' ).localize(dt) # 2018-09-01 00:00:00+09:00 # 以下のようにコンストラクタで tzinfo を指定すると19分というずれが発生 datetime.datetime( 2018 , 9 , 1 , tzinfo = pytz.timezone( 'Asia/Tokyo' )) # 2018-09-01 00:00:00+09:19 |
参考
0 件のコメント:
コメントを投稿