GoogleAppEngineでの、日本時間(JST)表示で微妙にハマった

GoogleAppEngineでは基本的にUTC(協定世界時)を使いますが、Web画面上に日時を表示したいので、JST(日本標準時)に変換する必要がありました。

いろいろ調べてみたら以下のやり方があって、

・カスタム datetime.tzinfo を書く
・ライブラリpython-dateutil を使って変換する
・ライブラリpytz を使って変換する
・Djangoテンプレートで変換する

すべてを試したわけでないんだけど、それぞれについて書いてみたいと思います。
(ちなみに、僕は最終的にdateutilを採用することにしました。)

僕が使っている環境
Windows7(64Bit)
Python 2.5.4
Google App Engine SDK for Python 1.7.1
Aptana Studio 3.2.2


(1)はじめに
まず僕がやろうとしていることを簡単に。

GoogleAppEngine上でPythonを使って、Twitterのトレンド情報を取ってみました。
取得したトレンド情報は、いったんDataStoreに保存しておきます。

でそのあとに、DataStoreからデータを取出して、ブラウザ上で一覧表示できるようにしました。
取得した情報には、集計時間がTimeZone UTCで記載されています。この集計時間を、TimeZone JSTに変換して表示させたい。

って感じ。


(2)カスタム datetime.tzinfo を実装してみる
GAEのマニュアルを見ると、カスタム datetime.tzinfo を実装すればいいって書いてあったので、とりあえずそれを試してみました。

(参考)
型とプロパティ クラス – Google App Engine — Google Developers
5.1.6 tzinfo Objects

が、マニュアルに記載されているサンプル内容(class Pacific_tzinfo(datetime_module.tzinfo))がさっぱりよくわからなかったので、こちらのページを参考にして、Class定義してみました。
(参考)例のあれ(仮題)- Google App EngineのタイムゾーンがUTCなので。

実際は参考にしたというより、そのままコピペしたんだけど、動作確認してみたコードを以下に載せておきます。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import datetime

#########################

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 'Asia/Tokyo'

#################

print '-- 現在時刻を表示 ' + '-' * 20

print u'tzinfo なし=%s' % datetime.datetime.now()
print u'tzinfo JST=%s' % datetime.datetime.now(JstTzinfo())
print u'tzinfo UTC=%s' % datetime.datetime.now(UtcTzinfo())

print '-- JSTにセットして表示 ' + '-' * 20
dt= datetime.datetime.strptime('2013-02-23T09:59:13Z', "%Y-%m-%dT%H:%M:%SZ")
dt_JST = dt.replace(tzinfo=UtcTzinfo()).astimezone(JstTzinfo())

print 'dt    =%s' % dt
print "dt_JST=%s" % dt_JST

print '=' * 20

###############

時間をセットする処理のところで、日時のフォーマットが「2013-02-23T09:59:13Z」になっているんだけど、あまり気にしないように。
これはTwitterTrendAPI側から返ってくる日時フォーマットに合わせて書いているだけです。
このコードを実行するとこんな感じになります。

-- 現在時刻を表示 --------------------
tzinfo なし=2013-03-02 00:54:29.299000
tzinfo JST=2013-03-02 00:54:29.299000+09:00
tzinfo UTC=2013-03-01 15:54:29.299000+00:00
-- JSTにセットして表示 --------------------
dt    =2013-02-23 09:59:13
dt_JST=2013-02-23 18:59:13+09:00
====================

とりあえずやりたいことはできたけど、やっぱりライブラリ使ったほうがいいかなっておもった。
JST以外のタイムゾーンを使いたくなった時に、いちいちClassを書くのがめんどいし、書き方もよくわからん。


(3)ライブラリpython-dateutil を使ってみる
python-dateutilのダウンロードはこちらから。
python-dateutil 2.1 : Python Package Index

ただし、dateutilのバージョン2.**は、Python3用なので、上記のサイトからこちらのdateutilホームページに行き、python-dateutil-1.5.tar.gz をダウンロードします。
python-dateutil – Labix

