Flask 教程 第十二章:日期和时间

本文翻译自The Flask Mega-Tutorial Part XII: Dates and Times

这是Flask Mega-Tutorial系列的第十二部分,我将告诉你如何以适配所有用户的方式处理日期和时间,无论他们身处地球上的何处。

显示日期和时间是Microblog应用中长期被忽略的其中一个方面。 直到现在,我也只是让Python渲染了User模型中的datetime对象,并且完全忽略了Post模型中的datetime对象。

本章的GitHub链接为:BrowseZipDiff.

时区地狱

使用服务器端的Python渲染日期和时间来展示到用户的浏览器并非一个好主意。考虑如下的例子, 我在2017年9月28日下午4点06分写这篇文章。我身处的时区是PDT(UTC-7),在Python解释器中运行如下:

1 >>> from datetime import datetime
2 >>> str(datetime.now())
3 ‘2017-09-28 16:06:30.439388‘
4 >>> str(datetime.utcnow())
5 ‘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来开始吧:

1 (venv) $ pip install flask-moment

使用常规方法添加该插件到Flask应用中:

app/__init__.py:Flask-Moment实例。

1 # ...
2 from flask_moment import Moment
3
4 app = Flask(__name__)
5 # ...
6 moment = Moment(app)

与其他插件不同的是,Flask-Moment与moment.js一起工作,因此应用的所有模板都必须包含moment.js。为了确保该库始终可用,我将把它添加到基础模板中,可以通过两种方式完成。 最直接的方法是显式添加一个<script>标签来引入库,但Flask-Moment的moment.include_moment()函数可以更容易地实现它,它直接生成了一个<script>标签并在其中包含moment.js:

app/templates/base.html:在基础模板中包含moment.js

1 ...
2
3 {% block scripts %}
4     {{ super() }}
5     {{ moment.include_moment() }}
6 {% endblock %}

我在这里添加的scripts块是Flask-Bootstrap基础模板暴露的另一个块,这是JavaScript引入的地方。该块与之前的块不同的地方在于它已经在基础模板中定义了一些内容了。我想要追加moment.js库的话,就需要使用super()语句,才能继承基础模板中已有的内容,否则就是替换。

使用Moment.js

Moment.js为浏览器提供了一个moment类。 呈现时间戳的第一步是创建此类的对象,并以ISO 8601格式传递所需的时间戳。 这里是一个例子:

1 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渲染时间戳。

1                 {% if user.last_seen %}
2                 <p>Last seen on: {{ moment(user.last_seen).format(‘LLL‘) }}</p>
3                 {% endif %}

如你所见,Flask-Moment使用的语法类似于JavaScript库的语法,其中一个区别是,moment()的参数现在是Python的datetime对象,而不是ISO 8601字符串。 从模板发出的moment()调用也会自动生成所需的JavaScript代码,以将呈现的时间戳插入DOM的适当位置。

我可以利用Flask-Moment和moment.js的第二个地方是被主页和个人主页调用的_post.html子模板。 在该模板的当前版本中,每条用户动态都以“用户名说:”行开头。 现在我可以添加一个用fromNow()渲染的时间戳:

app/templates/_post.html: 在用户动态子模板中渲染时间戳。

1                 <a href="{{ url_for(‘user‘, username=post.author.username) }}">
2                     {{ post.author.username }}
3                 </a>
4                 said {{ moment(post.timestamp).fromNow() }}:
5                 <br>
6                 {{ post.body }}

下面,你可以看到这两个时间戳在Flask-Moment和moment.js的渲染下,表现如何:

原文地址:https://www.cnblogs.com/sunwk/p/12059055.html

时间: 2024-10-28 16:35:26

Flask 教程 第十二章:日期和时间的相关文章

Flask 教程 第十四章:Ajax

本文翻译自The Flask Mega-Tutorial Part XIV: Ajax 这是Flask Mega-Tutorial系列的第十四部分,我将使用Microsoft翻译服务和少许JavaScript来添加实时语言翻译功能. 在本章中,我将从服务器端开发的“安全区域”脱离,研究与服务器端同样重要的客户端组件的功能. 你是否看到过某些网站在用户生成的内容旁边显示的“翻译”链接? 这些链接会触发非用户本地语言内容的实时自动翻译. 翻译的内容通常插入原始版本的下方. Google将其显示为外语

Flask 教程 第十六章:全文搜索

