Python の datetime オブジェクトにおけるタイムゾーン情報の扱い

2014年9月21日 17:03

ざっと調べてみただけでも、Python のタイムゾーン情報で苦労している人は少なくないようだ。

ツイートの後半部分でも触れているが、公式ドキュメントによれば、datetime オブジェクトは自身がタイムゾーン情報を持つ場合と持たない場合があり、前者を aware、後者を naive としている。そして、タイムゾーン情報を明示的に指定しない限り、datetime オブジェクトは naive である。

>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2014, 9, 21, 16, 41, 21, 318027)

pytz などを使えば、タイムゾーン情報を明示的に指定することができる。

>>> import pytz
>>> import datetime
>>> datetime.datetime.now(pytz.timezone('Asia/Tokyo'))
datetime.datetime(2014, 9, 21, 16, 47, 19, 748651, tzinfo=<DstTzInfo 'Asia/Tokyo' JST+9:00:00 STD>)

ツイートにも記したように、筆者の場合は pytz を使わずに UTC と JST のクラスを作って対応している。オリジナルではないが、参考までに記しておく。

import datetime

def utc2jst(dt):
  return dt.replace(tzinfo=UtcTzinfo()).astimezone(JstTzinfo())

def jst2utc(dt):
  return dt.replace(tzinfo=JstTzinfo()).astimezone(UtcTzinfo())

class UtcTzinfo(datetime.tzinfo):
  def utcoffset(self, dt): return datetime.timedelta(0)
  def dst(self, dt): return datetime.timedelta(0)
  def tzname(self, dt): return 'UTC'
  def olsen_name(self): return 'UTC'

class JstTzinfo(datetime.tzinfo):
  def utcoffset(self, dt): return datetime.timedelta(hours=9)
  def dst(self, dt): return datetime.timedelta(0)
  def tzname(self, dt): return "JST"
  def olsen_name(self): return 'Tokyo/Asia'

さて、ndb の DatetimeProperty に TimeZone 情報付き(= aware)の datetime オブジェクトが保存できなかった件だが、GAE のドキュメントを紐解いてみたところ、ちゃんと注釈があった。

Note: App Engine clock times are always expressed in coordinated universal time (UTC). This becomes relevant if you use the current date or time (datetime.datetime.now()) as a value or convert between datetime objects and POSIX timestamps or time tuples. However, no explicit time zone information is stored in the Datastore, so if you are careful you can use these to represent local times in any timezone—if you use the current time or the conversions.

つまり、Datastore にはタイムゾーン情報は保存されないってことですね。納得。