在Python的Flask框架中使用日期和时间的教程


Posted in Python onApril 21, 2015

 时间戳的问题

我们的微博应用的一个忽略了很久的问题就是日间和日期的显示。

直到现在,我们在我们的User和Post对象中使用Python它自己的方式来渲染时间对象,但这并不是一个好的解决方案。

考虑下这样的例子。我正在写这篇文章,此时正是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()方法返回了正确的时间,但是now()调用返回的时间是UTC单位。

那么,使用哪个更好呢?

如果我们用now(),所有数据库里的时间戳将会与服务器运行的当地时间一致,这将会产生一些问题。

比如,如果有一天,我们需要将服务器放到别的地方(不在一个时区),那么在重启服务器之前,数据库里的时间都需要更新到与新地点保持一致。

还会有更为重要的问题。不同时区的用户将会很难知道什么时候发送邮件,如果用户看到的是PST时区的时间,他们就很难知道邮件是什么时候发送的,这就需要用户根据这个时间做相应的调整。

很显然这不是一个好的选择,这也是我们为什么在创建数据库时就使用UTC时区保存时间戳。

在标准化时间戳为UTC时,解决了移动服务器的问题。但是他不能解决第二个问题,数据和时间在世界上不同地方使用UTC展现给用户。

假设一个用户在PST时区下午3点发送了一封邮件,这封邮件立刻显示在他面前,上面写着11:00pm,或者更具体点(23:00)。

我写这个文章的目的也就是让我们的用户不再因为数据和时间的显示而困惑。

使用具体的时间戳

通常的解决方法是,每一个用户都从UTC转化到当地的时间。这就需要我们动态变化,从而使数据库的UTC与之保持一致。

但是我们怎么知道用户在哪呢?

许多网站都有一个设置页面设置他们的时区。这就需要我们添加一个新的页面,并在表单上提供下拉框让用户选择时区,用户第一次登录的时候需要设置时区,并把它作为注册的一部分。

这是一个正常的解决方法,但是这对于用户来说有点累赘,用户需要输入一条他们已经在操作系统中配置过的信息。所以如果我们能抓取到用户电脑里设置的时区那解决问题会变得更有效率。

出于安全因素,浏览器不允许我们进入用户操作系统获取信息。即使它允许,我们也得知道在Windows,Linux,Mac,iOS,Android中从哪儿能获得到时区,这还不包括其他非主流操作系统。

在浏览器中得到用户的时区,然后通过标准的Javascript API获取到。在Web 2.0世界中用户允许Javascript执行(很少有网站不使用Javascript),所以通过Javascript获取用户时区是可行的。

我们用Javascript有两种方式配置可用的时区:

    老派的做法:当用户第一次登录服务器时让浏览器以某种方式发送时区信息给我们。这个可以通过Ajax调用,或者更简单的通过meta refresh tag来实现。一旦服务器知道了时区信息,它就能保存它在用户session中,然后调整所有页面的时间显示。
    新派的做法:不改变服务器端的任何东西,但仍然会发送UTC时间戳到客户端浏览器。转换UTC到本地时间的工作通过Javascript在客户端执行。

两种方法都是有效的,但第二种更有优势一点。浏览器能依照系统本地配置最好滴完成时间转换。像上午/下午 vs 24小时制,日/月/年 vs 月/日/年 还有其他各种文化的格式,这些格式都是浏览器可访问的,但服务器就不一定了。

如果这些还不够,那新派的做法还有一个更大的优势,而且别人已经为我们做了这件事(moment.js要登场了)!

moment.js简介

Moment.js 是一个小、免费、开源的Javascript库,它将日期和事件提升到另一个等级。它提供了能想象到的所有的时间日期格式,下面就是一些。

要在我们的应用中使用moment.js就需要在我们的模板文件中写那么一丢丢的Javascript代码。我们先来通过ISO 8601 时间来创建一个moment对象。例如:通过上面Python例子的UTC时间来创建一个moment对象,就像这样:
 

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

一旦对象被创建,它就可以被转化成各种各样格式类型的string。例如,将一个灰常冗长的时间显示转换为本地系统的时间:
 

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

下面就是转换以后的时间显示:

Tuesday, January 1 2013 7:55 AM

这儿有更多的例子将同样的时间戳转化为不同的格式:

在Python的Flask框架中使用日期和时间的教程

这个类库对转化选项的支持不止这些。除了format()之外,它还提供了fromNow()和calendar()这些更友好的时间戳转化方法: 

在Python的Flask框架中使用日期和时间的教程

 注意上面所有的例子中服务器转换相同的UTC时间,而你自己的本地浏览器则会转换不同的时间。

最后我们补上漏掉的一点Javascript小技巧,在页面中显而易见的是,代码实际上由moment返回了string类型。最简单的完成方式是用Javascript的document.write方法:
 

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

通过使用Javascript的document.write是灰常简单和直接的方式来生成一部分HTML代码,然而需要注意的是这种方式有一些限制。最需要主义的一点就是document.write方法只能在document被加载时使用,当document加载完成后,它便不能修改document了。这个限制的结果就是当通过 Ajax 来加载数据时这种解决方案就失效了。
 
整合moment.js

这儿我们需要做一点点事把moment.js添加到我们的微博客中.

首先,我们需要下载moment.min.js这个库到/app/static/js这个文件夹中,这样它就可以作为静态文件为客户端服务。

然后我们在我们的模板文件(fileapp/templates/base.html)中添加这个库(moment.min.js)的引用:

 

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

现在我们可以在模板文件中添加<script>标签来显示我们想要的时间戳。但我们想替换这种方法,于是我们给moment.min.js加了一层包装来更好的从模板中调用它。这样将节约我们将来修改时间戳代码的时间,因为这样我们只要在一个地方修改它就好了。

