はじめに
ほぼフルリモートでの仕事をするようになり、通勤に時間を費やす必要がなくなりました。その時間を他に使えるのでとても良いのですが、体を動かす機会が減るのと、ポッドキャストを聞く時間が確保できないのが悩みポイントでした。その両方を解決するために毎朝少し離れたスーパー(朝8時開店!)まで歩くのを日課にし始めたのですが、今日聞いていた PythonBytesというポッドキャストで目からウロコなtipsを話していたのでご紹介します。
ポッドキャストで話をしている人たちも「え、そんなことできるの?」「知らなかった」って言ってましたが、 私も聞いていて驚きました。直接聞いてみたい方はEpisode #190の27分ころです。
timedeltaとは
timedelta
はpythonの標準ライブラリに含まれる機能で datetime
モジュールに含まれています。datetime
同士を引き算した時に timedelta
型のデータが返ってきます。
現在時刻を取ってくるdatetime.now()
を二回呼んで、その差をとってみます。
>>> from datetime import datetime, timedelta
>>> d0 = datetime.now()
>>> d1 = datetime.now()
>>> td = d1 - d0
>>> td
datetime.timedelta(seconds=1, microseconds=508102)
差の結果はtimedelta
型のデータで、この場合2つの時刻の差分は 1秒 + 508102マイクロ秒 = 1.508102秒 ということになります。それぞれの値は属性値として取得できます。
>>> td.days
0
>>> td.seconds
1
>>> td.microseconds
508102
ここで、もう少し大きな差分を考えてみましょう。
>>> d0 = datetime(2020,7,31,22,5,0, 113459)
>>> d1 = datetime(2020,8,3,5,55,26, 93450)
>>> d1 - d0
datetime.timedelta(days=2, seconds=28225, microseconds=979991)
7月31日の22時5分0.113459秒から8月3日の5時55分26.093450秒まで。2つのdatetimeを引くだけでその差は2日と28225.979991秒ということがわかります。この例では月をまたいだ日付の差分をとっていて、手でやると面倒で間違いやすそうな計算ですが、datetimeがよろしくやってくれてるのはとても助かります。
timedeltaを時間換算する
このように簡単に計算できるものの、「2日と28225.979991秒」って微妙にわかりにくいですよね。これって何時間なのかな?と思った時にとたんにハードルが上がります。パッと思いつくのは属性値を取り出して計算してみること。
>>> td.days * 24 + (td.seconds + td.microseconds / 1000000 ) / 60 / 60
55.8405499975
あるいは total_seconds()
メソッドを使えばもう少し簡単になります。
>>> td.total_seconds() / 60 / 60
55.840549997500005
こちらは少し誤差がでますね。
いずれにしても、それほど難しい計算ではないけど、せっかく timedeltaという専用の型があるのにそれを使ってできないというのが悔しい。
でも実はできるんです。それが今日の本題ですが、解はとても単純で「計算したい単位のtimedeltaで割り算する」です。
例えば上の場合だと
>>> td / timedelta (hours=1)
55.8405499975
おおー、凄くないですか?なぜこうなるのかを少しだけ書いておきます。
timedeltaは属性値としては days, seconds, microseconds の3つですが、コンストラクターはそれ以外の時間単位もキーワード引数でサポートしています。
>>> timedelta(minutes=1)
datetime.timedelta(seconds=60)
>>> timedelta(hours=1)
datetime.timedelta(seconds=3600)
>>> timedelta(weeks=1)
datetime.timedelta(days=7)
見ておわかりのように、別の単位で指定された場合は days, seconds, microsecondsに正規化されてtimedeltaの属性として保存されます。
そして、timedelta同士の割り算は2つの時間間隔の比を計算することになり、結果はfloat (浮動小数点型)になります。したがって、「時間」単位であれば timedelta(hours=1)
で割ることで目的の値を得られます。
これの応用で、例えば、「5/6から8/19は何週間かな?」と思った時には
>>> (datetime (2020, 8,19) - datetime (2020, 5, 6)) / timedelta(weeks=1)
15.0
と一発で計算できます。
まとめ
datetimeがPythonに導入されてからだいぶ経ちますが、今までこういうtimedeltaの使い方をしたことがありませんでした。Podcastによると、これは別に裏ワザということでもなくtimedeltaの作者自身が意図した使い方らしいのですが、それなのであればドキュメントに例示しておくとかもう少し広める努力をして欲しかったなと思います(笑)