ダウンロードしたファイルを展開すると、dateutilというフォルダがあるので、これをGAEのプロジェクトフォルダ直下へコピーして使います。
動作確認してみたコードを以下に載せておきます。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import datetime
import dateutil.tz

#########################

print '-- 現在時刻を表示 ' + '-' * 20

print u'tzinfo なし=%s' % datetime.datetime.now()
print u'tzinfo JST=%s' % datetime.datetime.now(dateutil.tz.gettz('Asia/Tokyo'))
print u'tzinfo UTC=%s' % datetime.datetime.now(dateutil.tz.tzutc())

print '-- JSTにセットして表示 ' + '-' * 20
dt= datetime.datetime.strptime('2013-02-23T09:59:13Z', "%Y-%m-%dT%H:%M:%SZ")
dt_Tokyo = dt.replace(tzinfo=dateutil.tz.tzutc()).astimezone(dateutil.tz.gettz('Asia/Tokyo'))

print 'dt      =%s' % dt
print "dt_Tokyo=%s" % dt_Tokyo

print '=' * 20

日時のフォーマット「2013-02-23T09:59:13Z」は、TwitterTrendAPI側から返ってくる日時フォーマットに合わせて書いています。
このコードを実行するとこんな感じになります。

 
-- 現在時刻を表示 --------------------
tzinfo なし=2013-03-02 01:23:49.375000
tzinfo JST=2013-03-02 01:23:49.637000+09:00
tzinfo UTC=2013-03-01 16:23:49.637000+00:00
-- JSTにセットして表示 --------------------
dt      =2013-02-23 09:59:13
dt_Tokyo=2013-02-23 18:59:13+09:00
====================

Djangoテンプレート側では、以下の書き方でDateTime型のフォーマットを変えられます。

{{dt_Tokyo|date:"Y/n/d G:i T(O)"}}

この場合、2013/3/01 1:07 JST(+0900) って表示されます。

(参考)
オトなか: DjangoでDateTime型をテンプレート内でフォーマッティングする方法
Built-in template tags and filters | Django documentation | Django


(4)ライブラリpytz
とりあえず僕はdateutilを使うことにしたので、pytzは試していません。
ただpytzをGAE上で使うときの注意点があるみたいなので、そのへんを書いておきます。

pytzのダウンロードはこちらからできます。
pytz 2012j : Python Package Index

ただし、上記のもの(pytz 2012j)はGAE上で使うのはダメです。GAE上ではこちらのものを使う必要があります。
gaepytz 2011h : Python Package Index

このgaepytz 2011hのページに記載があるんですが、「pytz 2012jはGAE上でパフォーマンスの問題がある」とのことです。
pytz has a severe performance problem that impedes its usage on Google App Engine.

ためしにダウンロードしてみるとわかるんですが、pytz 2012j のほうは、zoneinfoファイルが500個以上あります。これを毎回、読みにいってしまいパフォーマンスに影響する模様。(GAE上で使うときだけ?問題が起きるのはなぜかなのか、という素朴な疑問がわいたけど、よくわからない)
(gaepytz 2011hのほうは、zoneinfoファイルがzip圧縮されています。)

あと、gaepytz 2011hを使うときは、こんな感じでインポートするとのこと。
from pytz.gae import pytz

pytzの使い方は、このへんのサイトが参考になります。
PythonでJST日付をUTC(GMT)に変換する。: 俺の砂箱
Pythonのタイムゾーンを扱うモジュールpytzインストールメモ | JWAVER


(5)Djangoテンプレートで変換する

DjangoのCustom template filters機能と思うんだけど、こちらにやり方が載っています。

技術メモ: Google App Engineで簡単に日本時間(JST)を扱う方法

自分でタイムゾーンを変換するClass定義しておき、DjangoのTemplateから処理を呼び出しています。

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s