Flask 教程 第十二章:日期和时间
本文翻译自The Flask Mega-Tutorial Part XII: Dates and Times
这是Flask Mega-Tutorial系列的第十二部分,我将告诉你如何以适配所有用户的方式处理日期和时间,无论他们身处地球上的何处。
显示日期和时间是Microblog应用中长期被忽略的其中一个方面。 直到现在,我也只是让Python渲染了User
模型中的datetime
对象,并且完全忽略了Post
模型中的datetime
对象。
本章的GitHub链接为:Browse, Zip, Diff.
时区地狱
使用服务器端的Python渲染日期和时间来展示到用户的浏览器并非一个好主意。考虑如下的例子, 我在2017年9月28日下午4点06分写这篇文章。我身处的时区是PDT(UTC-7),在Python解释器中运行如下:
>>> from datetime import datetime
>>> str(datetime.now())
'2017-09-28 16:06:30.439388'
>>> str(datetime.utcnow())
'2017-09-28 23:06:51.406499'
datetime.now()
调用返回我所处位置的本地时间,而datetime.utcnow()
调用则返回UTC时区中的时间。 如果我可以让遍布世界不同地区的多人同时运行上面的代码,那么datetime.now()
函数将为他们每个人返回不同的结果,但是无论位置如何,datetime.utcnow()
总是会返回同一时间。 那么你认为哪一个更适合用在一个很可能其用户遍布世界各地的Web应用中呢?
很明显,服务器必须管理一致且独立于位置的时间。 如果这个应用增长到在全世界不同地区都需要部署生产服务器的时候,我不希望每个服务器都在写入不同时区的时间戳到数据库,因为这会导致其无法正常地运行。 由于UTC是最常用的统一时区,并且在datetime
类中也受到支持,因此我将会使用它。
但这种方法存在一个严重问题。 对处于不同时区的用户,如果他们看到的是UTC时区中的时间,那么很难确定是何时发布的信息。 他们需要事先知道展示的时间是UTC时区的,才能在精神上调整自己的时区。 设想一下PDT时区中的一个用户在下午3点发布了一些内容,并立即看到该帖子以UTC时间表示的晚上10:00或更准确的22:00,这太混乱了。
从服务器的角度来说,将时间戳标准化为UTC,意义重大,但这会为用户带来可用性问题。 本章的目标就是解决该问题,同时保持服务器中以UTC格式管理的所有时间戳。
While standardizing the timestamps to UTC makes a lot of sense from the server’s perspective, this creates a usability problem for users. The goal of this chapter is to address this problem while keeping all the timestamps managed in the server in UTC.
时区转换
该问题的直接解决方案是将所有时间戳从存储的UTC单位转换为每个用户的本地时间。 这样一来,服务器可以继续使用UTC来保持时区的一致性,而针对每个用户量身定制的即时转换来解决可用性问题。 这个解决方案棘手的部分是要知道每个用户的位置。
许多网站都有一个配置页面供用户指定他们的时区。 这将需要我添加一个新的页面,其中我向用户显示带有时区列表的下拉列表。 也可能用户在第一次访问网站时,作为注册的一部分,会被要求输入他们的时区。
虽然该方案可以解决问题,但要求用户输入他们已经在其操作系统中配置的信息有点奇怪。 如果我能从他们的计算机中获取时区设置,似乎效率会更高。
事实证明,Web浏览器可以获取用户的时区,并通过标准的日期和时间JavaScript API暴露它。 实际上有两种方法来利用JavaScript提供的时区信息:
- “老派”方法是当用户第一次登录到应用程序时,Web浏览器以某种方式将时区信息发送到服务器。 这可以通过Ajax调用完成,或者更简单地使用meta refresh tag。 一旦服务器知道了时区,就可以将其保存在用户的会话中,或者将其写入用户在数据库中的条目中,然后在渲染模板时从中调整所有时间戳。
- “新派”的做法是不改变服务器中的东西,而在客户端中使用JavaScript来对UTC和本地时区之间进行转换。
两种选择都是有效的,但第二种选择有很大优势。 光是知道用户的时区并不足以以用户期望的格式呈现日期和时间。 浏览器还可以访问系统区域配置,该配置指定AM/PM与24小时制,DD/MM/YYYY与MM/DD/YYYY,以及许多其他文化或地区风格之类的东西。
如果这还不够,新派方法还有另一个优势,用一个开源的库来完成所有这些工作!
Moment.js和Flask-Moment简介
Moment.js是一个小型的JavaScript开源库,它将日期和时间转换成目前可以想象到的所有格式。 不久前,我创建了Flask-Moment,一个小型Flask插件,它可以使你在应用中轻松使用moment.js。
因此,让我们从安装Flask-Moment来开始吧:
(venv) $ pip install flask-moment
使用常规方法添加该插件到Flask应用中:
app/__init__.py
:Flask-Moment实例。
# ...
from flask_moment import Moment app = Flask(__name__)
# ...
moment = Moment(app)
与其他插件不同的是,Flask-Moment与moment.js一起工作,因此应用的所有模板都必须包含moment.js。为了确保该库始终可用,我将把它添加到基础模板中,可以通过两种方式完成。 最直接的方法是显式添加一个<script>
标签来引入库,但Flask-Moment的moment.include_moment()
函数可以更容易地实现它,它直接生成了一个<script>
标签并在其中包含moment.js:
app/templates/base.html:在基础模板中包含moment.js
... {% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}
我在这里添加的scripts
块是Flask-Bootstrap基础模板暴露的另一个块,这是JavaScript引入的地方。该块与之前的块不同的地方在于它已经在基础模板中定义了一些内容了。我想要追加moment.js库的话,就需要使用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"
此示例创建了一个moment对象,该对象被初始化为2017年9月28日晚上9:45 UTC。 你可以看到,我上面尝试的所有选项都以UTC-7时区来呈现,因为这是我计算机上配置的时区。你可以在microblog上进行此操作,只要你引入了moment.js。或者你也可以在https://momentjs.com/上尝试。
请注意不同的方法是如何创建的不同的表示。 使用format()
,你可以控制字符串的输出格式,类似于Python中的strftime函数。 fromNow()
和calendar()
方法很有趣,因为它们会根据当前时间显示时间戳,因此你可以获得诸如“一分钟前”或“两小时内”等输出。
如果你直接在JavaScript中运行,则上述调用将返回渲染后的时间戳字符串。 然后,你可以将此文本插入页面上的适当位置,不幸的是,这需要JavaScript与DOM配合使用。 Flask-Moment插件通过启用一个类似于JavaScript上的moment
对象,大大简化了对moment.js的使用,并融合了所需的JavaScript逻辑,使渲染后的时间展示在页面上。
我们来看看出现在个人主页中的时间戳。 当前的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子模板。 在该模板的当前版本中,每条用户动态都以“用户名说:”行开头。 现在我可以添加一个用fromNow()
渲染的时间戳:
app/templates/_post.html: 在用户动态子模板中渲染时间戳。
<a href="{{ url_for('user', username=post.author.username) }}">
{{ post.author.username }}
</a>
said {{ moment(post.timestamp).fromNow() }}:
<br>
{{ post.body }}
下面,你可以看到这两个时间戳在Flask-Moment和moment.js的渲染下,表现如何:
Flask 教程 第十二章:日期和时间的更多相关文章
- Flask 教程 第二十二章:后台作业
本文翻译自The Flask Mega-Tutorial Part XXII: Background Jobs 这是Flask Mega-Tutorial系列的第二十二部分,我将告诉你如何创建独立于W ...
- python 教程 第二十二章、 其它应用
第二十二章. 其它应用 1) Web服务 ##代码 s 000063.SZ ##开盘 o 26.60 ##最高 h 27.05 ##最低 g 26.52 ##最新 l1 26.66 ##涨跌 c ...
- 2017.2.13 开涛shiro教程-第十二章-与Spring集成(二)shiro权限注解
原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第十二章-与Spring集成(二)shiro权限注解 shiro注 ...
- 2017.2.13 开涛shiro教程-第十二章-与Spring集成(一)配置文件详解
原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第十二章-与Spring集成(一)配置文件详解 1.pom.xml ...
- python 教程 第十二章、 标准库
第十二章. 标准库 See Python Manuals ? The Python Standard Library ? 1) sys模块 import sys if len(sys.argv) ...
- Flask 教程 第十九章:Docker容器上的部署
本文翻译自The Flask Mega-Tutorial Part XIX: Deployment on Docker Containers 这是Flask Mega-Tutorial系列的第十九部分 ...
- Flask 教程 第十四章:Ajax
本文翻译自The Flask Mega-Tutorial Part XIV: Ajax 这是Flask Mega-Tutorial系列的第十四部分,我将使用Microsoft翻译服务和少许JavaSc ...
- Flask 教程 第十五章:优化应用结构
本文翻译自The Flask Mega-Tutorial Part XV: A Better Application Structure 这是Flask Mega-Tutorial系列的第十五部分,我 ...
- Flask 教程 第十六章:全文搜索
本文翻译自The Flask Mega-Tutorial Part XVI: Full-Text Search 这是Flask Mega-Tutorial系列的第十六部分,我将在其中为Microblo ...
随机推荐
- luogu P1840 Color the Axis_NOI导刊2011提高(05)|并查集
题目描述 在一条数轴上有N个点,分别是1-N.一开始所有的点都被染成黑色.接着我们进行M次操作,第i次操作将[Li,Ri]这些点染成白色.请输出每个操作执行后剩余黑色点的个数. 输入格式 输入一行为N ...
- linux gre隧道创建
目录 linux gre隧道创建 实验环境 实验目的 实验步骤 1.在host A(10.10.10.47)上面操作 2.在host B(192.168.0.118)上面操作 实验结果 还原实验环境 ...
- UESTC1961-咸鱼睡觉觉
咸鱼睡觉觉 Time Limit: 1000 MS Memory Limit: 64 MB Submit Status 咸鱼要睡觉觉了! 但那群咕咕有点烦. 咸鱼决定要赶走一些咕咕,使得他们不 ...
- windows程序设计00_HelloWorld
#include <windows.h> int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR sz ...
- 带你快速了解Java锁中的公平锁与非公平锁
前言 Java语言中有许多原生线程安全的数据结构,比如ArrayBlockingQueue.CopyOnWriteArrayList.LinkedBlockingQueue,它们线程安全的实现方式并非 ...
- angular6路由参数的传递与获取
1.访问路由链接:/test/id 路由配置: {path: 'test/:id', component: TestComponent} html传参: <a href="javasc ...
- pipelinedb学习笔记 - 1. Continuous Views (连续视图)
Continuous Views 一.Continuous Views 英文直译过来叫连续视图, 在pipelindb中是被定义为专门用来展示 Stream中数据用的.例如:Stream中有一些用户信 ...
- JSP注册登录页教程
转载请标明原文地址:http://www.cnblogs.com/zhangyukof/p/6785258.html 一.准备工作 已搭建好的SSH框架工程一个,如果没有,请参考我的上一篇文章< ...
- 在 ASP.NET Core 中使用 FluentValidation 进行验证
目录 从 NuGet 安装 FluentValidation 争对 Resource类 建立 FluentValidation 在Startup中对写好的验证进行注册 从 NuGet 安装 Fluen ...
- logging in kubernetes
background docker docker的日志输出可以通过指定driver输出到不同的位置,常用的是journald和json-file. 使用journald日志输出可能受限于jourand ...