我们的包装是一个灰常简单的Python类(fileapp/momentjs.py):
 

from jinja2 import Markup
 
class momentjs:
 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方法没有直接返回一个string类型而是返回一个由我们的模板引擎Jinja2提供的Markup对象。这么做的原因是Jinja2默认会转义所有string,举例来说,我们的<script>标签不能被传到客户端,而会被替换成<script>("<"">"这些符号被转义了,译者注)。所以,用Markup对象来包装这些string会告诉Jinja2这些string不能被转义。

现在我们有了一个包装器,我们需要通过Jinja2来连接,这样我们的模板就可以来调用它(fileapp/__init__.py):
 

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

这样Jinja2就会把我们的类当做全局变量来暴露给所有的模板。

现在我们准备开始修改我们的模板。在我们的应用中有两个地方来显示日期和事件。一个是在用户属性页面,我们显示”最后看过“的时间。对于这个时间戳我们将用calendar()方法来转换(fileapp/templates/user.html):
 

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

第二个地方是post的子模板中,它被index,user和search页面调用。在这儿我们用fromNow()方法来转换,因为post是多久前被创建的比它具体是哪一个时刻被创建的重要。要提取转换以后的post时间到子页面,我们只需要改动一个地方就可以影响所有需要转换的post时间(fileapp/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>

随着这些模板的改动,我们便解决了所有的时间戳问题。我们不需要单独的去修改服务器端的代码!
 
结束语

不知不觉中,今天我们通过做一些客户端时间日期配置的改变,已经朝使微博客变得更受多国用户青睐迈出重要一步。

在这个系列的下一部分中,我们将要使微博客支持多国语言(国际化),从而让它不同国家用户的更受欢迎。

同时,这儿提供了集成moment.js的应用得下载链接:

下载 microblog-0.13.zip。

Python 相关文章推荐
python实现网页链接提取的方法分享
Feb 25 Python
Python的Scrapy爬虫框架简单学习笔记
Jan 20 Python
基于Django URL传参 FORM表单传数据 get post的用法实例
May 28 Python
python爬取个性签名的方法
Jun 17 Python
python 借助numpy保存数据为csv格式的实现方法
Jul 04 Python
python按照多个条件排序的方法
Feb 08 Python
python函数与方法的区别总结
Jun 23 Python
Pandas之DataFrame对象的列和索引之间的转化
Jun 25 Python
python设置随机种子实例讲解
Sep 12 Python
python批量处理txt文件的实例代码
Jan 13 Python
记一次django内存异常排查及解决方法
Aug 07 Python
python实现批量移动文件
Apr 05 Python
在Python的Flask框架下收发电子邮件的教程
Apr 21 #Python
在Python的Flask框架中实现全文搜索功能
Apr 20 #Python
Python的Flask框架中实现分页功能的教程
Apr 20 #Python
在Python的Flask框架中实现单元测试的教程
Apr 20 #Python
Python的Flask框架中实现登录用户的个人资料和头像的教程
Apr 20 #Python
Python的Flask框架中实现简单的登录功能的教程
Apr 20 #Python
Python的Flask框架与数据库连接的教程
Apr 20 #Python
You might like
php 无限级分类,超级简单的无限级分类,支持输出树状图
2014/06/29 PHP
javascript 限制输入和粘贴(IE,firefox测试通过)
2008/11/14 Javascript
基于jQuery实现下拉收缩(展开与折叠)特效
2012/12/25 Javascript
关于jquery css的使用介绍
2013/04/18 Javascript
jquery实现图片滚动效果的简单实例
2013/11/23 Javascript
jQuery选择器全面总结
2014/01/06 Javascript
js与jquery回车提交的方法
2015/02/03 Javascript
JQuery radio(单选按钮)操作方法汇总
2015/04/15 Javascript
javascript结合canvas实现图片旋转效果
2015/05/03 Javascript
微信小程序 参数传递实例代码
2017/03/20 Javascript
Angular实现双向折叠列表组件的示例代码
2017/11/21 Javascript
微信小程序实现聊天对话(文本、图片)功能
2018/07/06 Javascript
js使用ajax传值给后台,后台返回字符串处理方法
2018/08/08 Javascript
基于vue如何发布一个npm包的方法步骤
2019/05/15 Javascript
js中!和!!的区别与用法
2020/05/09 Javascript
微信小程序实现上传照片代码实例解析
2020/08/04 Javascript
JS轮播图的实现方法2
2020/08/25 Javascript
关于Js中new操作符的作用详解
2021/02/21 Javascript
python获取豆瓣电影简介代码分享
2014/01/16 Python
python中将函数赋值给变量时需要注意的一些问题
2017/08/18 Python
Python Web程序部署到Ubuntu服务器上的方法
2018/02/22 Python
1分钟快速生成用于网页内容提取的xslt
2018/02/23 Python
pandas数值计算与排序方法
2018/04/12 Python
Jupyter notebook在mac:linux上的配置和远程访问的方法
2019/01/14 Python
Python3远程监控程序的实现方法
2019/07/15 Python
纽约通行卡:The New York Pass(免费游览纽约90多个景点)
2017/07/29 全球购物
高一历史教学反思
2014/01/13 职场文书
酒店员工职业生涯规划
2014/02/25 职场文书
市场拓展计划书
2014/05/03 职场文书
市场调查策划方案
2014/06/10 职场文书
2014年护士个人工作总结
2014/11/11 职场文书
2014年宣传工作总结
2014/11/18 职场文书
结婚通知短信怎么写
2015/04/17 职场文书
戒赌保证书
2015/05/11 职场文书
Python List remove()实例用法详解
2021/08/02 Python
小程序实现悬浮按钮的全过程记录
2021/10/16 HTML / CSS