1. ホーム
  2. python

[解決済み] Pythonでタイムゾーンを意識したdatetimeオブジェクトを作るには?

2022-03-15 07:30:12

質問

必要なもの

タイムゾーンを認識しないdatetimeオブジェクトがあり、他のタイムゾーンを認識するdatetimeオブジェクトと比較できるようにするためにタイムゾーンを追加する必要があります。このレガシーなケースのために、アプリケーション全体をタイムゾーン非対応に変換することはしたくありません。

試してみたこと

まず、問題を実証するために

Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> import pytz
>>> unaware = datetime.datetime(2011,8,15,8,15,12,0)
>>> unaware
datetime.datetime(2011, 8, 15, 8, 15, 12)
>>> aware = datetime.datetime(2011,8,15,8,15,12,0,pytz.UTC)
>>> aware
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> aware == unaware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes

まず、astimezoneを試してみました。

>>> unaware.astimezone(pytz.UTC)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: astimezone() cannot be applied to a naive datetime
>>>

実際に変換を行おうとしているのだから、これが失敗するのは驚くことではありません。 置換したほうがよさそうだ(たとえば Pythonでdatetime.day()の値を"timezone aware"で取得するにはどうしたらいいですか? ):

>>> unaware.replace(tzinfo=pytz.UTC)
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> unaware == aware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
>>> 

しかし、ご覧のように、replaceはtzinfoを設定するようですが、オブジェクトを認識させることはできません。 私は、入力文字列を解析する前にタイムゾーンを持たせるために加工する準備をしています(それが重要なら、私は解析にdateutilを使っています)、しかし、それは信じられないほど不器用なようです。

また、Python2.6とPython2.7の両方で試しましたが、同じ結果でした。

コンテキスト

私は、いくつかのデータファイルのパーサーを書いています。日付文字列にタイムゾーンインジケータがない古い形式をサポートする必要があります。データソースはすでに修正しましたが、古いデータ形式をまだサポートする必要があります。レガシーデータを一回で変換することは、ビジネス上の様々な理由により、選択肢にはありません。一般的に、デフォルトのタイムゾーンをハードコーディングするのは好きではありませんが、このケースではそれが最良の選択肢のように思われます。私は、問題のレガシーデータがすべてUTCであることを確信しているので、この場合、デフォルトでUTCにするリスクを受け入れる用意があるのです。

解決方法は?

一般に、素朴な日付時刻をタイムゾーンに対応させるためには ローカライズメソッド :

import datetime
import pytz

unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)

now_aware = pytz.utc.localize(unaware)
assert aware == now_aware

UTCタイムゾーンの場合、実際には localize サマータイムの計算をする必要がないためです。

now_aware = unaware.replace(tzinfo=pytz.UTC)

が動作します。( .replace は新しいdatetimeを返します。 unaware .)