本文翻译自The Flask Mega-Tutorial Part XVI: Full-Text Search 这是Flask Mega-Tutorial系列的第十六部分,我将在其中为Microblog添加全文搜索功能. 本章的目标是为Microblog实现搜索功能,以便用户可以使用自然语言查找有趣的用户动态内容.许多不同类型的网站,都可以使用Google,Bing等搜索引擎来索引所有内容,并通过其搜索API提供搜索结果. 这这方法适用于静态页面较多的的大部分网站,比如论坛. 但在我的应用中,基

C#图解教程 第十二章 数组

数组 数组 数组实际上是由一个变量名称表示的一组同类型的数据元素.每个元素通过变量名称和一个或多个方括号中的索引来访问: 数组名 索引 ↓ ↓ MyArray[4] 定义 让我们从C#中与数组有关的重要定义开始 元素 数组的独立数据项称为元素.数组的所有元素必须是同类型的,或继承自相同的类型 秩/维度 数组可以有任何为正数的维度数.数组的维度数称作秩(rank) 维度长度 数组的每个维度都有一个长度,就是这个方向的位置个数 数组长度 数组的所有维度中的元素的总和称为数组的长度 重要细节 关于C#

D3.js的v5版本入门教程(第十二章)—— D3.js中各种精美的图形

D3.js的v5版本入门教程(第十二章) D3中提供了各种制作常见图形的函数,在d3的v3版本中叫布局,通过d3.layout.xxx,来新建,但是到了v5,新建一个d3中基本的图形的方式变了(我也并不知道是不是还叫布局,我觉得也可以这么叫,反正布局指的也是一个绘图函数) 下面是d3中一些常见的部分图形 bubble —— 泡泡图 packing —— 打包图 bundling —— 捆图 force —— 力导向图 chord —— 弦图 pie——饼状图 tree——树状图 中国地图 我们利

进击的Python【第十二章】:mysql介绍与简单操作,sqlachemy介绍与简单应用

进击的Python[第十二章]:mysql介绍与简单操作,sqlachemy介绍与简单应用 一.数据库介绍 什么是数据库? 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库,每个数据库都有一个或多个不同的API用于创建,访问,管理,搜索和复制所保存的数据.我们也可以将数据存储在文件中,但是在文件中读写数据速度相对较慢.所以,现在我们使用关系型数据库管理系统(RDBMS)来存储和管理的大数据量.所谓的关系型数据库,是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来

MiS603开发板 第十二章 SLAVE FIFO流传输

作者:MiS603开发团队 日期:20150911 公司:南京米联电子科技有限公司 论坛:www.osrc.cn 网址:www.milinker.com 网店:http://osrc.taobao.com EAT博客:http://blog.chinaaet.com/whilebreak 博客园:http://www.cnblogs.com/milinker/ MiS603开发板 第十二章 SLAVE FIFO流传输 15.1 USB 固件源码分析 SLAVE FIFOUSB 固件源码仍然采用上

C和指针 (pointers on C)——第十二章:使用结构和指针

第十二章 使用结构和指针 这章就是链表.先单链表,后双向链表. 总结: 单链表是一种使用指针来存储值的数据结构.链表中的每个节点包含一个字段,用于指向链表的下一个节点. 有一个独立的根指针指向链表的第1个节点.单链表只能从一个方向遍历. 如何insert单链表:1.新节点的link字段必须设置为指向它的后面节点.2.前一个节点的link字段必须指向这个新节点. 为了防止可能会插入链表的起始位置这种情况,在C中,可以保存一个指向必须进行修改的link字段的指针,而不是保存一个指向前一个节点的指针.

《构建之法》第十一、十二章学习总结

第十一章的内容是软件设计与实现. 在第一节中,讲的是关于分析和设计方法,向我们介绍在"需求分析"."设计与实现"阶段."测试""发布"阶段该搞清楚的问题. 在第二节中,讲的是关于图形建模和分析方法.在表达实体和实体之间的关系时,可以用到思维导图(Mind Map).实体关系图(ERD).UCD ;在表达数据的流动时,可以用到DFD工具:在表达控制流的时候可以用到FSM工具:前面提到的这些图形建模方法各有特点,UML却可以有一个

C primer plus 第五版十二章习题

看完C prime plus(第五版)第十二章,随带完成了后面的习题. 1.不使用全局变量,重写程序清单12.4的程序. 先贴出12.4的程序,方便对照: 1 /* global.c --- 使用外部变量 */ 2 #include <stdio.h> 3 int units = 0; //一个外部变量 4 void critic(void); 5 int main(void) 6 { 7 extern int units; 8 9 printf ("How many pounds