2010年8月27日 星期五

App Engine 上的時區問題

在 App Engine 上如果要儲存時間,在 model 中就會用 DateTimeProperty, DateProperty 或 TimeProperty 來做為資料型態,比如說:
...
from google.appengine.ext import db

class FooModel(db.Model):
  ...
  ...
  created = db.DateTimeProperty(auto_now_add=True)
DateTimeProperty 來說,它對應到的資料結構是 python 標準函式庫中的 datetime.datetime,不過,當你加上了 auto_now_add=True 這個設定之後,每當建立一個新的 entity 時,Datastore API 就會自動加上「現在時間」,也就是 datetime.datetime.now() 的資料,但是 App Engine 所使用的預設時區是 UTC(也就是 GMT+0),簡單地說,如果你的網站使用者都是台灣的用戶,那就會感覺到這個時間晚了八個小時(因為台灣的時區是 CST,也就是 GMT+8),如果要處理這個問題,這裡提供兩個可能的解決方法:

不要依賴自動產生的時間,DIY

第一個解決方法,就是在產生/儲存 entity 時不要讓 Datastore API 幫你產生現在的時間,每次在產生或是更新 entity 資料時,自己手動產生出正確的時間。在解決問題之前,首先要製作一個台灣時區的 tzinfo,才可以去改變 datetime.datetime 所表示的時區:

...
from datetime import tzinfo, timedelta

class TaiwanTimeZone(tzinfo):
    def utcoffset(self, dt):
        return timedelta(hours=8)

    def tzname(self, dt):
        return 'CST'

    def dst(self, dt):
        return timedelta(hours=0)

接著,在產生或更新 entity 資料時:
...
foo = FooModel(......, 
               created=datetime.datetime.now(TaiwanZone()))
foo.put()
這樣就會用台灣時區(GMT+8)的時間來儲存資料了。

在輸出時用 filter 換掉

另外一個方式,就是在 template 要輸出時再換掉時區,當然還是要準備一個 TaiwanTimeZonetzinfo 類別,然而在儲存資料時,還是使用預設的 UTC 時區去處理,但是可以自訂一個 template filter (詳細的作法請見這篇文章)在 template 中顯示時動態換掉時間及時區:
# get template register
register = webapp.template.create_template_register()

@register.filter
def twtz(value):
    from datetime import datetime, timedelta
    return (value + timedelta(hours=8)).replace(tzinfo=TaiwanTimeZone())
這樣只要在 template 中加上這個 filter 來顯示就可以了。

2 則留言:

  1. 第二段:
    created=datetime.datetime.now(TaiwanZone()))

    是不是應該為
    created=datetime.datetime.now(TaiwanTimeZone()))

    回覆刪除