Django model 遇到datetime 物件比對問題。

系統:

python 2.7.12
Django  1.8.17
OS: linux mint 18.1 Serena

起始:

一開始因為有個model裡面有個欄位是放expire time,用來辨別這個東西是否過期。所以很直覺的就會用datetime來做比對。例如:
from clover.models import food
import datetime
apple = food.objects.get(name='apple')
print(datetime.datetime.now() > apple.expire_time)

這邊只是很單純比較apple 是否過期,也就是說當現在的日期 大於 apple的過期日~就會印出True, 否就印出False !在命令列去執行時會出現錯誤如下:


出現 TypeError:  can't compare offset-naive and offset-aware datetimes  錯誤!
於是乎開始去了解這是什麼問題。

原因:

後來參考了Django 官方網站與Stackoverfollow才又得知一件之前不知道的事情。
由於Django是採用UTC的時區儲存Database有關datetime的資訊,並且是採用time-zone-aware的datetime objects作為內部使用,而且透過轉換這些物件應用在templates與form表單的當前使用者。

再來是關於 offset-naiveoffset-aware 的意義。依照Django 官方的說明是指:
如果這個datetime object擁有tzinfo的屬性並且可以儲存 time zone的資訊就稱作aware
反之naive

這時候我們可使用Django 內建的is_aware() 與is_naive()做測試。

from django.utils import timezone
import datetime
timezone.is_aware(datetime.datetime.now())    <= False
從這邊可以發現透過系統提供的datetime本身不是aware,所以Django會建議你安裝pytz來做轉換。

解決方式:

簡單的作法是採用Django內建timezone來使用。
例如:
from django.utils import timezone
from clover.models import food
import datetime
apple = food.objects.get(name='apple')
print(timezone.now() > apple.expire_time)    <= 不會有錯誤訊息
這樣子就不出錯了!但是要注意的是使用Django 內建的timezone 必須在設定檔內要開啟 USE_TZ = True 以預設的設定檔是在你專案下的settings.py這個檔案。


另外還可以搭配原本系統的datetime.timedelta 來做時間的位移。
例如:
from django.utils import timezon
import datetime
after_a_day = timezone.now()+ datetime.timedelta(days=1)
print(after_a_day)               <= 印出一天後的時間
timezone.is_aware(after_a_day)   <=  True
基本上我的需求大概只有這些,如果需要更複雜的行為與動作建議採用Django官網上面的 pytz套件來使用。

Reference:

留言

  1. 作者已經移除這則留言。

    回覆刪除
  2. sqlite3 是直接用 pytz 沒錯,但 MySQL 要先執行這行來匯入時區資訊 (django queryset 裡有提到這個): mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql

    https://dev.mysql.com/doc/refman/5.7/en/mysql-tzinfo-to-sql.html

    回覆刪除

張貼留言