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 ...
随机推荐
- 2019-2020-1 20199304《Linux内核原理与分析》第六周作业
第五章 系统调用的三层机制(下) 1.往MenuOS中添加命令 (1)首先进入LinuxKernel文件夹,将menu目录删除.然后再git clone克隆下载更新了版本之后的menu目录(包含tim ...
- Python小数据保存,有多少中分类?不妨看看他们的类比与推荐方案...
小数据存储 我们在编写代码的时候,经常会涉及到数据存储的情况,如果是爬虫得到的大数据,我们会选择使用数据库,或者excel存储.但如果只是一些小数据,或者说关联性较强且存在存储后复用的数据,我们该如何 ...
- ubuntu下安装gcc
在ubuntu下安装gcc 第一次写blog,多多包涵! gcc安装步骤 废话不多说,gcc安装步骤如下: 1. sudo apt update 2. sudo apt install build-e ...
- Java修炼——基于TCP协议的Socket编程_双向通信_实现模拟用户登录
首先我们需要客户端和服务器端. 服务器端需要:1.创建ServerSocket对象.2.监听客户端的请求数据.3.获取输入流(对象流)即用户在客户端所发过来的信息. ...
- 使用echarts去对数据进行图形分析
首先导入js包:echarts.min.js <script type="text/javascript" src="js/echarts.min.js" ...
- GZIP怎么运用在.NET MVC 简单实现
ZIP压缩其实就是将网页内容压缩,减少HTML代码网络传输的代价,来提高Web性能. 这个请求的过程解释一下: 1:客户端Request请求.Http_header中会根据相应的浏览器发送相应的编码规 ...
- TypeScript躬行记(2)——接口
在传统的面向对象语言中,接口(Interface)好比协议,它会列出一系列的规则(即对行为进行抽象),再由类来实现这些规则.而TypeScript中的接口更加灵活,除了包含常规的作用之外,它还能扩展其 ...
- Flink入门(四)——编程模型
flink是一款开源的大数据流式处理框架,他可以同时批处理和流处理,具有容错性.高吞吐.低延迟等优势,本文简述flink的编程模型. 数据集类型: 无穷数据集:无穷的持续集成的数据集合 有界数据集:有 ...
- Seata 配置中心实现原理
Seata 可以支持多个第三方配置中心,那么 Seata 是如何同时兼容那么多个配置中心的呢?下面我给大家详细介绍下 Seata 配置中心的实现原理. 配置中心属性加载 在 Seata 配置中心,有两 ...
- 【CuteJavaScript】Angular6入门项目(3.编写服务和引入RxJS)
本文目录 一.项目起步 二.编写路由组件 三.编写页面组件 1.编写单一组件 2.模拟数据 3.编写主从组件 四.编写服务 1.为什么需要服务 2.编写服务 五.引入RxJS 1.关于RxJS 2.引 ...