flask入門的教程-日期和時間 Dates and Times

文章轉自 :https://github.com/WapeYang/The-Flask-Mega-Tutorial/blob/master/dateandtime.rst

感謝原作者的付出

轉載時間爲:2014-05-06

日期和時間

善意提醒

對於那些還沒有注意到的讀者,近來項目已經遷移到 github 上,你們可以在這個位置找到代碼:https://github.com/miguelgrinberg/microblog

我已經添加了標籤指向每個教程步​​驟,爲您提供便利。

時間戳的問題

我們 microblog 應用程序已經忽略很長時間的一個方面就是時間和日期的顯示。

到目前爲止,我們信任 Python 本身去渲染在我們 User 和 Post 對象中的 datetime 對象,然而這真不是一個好的解決方案。

考慮如下的例子。我在 2012.12.31 的下午 3:54 寫的這篇文章。我的時區是 PST (或者 UTC-8 如果你更喜歡的話)。在 Python 解釋器運行能得到如下信息:

>>> from datetime import datetime
>>> now = datetime.now()
>>> print now
2012-12-31 15:54:42.915204
>>> now = datetime.utcnow()
>>> print now
2012-12-31 23:55:13.635874

now() 調用返回本地時區的正確的時間,然而 utcnow() 調用返回 UTC 單位的時間。

因此用哪個更好?

如果我們所有的時間戳都使用 now(),我們將會在數據庫中存儲服務器運行本地的時間,然而這會帶來一些問題。

有一天我們想把服務器遷移到不同時區的地方,那麼所有數據庫中的時間戳必須修改成當地的正確時間在服務器重啓之前。

但是這還有一個更重要的問題。對於不同時區的用戶很難清楚知道 blog 的發佈時間因爲他們看到的是在 PST 時區的時間。他們需要提前知道服務器的時區是 PST 才能做出適當的調整。

顯然,這不是一個好的選擇,這是爲什麼開始使用我們的數據庫的時候,我們決定我們總是以 UTC 時區存儲時間戳。

儘管用 UCT 時區標準化了時間戳解決了遷移服務器的問題。但是它解決不了第二個問題,日期和時間在世界上的任何地方都會以 UTC 形式呈獻給用戶。

這也是很令人困惑的。想象下一個在 PST 時區的用戶在下午 3:00 發佈一篇 blog,結果首頁顯示的時間是下午 11:00 或者是 23:00。用戶會不會感到很奇怪啊?

今天,文章的目的是解決日期和時間顯示的問題,使得我們的用戶不要混淆。

用戶特定的時間戳

最明顯解決問題的方案就是爲每一個用戶單獨把時間戳從 UTC 轉換到當地時區。這也允許我們接着在數據庫中使用 UTC 時區。

但是如何知道每一個用戶的當地時區了?

許多網頁都會有一個配置頁,用戶可以指定當地時區。這就需要我們添加一個新頁,新頁上需要一個表單,用戶可以在表單上的下拉框中選擇自己當地的時區。作爲註冊的一部分,用戶第一次訪問頁面的時候,需要被要求輸入他們的時區。

儘管這是一個比較好的解決方案,但是用戶的系統配置中已經配置了時區,讓用戶輸入這樣的信息有些累贅。如果我們能獲取用戶電腦上的時區的話看起來更有效些。

出於安全原因,網頁瀏覽器是不允許從用戶的操作系統中獲取信息。即使這是可能的話,我們需要在 Windows, Linux, Mac, iOS, 以及 Android 上(這還是沒有統計其他類型的操作系統)去查詢時區。

其實網頁瀏覽器知道用戶的時區,並且可以通過標準的 Javascript APIs 導出。在 Web 2.0 世界裏,用戶開啓 Javascript 的假設是安全的(現代的網頁沒有腳本是不能工作的),因此這個方案是有潛力的。

我們有兩種方式通過 Javascript 來利用可用的時區配置:

  • “老派” 的方式就是讓網頁瀏覽器在某時候發送時區信息當用戶第一次登錄到服務器。這可以通過 Ajax 調用,或者 一個 刷新標記。一旦服務器知道了時區會把它保存到用戶的會話中,當模板渲染的時候會去調整時間。
  • “新派” 的方式就是不會在服務器上改變任何東西,會繼續把 UTC 時間戳發送到客戶端瀏覽器上。從 UTC 到當地時區的轉換是通過 Javascript 在客戶端實現的。

兩種方式都不錯,但是第二種更加不錯。瀏覽器是最有能力根據系統時區配置來渲染時間的。最具有吸引力的是,第二種方式有現成的。

介紹 moment.js

