天天看點

12 Flask mega-tutorial 第12章 日期和時間 Dates and Times時區“地獄”時區轉換介紹Moment.js和Flask-Moment使用Moment.js

如需轉載請注明出處。

win10 64位、Python 3.6.3、Notepad++、Chrome 67.0.3396.99(正式版本)(64 位)

注:作者編寫時間2018-02-21,linux、python 3.5.2

以下内容均是加入自己的了解與增删,以記錄學習過程。不限于翻譯,部分不完全照搬作者Miguel Grinberg的部落格,版權屬于作者,感謝他提供免費學習的資料。

傳送門
00 開篇 01 Hello world 02 模闆 03 Web表單
04 資料庫 05 使用者登入 06 個人資料和頭像 07 錯誤處理
08 關注 09 分頁 10 支援QQ郵箱 11 美化頁面
12 時間和日期 13 I18n和L10n 翻譯成中文 zh-CN 14 Ajax(百度翻譯API 15 更好的App結構(藍圖)
16 全文搜尋 17 部署到騰訊雲Ubuntu 18 部署到Heroku 19 部署到Docker容器
20 JavaScript魔法 21 使用者通知 22 背景工作(Redis) 23 應用程式程式設計接口(API)

本章将學習如何以适合所有使用者的方式使用

日期和時間

,無論他們身居何處。

目前為止,一直忽略Microblog應用程式顯示日期和時間的問題,隻是讓

Python

渲染了

User

模型中的

datetime

對象,并完全忽略

Post

模型中的

datetime

對象。

時區“地獄”

在伺服器上用

Python

去呈現日期和時間,在Web浏覽器上以這種方式渲染給使用者可不是一個好主意。如示例,在Python解釋器中運作如下内容:

(venv) D:\microblog>python
Python 3.6.3 (v3.6.3:2c5fed8, Oct  3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import datetime
>>> str(datetime.now())
'2018-08-23 09:52:14.895893'
>>> str(datetime.utcnow())
'2018-08-23 01:52:28.247986'
>>> quit()

(venv) D:\microblog>
           

調用

datetime.now()

将傳回我目前所在地區(中國-北京(時間))的正确的時間,而調用

datetime.utcnow()

将傳回UTC時區的時間。如果我能讓生活在世界不同地區的許多人同時一起運作上述代碼,

datetime.now()

函數将為每個人傳回不同的結果,但

datetime.utcnow()

無論地理位置在哪,都将傳回相同的時間。那麼你認為哪一個更适合一個很可能讓使用者遍布全球的Web應用程式中使用?

很明顯,伺服器必須管理 一緻且獨立于位置的時間。如果這個應用程式增長到世界各地需要多個

生産伺服器

的程式,當然就不希望每個伺服器在不同時區寫入資料庫的時間戳,因為這樣就無法使用這些時間。由于

UTC

是最常用的統一時區,并且在

datetime類

中受支援,是以在此将使用它。

但這種方法存在一個重要問題。對于不同時區的使用者,如果他們在UTC看到時間,那麼将很難弄清楚釋出文章的時間。他們需要提前時間是UTC,以便他們能夠“精神上”調整時間到他們自己的時區。想象一下,PDT時區的使用者在下午3點釋出了一些内容,并立即看到這個文章在UTC時間晚上10點出現,或更準确的說是22:00。這将是很令人困惑的。

雖然從伺服器的角度上,将時間戳标準化很有意義,但這會使用者帶來可用性問題。本章的目标是解決這個問題,同時保持伺服器以UTC格式管理的所有時間戳。

時區轉換

這個問題顯而易見的解決方案是:将所有時間戳 從存儲的UTC機關轉換為每個使用者的本地時間。這允許伺服器繼續使用UTC來保持一緻性,同時為每個使用者量身定制地即時轉換解決可用性問題。這個解決方案的棘手部分 是了解每個使用者的位置。

許多網站都有一個配置頁面,使用者可以在其中指定時區。這将要求我添加一個帶表單的新頁面,這個表單中,我可以向使用者顯示帶有時區清單的下拉清單。作為注冊的一部分,使用者可以在第一次通路網站時要求輸入他們的時區。

雖然這是解決問題的一個不錯解決方案,但要求使用者輸入他們已在其作業系統中配置的資訊有點奇怪。如果能從他們的計算機中擷取時區設定似乎會更有效率。

事實證明,Web浏覽器知道使用者的時區,并通過标準日期和時間JavaScript API公開它。實際上,通過JavaScript,有兩種方法可利用時區資訊:

  1. “老派”方法是在使用者首次登陸應用程式時,讓Web浏覽器以某種方式将時區資訊發送到伺服器。這可以通過Ajax調用完成,或更簡單地使用

    meta refresh tag

    。一旦伺服器知道時區,它就可以将其儲存在使用者的會話中,或将其寫入資料庫中的使用者條目,然後在渲染模闆時用它調整所有時間戳。
  2. “新派”方法是不改變伺服器中的東西,而在用戶端中使用JavaScript在用戶端中進行從UTC到本地時區的轉換。

這兩個選項都是有效的,但第二個選項有很大的優勢。光是知道使用者的時區,并不足以以使用者期望的格式呈現日期和時間。浏覽器還可通路系統區域配置,這個配置指定AP/PM與24小時制,DD/MM/YYYY與MM/DD/YYYY,以及許多其他文化或區域樣式之類的内容。

如果上述還不夠,那麼“新派”方法還有一個優勢。有一個開源庫可完成所有這些工作。

介紹Moment.js和Flask-Moment

[Moment.js](http://momentjs.com/)是一個小型開源JavaScript庫,它将日期和時間渲染成另一個級别,因為它提供每一個可想象的格式化選項。不久前,建立了

Flask-Moment

,它是一個小型

Flask擴充

,它可以輕易地将`moment.js`合并到應用程式中。

首先,安裝

Flask-Moment

:版本0.6.0

(venv) D:\microblog>pip install flask-moment
Collecting flask-moment
  Using cached https://files.pythonhosted.org/packages/dd/f7/13e9d7480f9097e0efe945e17309c34e0a547a6cfb3f9728324d2f9bf462/Flask_Moment-0.6.0-py2.py3-none-any.whl
Requirement already satisfied: Flask in d:\microblog\venv\lib\site-packages (from flask-moment)
Requirement already satisfied: Jinja2>=2.10 in d:\microblog\venv\lib\site-packages (from Flask->flask-moment)
Requirement already satisfied: itsdangerous>=0.24 in d:\microblog\venv\lib\site-packages (from Flask->flask-moment)
Requirement already satisfied: Werkzeug>=0.14 in d:\microblog\venv\lib\site-packages (from Flask->flask-moment)
Requirement already satisfied: click>=5.1 in d:\microblog\venv\lib\site-packages (from Flask->flask-moment)
Requirement already satisfied: MarkupSafe>=0.23 in d:\microblog\venv\lib\site-packages (from Jinja2>=2.10->Flask->flask-moment)
Installing collected packages: flask-moment
Successfully installed flask-moment-0.6.0
           

以正常方式添加到Flask應用程式中:

app/__init__.py

:添加Flask-Moment執行個體

# ...
from flask_moment import Moment

app = Flask(__name__)
# ...
moment = Moment(app)
           

與其他擴充不同,

Flask-Moment

moment.js

一起使用,是以應用程式的所有模闆都必須包含這個庫。為了確定這個庫始終可用,将在 基礎模闆中添加它。這可通過兩種方式完成。最直接的方法是顯示地以導入庫的方式添加一個

<script>

标簽,但

Flask-Moment

使其變得更容易,即通過公開一個

moment.include_moment()

函數,它會生成

<script>

标簽。

app/templates/base.html

:在基礎模闆中包含moment.js

...
{% block scripts %}
	{{ super() }}
	{{ moment.include_moment() }}
{% endblock %}
           

在這添加的

scripts塊

Flask-Bootstrap

的基礎模闆導出的另一個塊。這是包含JavaScript導入的地方。這個塊與之前的塊不同,因為它已經在基礎模闆中定義了一些内容。我想的是 添加

moment.js

庫,而不會失去基本内容。這是通過

super()

語句實作的,這個語句将保留基礎模闆中的内容。如果在沒有使用

super()

的情況下,在你的模闆中定義一個塊,那麼在基礎模闆中,為這個塊定義的任何内容都将失去。

使用Moment.js

Moment.js

使得一個`moment類`可供浏覽器使用。渲染時間戳的第一步是建立這個類的對象,以ISO 8601格式傳遞所需的時間戳。下方是例子:

t = moment('2017-09-28T21:45:23Z')
           

如果你不熟悉日期和時間的ISO 8601标準格式,格式如:

{{ year }}-{{ month }}-{{ day }}T{{ hour }}:{{ minute }}:{{ second }}{{ timezone }}

。我已經決定隻用UTC時區,是以最後一部分将始終是

Z

,它代表ISO 8601标準中的UTC。

moment

對象提供為不同渲染選項提供了幾種方法。以下是一些最常見的選項:

moment('2017-09-28T21:45:23Z').format('L')
"09/28/2017"
moment('2017-09-28T21:45:23Z').format('LL')
"September 28, 2017"
moment('2017-09-28T21:45:23Z').format('LLL')
"September 28, 2017 2:45 PM"
moment('2017-09-28T21:45:23Z').format('LLLL')
"Thursday, September 28, 2017 2:45 PM"
moment('2017-09-28T21:45:23Z').format('dddd')
"Thursday"
moment('2017-09-28T21:45:23Z').fromNow()
"7 hours ago"
moment('2017-09-28T21:45:23Z').calendar()
"Today at 2:45 PM"
           

上述示例 建立了一個時刻對象,初始化為

2017年9月28日晚上9:45 UTC

。上面嘗試的所有選項都以UTC-7時區(在中國的話,将用UTC+8)來呈現,因為這是作者的計算機上配置的時區。可在浏覽器的控制台中輸入上述指令,得確定打開控制台的頁面包含moment.js。如果引入了moment.js,也可以在Microblog上操作。或者在https://momentjs.com/上操作。

注意不同方法是如何建立不同的表示的。使用

format()

,可以控制字元串的輸出格式,類似Python中的

strftime()

函數。

fromNow()

calendar()

方法很有趣,因為它們會根據目前時間顯示時間戳,是以會得到如“一分鐘前”或“兩個小時内”的輸出。

如果直接使用JavaScript,那麼上述調用将傳回具有呈現時間戳的字元串。然後,可将此文本插入頁面上的适當位置,遺憾的是,需要JavaScript與DOM配合使用。

Flask-Moment

在模闆中啟用類似于JavaScript的對象,極大地簡化

moment.js

的使用。

我們來看一下 個人資料頁面 中顯示的時間戳。目前

user.html

模闆允許使用Python生成時間的字元串表示。現在使用

Flask-Moment

渲染這個時間戳,如下:

app/templates/user.html

:使用moment.js渲染時間戳

...
		{% if user.last_seen %}
			<p>Last seen on:{{ moment(user.last_seen).format('LLL') }}</p>
		{% endif %}
...
           
Flask-Moment

使用的文法類似于 JavaScript庫的文法,一個差別是

moment()

的參數現在是一個Python

datetime

對象,而不是一個ISO 8601字元串。

moment()

從模闆發出的調用還會自動生成所需的JavaScript代碼,以将呈現的時間戳插入到DOM的适當位置。

可以利用

Flask-Moment

moment.js

的第二個地方是

_post.html

子模闆,它是從

/index

/user

頁面中調用的。在目前版本的模闆中,每個文章前面都有一個

“username says:”行

。現在添加一個

fromNow()

時間戳:

app/templates/_post.html

<a href="{{ url_for('user', username=post.author.username) }}" target="_blank" rel="external nofollow" >
                    {{ post.author.username }}
                </a>
                said {{ moment(post.timestamp).fromNow() }}:
                <br>
                {{ post.body }}
           

flask run

運作程式,效果:

C:\Users\Administrator>d:

D:\>cd D:\microblog\venv\Scripts

D:\microblog\venv\Scripts>activate
(venv) D:\microblog\venv\Scripts>cd D:\microblog

(venv) D:\microblog>set MAIL_SERVER=smtp.qq.com

(venv) D:\microblog>set MAIL_PORT=465

(venv) D:\microblog>set MAIL_USE_SSL=True

(venv) D:\microblog>set [email protected]

(venv) D:\microblog>set MAIL_PASSWORD=16位授權碼

(venv) D:\microblog>flask run
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
[2018-08-23 15:10:27,042] INFO in __init__: Microblog startup
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [23/Aug/2018 15:10:36] "GET /user/oldiron HTTP/1.1" 200 -
127.0.0.1 - - [23/Aug/2018 15:11:04] "GET /edit_profile HTTP/1.1" 200 -
127.0.0.1 - - [23/Aug/2018 15:11:05] "GET /index HTTP/1.1" 200 -
127.0.0.1 - - [23/Aug/2018 15:11:08] "GET /user/oldiron HTTP/1.1" 200 -
127.0.0.1 - - [23/Aug/2018 15:11:21] "GET /explore HTTP/1.1" 200 -
           
12 Flask mega-tutorial 第12章 日期和時間 Dates and Times時區“地獄”時區轉換介紹Moment.js和Flask-Moment使用Moment.js

目前為止,項目結構:

microblog/
    app/
        templates/
	        email/
		        reset_password.html
		        reset_password.txt
	        _post.html
	        404.html
	        500.html
            base.html
            edit_profile.html
            index.html
            login.html
            register.html
            reset_password.html
            reset_password_request.html
            user.html
        __init__.py
        email.py
        errors.py
        forms.py
        models.py
        routes.py
    logs/
        microblog.log
    migrations/
    venv/
    app.db
    config.py
    microblog.py
    tests.py
      

參考:

作者部落格

源代碼

如需轉載請注明出處。

繼續閱讀