Moment.js 是一個小型的,自由的,開源的 Javascript 庫,它能夠渲染日期和時間。它提供了富有想象的格式化選項。

爲了在我們應用程序中使用 moment.js,我們需要在模板中寫一點 Javascript。我們開始從 ISO 8601 時區構建一個 moment 對象。例如,我們可以創建一個 moment 對象像這樣:

moment("2012-12-31T23:55:13 Z")

一旦對象被構建,它能夠被渲染成各種格式的字符串。例如,根據系統時區進行詳細的渲染:

moment("2012-12-31T23:55:13 Z").format('LLLL');

下面就是結果:

Tuesday, January 1 2013 7:55 AM

這裏是一些不同格式渲染的效果:

images/7.jpg

除了提供了 format(),它還提供了 fromNow() 和 calendar():

images/8.jpg

請注意,在所有的例子中,服務器渲染相同的 UTC 時間,在自己的網頁瀏覽器中執行不同的渲染。

我們現在缺少的就是讓 moment 返回的字符串在頁面上可見。實現這個最簡單的方式就是 Javascript 的 document.write 函數:

<script>
document.write(moment("2012-12-31T23:55:13 Z").format('LLLL'));
</script>

整合 moment.js

在我們的應用程序中使用 moment.js 還需要做一些事情。

首先,把下載下來的 moment.min.js 放入到 /app/static/js 文件夾,這樣它就能作爲一個靜態文件提供給客戶端。

接着,在我們的基礎模板中添加對這個庫的引用(文件 app/templates/base.html):

<script src="/static/js/moment.min.js"></script>

我們現在在模板中添加 <script> 標籤,用來顯示時間戳。但是與這樣方式不同的,我們將會創建一個 moment.js 封裝,我們能在模版中調用這個封裝。這會節省不少時間如果我們必須修改時間戳渲染代碼,因爲我們已經把它放在一個地方。

我們的封裝是一個很簡單的 Python 類(文件 app/momentjs.py):

from jinja2 import Markup

class momentjs(object):
    def __init__(self, timestamp):
        self.timestamp = timestamp

    def render(self, format):
        return Markup("<script>\ndocument.write(moment(\"%s\").%s);\n</script>" % (self.timestamp.strftime("%Y-%m-%dT%H:%M:%S Z"), format))

    def format(self, fmt):
        return self.render("format(\"%s\")" % fmt)

    def calendar(self):
        return self.render("calendar()")

    def fromNow(self):
        return self.render("fromNow()")

注意 render 方法並不直接返回字符串而是把它放入了 Jinja2 提供的 Markup 對象中。原因是 Jinja2 默認情況下會自動轉義,例如,我們的 <script> 標籤是不可能到達到客戶端,因爲轉義成 &lt;script&gt;。把字符串包裹在 Markup 對象裏就是告訴 Jinja2 這個字符串是不需要轉義的。

既然我們有了一個封裝的類,我們需要跟 Jinja2 綁定,這樣模塊就可以使用它(文件 app/__init__.py):

from momentjs import momentjs
app.jinja_env.globals['momentjs'] = momentjs

上面的代碼就是告訴 Jinja2 導入我們的類作爲所有模板的一個全局變量。

現在我們準備修改模版。在我們應用程序中有兩個地方顯示日期和時間。一個就是用戶信息頁,那裏有最後一次登錄時間。對於這個時間戳,我們將會使用 calendar() 格式(文件 app/templates/user.html):

{% if user.last_seen %}
<p><em>Last seen: {{momentjs(user.last_seen).calendar()}}</em></p>
{% endif %}

第二個地方就是在 post 子模板,它是被首頁,用戶信息頁以及搜索頁調用。在這裏我們將會使用 fromNow() 格式,因爲一篇 blog 的撰寫時間和它離現在有多久了是一樣重要的。我們需要修改子模板使得所有使用它的頁面都有效(文件 app/templates/post.html):

<p><a href="{{url_for('user', nickname = post.author.nickname)}}">{{post.author.nickname}}</a> said {{momentjs(post.timestamp).fromNow()}}:</p>
<p><strong>{{post.body}}</strong></p>

做完這些修改後,我們解決了所有我們的時間戳問題。我們不需要對服務器代碼做單個的修改!

結束語

不知不覺中我們已經做了很重要的一步,使得 microblog 讓國際用戶能夠根據本地時區看到不同的日期和時間。在下一章中,我們會讓國際用戶更加高興,我們讓 microblog 支持多語言。

如果你想要節省時間的話,你可以下載 microblog-0.13.zip

我希望能在下一章繼續見到各位!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章