6 个 Python datetime 库

有许多库可以更轻松地在 Python 中测试、转换和读取日期和时间信息。
232 位读者喜欢这篇文章。
Hands on a keyboard with a Python book

WOCinTech Chat。由 Opensource.com 修改。CC BY-SA 4.0

很久以前,我们中的一人(Lacey)花了一个多小时盯着 Python 文档 中的表格,该表格描述了日期和时间格式化字符串。我很难理解其中一个特定的难题,因为我试图编写代码将来自 API 的 datetime 字符串转换为 Python datetime 对象,所以我寻求了帮助。

“你为什么不直接使用 dateutil?”有人问。

读者,如果你从本月的 Python 专栏中只学到一件事,那就是将 datetime 字符串转换为 datetime 对象有比 datetimestrptime 更简单的方法,我们就会认为自己是成功的。

但除了轻松地将字符串转换为更有用的 Python 对象之外,还有许多库提供了有用的方法和工具,可以更轻松地管理时间测试、将时间转换为不同的时区、以人类可读的格式传递时间信息等等。如果这是你第一次涉足 Python 中的日期和时间,请休息一下并阅读如何在 Python 中处理日期和时间。要理解为什么在编程中处理日期和时间很困难,请阅读 程序员对时间的错误认知

本文将向你介绍

请随意跳过你已经熟悉的库,专注于你陌生的库。

内置的datetime模块

在深入研究其他库之前,让我们回顾一下如何使用 datetime 模块将日期字符串转换为 Python datetime 对象。

假设我们从 API 收到此日期字符串,并且需要它作为 Python datetime 对象存在

2018-04-29T17:45:25Z

此字符串包括

  • YYYY-MM-DD 格式的日期
  • 字母“T”表示时间即将到来
  • HH:II:SS 格式的时间
  • 时区指示符“Z”,表示此时间为 UTC (阅读更多关于 datetime 字符串格式的信息)

要使用 datetime 模块将此字符串转换为 Python datetime 对象,你将从 strptime 开始。datetime.strptime 接受日期字符串和格式化字符,并返回 Python datetime 对象。

我们必须手动将 datetime 字符串的每个部分转换为 Python 的 datetime.strptime 可以理解的适当格式化字符串。四位数的年份用 %Y 表示。两位数的月份是 %m。两位数的日期是 %d。24 小时制的小时是 %H,零填充的分钟是 %M。零填充的秒是 %S

需要仔细查看 文档 中的表格才能得出这些结论。

由于字符串中的“Z”表示此 datetime 字符串位于 UTC 中,因此我们可以忽略格式化中的这一点。(现在,我们暂时不考虑时区。)

此转换的代码如下所示

$ from datetime import datetime 
$ datetime.strptime('2018-04-29T17:45:25Z', '%Y-%m-%dT%H:%M:%SZ')
datetime.datetime(2018, 4, 29, 17, 45, 25)

格式化字符串难以阅读和理解。我必须手动考虑原始字符串中的字母“T”和“Z”,以及标点符号和格式化字符串,如 %S%m。不太熟悉 datetime 的人阅读我的代码可能会觉得难以理解,即使其含义已得到充分记录,因为它难以阅读。

让我们看看其他库如何处理这种转换。

Dateutil

dateutil 模块 提供了 datetime 模块的扩展。

为了继续上面的解析示例,使用 dateutil 实现相同的结果要简单得多

$ from dateutil.parser import parse
$ parse('2018-04-29T17:45:25Z')
datetime.datetime(2018, 4, 29, 17, 45, 25, tzinfo=tzutc())

如果包含时区,dateutil 解析器将自动返回字符串的时区。由于我们的时区是 UTC,你可以看到返回的 datetime 对象是 UTC。如果你希望 parse 完全忽略时区信息并返回一个朴素的 datetime 对象,你可以将参数 ignoretz=True 传递给 parse,如下所示

$ from dateutil.parser import parse
$ parse('2018-04-29T17:45:25Z', ignoretz=True)
datetime.datetime(2018, 4, 29, 17, 45, 25)

Dateutil 还可以解析更易于人类阅读的日期字符串

$ parse('April 29th, 2018 at 5:45 pm')
datetime.datetime(2018, 4, 29, 17, 45)

dateutil 还提供诸如 relativedelta 之类的工具,用于计算两个 datetime 之间的时间差或向 datetime 添加/删除时间,rrule 用于创建重复出现的 datetime,以及 tz 用于处理时区,以及其他工具。

Arrow

Arrow 是另一个库,其目标是使操作、格式化和以其他方式处理日期和时间对人类更加友好。它包含 dateutil,并且根据其 文档,旨在“帮助你使用更少的导入和更少的代码来处理日期和时间。”

为了回到我们的解析示例,以下是如何使用 Arrow 将日期字符串转换为 Arrow datetime 类的实例

$ import arrow 
$ arrow.get('2018-04-29T17:45:25Z')
<Arrow [2018-04-29T17:45:25+00:00]>

你还可以在 get() 的第二个参数中指定格式,就像使用 strptime 一样,但 Arrow 会尽力自行解析你提供的字符串。get() 返回 Arrow datetime 类的实例。要使用 Arrow 获取 Python datetime 对象,请链接 datetime 如下

$ arrow.get('2018-04-29T17:45:25Z').datetime
datetime.datetime(2018, 4, 29, 17, 45, 25, tzinfo=tzutc())

使用 Arrow datetime 类的实例,你可以访问 Arrow 的其他有用方法。例如,它的 humanize() 方法将 datetime 转换为人类可读的短语,如下所示

$ import arrow
$ utc = arrow.utcnow()
$ utc.humanize()
'seconds ago'

在 Arrow 的 文档 中阅读更多关于 Arrow 有用方法的信息。

Moment

Moment 的创建者认为它是“alpha 质量”,但即使它处于早期阶段,它也很受欢迎,我们想提及它。

Moment 将字符串转换为更有用内容的方法很简单,类似于我们之前提到的库

$ import moment
$ moment.date('2018-04-29T17:45:25Z')
<Moment(2018-04-29T17:45:25)>

与其他库一样,它最初返回其自身 datetime 类的实例。要返回 Python datetime 对象,请添加另一个 date() 调用。

$ moment.date('2018-04-29T17:45:25Z').date
datetime.datetime(2018, 4, 29, 17, 45, 25, tzinfo=<StaticTzInfo 'Z'>)

这将把 Moment datetime 类转换为 Python datetime 对象。

Moment 还提供了使用人类可读的语言创建新日期的方法。要创建明天的日期

$ moment.date("tomorrow")
<Moment(2018-04-06T11:24:42)>

它的 addsubtract 命令接受关键字参数,使操作日期变得简单。要获得后天,Moment 将使用以下代码

$ moment.date("tomorrow").add(days=1)
<Moment(2018-04-07T11:26:48)>

Maya

Maya 包含了其他流行的库,这些库在 Python 中处理 datetime,包括 Humanizepytzpendulum 等。该项目的目标是使人们更容易处理 datetime。

Maya 的 README 包含几个有用的示例。以下是如何使用 Maya 重现之前的解析示例

$ import maya
$ maya.parse('2018-04-29T17:45:25Z').datetime()
datetime.datetime(2018, 4, 29, 17, 45, 25, tzinfo=<UTC>)

请注意,我们必须在 maya.parse() 之后调用 .datetime()。如果我们跳过此步骤,Maya 将返回 MayaDT 类的实例:<MayaDT epoch=1525023925.0>

由于 Maya 融合了许多有用的 datetime 库,因此它可以利用其 MayaDT 类的实例来执行诸如使用 slang_time() 方法将 timedelta 转换为纯语言,并将 datetime 间隔保存在单个类的实例中。以下是如何使用 Maya 将 datetime 表示为人类可读的短语

$ import maya
$ maya.parse('2018-04-29T17:45:25Z').slang_time()
'23 days from now`

显然,slang_time() 的输出将根据你与 datetime 对象的相对接近程度而变化。

Delorean

Delorean,以回到未来电影中的时间旅行汽车命名,特别有助于操作 datetime:将 datetime 转换为其他时区以及添加或减去时间。

Delorean 需要有效的 Python datetime 对象才能工作,因此如果你有需要使用的字符串 datetime,最好与上面提到的库之一结合使用。例如,将 Delorean 与 Maya 一起使用

$ import maya 
$ d_t = maya.parse('2018-04-29T17:45:25Z').datetime()

现在,有了可用的 datetime 对象 d_t,你可以使用 Delorean 执行诸如将 datetime 转换为美国东部时区之类的操作

$ from delorean import Delorean
$ d = Delorean(d_t)
$ d
Delorean(datetime=datetime.datetime(2018, 4, 29, 17, 45, 25), timezone='UTC')
$ d.shift('US/Eastern')
Delorean(datetime=datetime.datetime(2018, 4, 29, 13, 45, 25), timezone='US/Eastern')

看到小时数如何从 17 变为 13 了吗?

你还可以使用自然语言方法来操作 datetime 对象。要获取 2018 年 4 月 29 日(我们一直在使用的日期)之后的下一个星期五

$ d.next_friday()
Delorean(datetime=datetime.datetime(2018, 5, 4, 13, 45, 25), timezone='US/Eastern')

在 Delorean 的 文档 中阅读更多关于 Delorean 的信息。

Freezegun

Freezegun 是一个库,可帮助你在 Python 代码中使用特定的 datetime 进行测试。使用 @freeze_time 装饰器,你可以为测试用例设置特定的日期和时间,并且所有对 datetime.datetime.now()datetime.datetime.utcnow() 等的调用都将返回你指定的日期和时间。例如

from freezegun import freeze_time
import datetime

@freeze_time("2017-04-14")
def test():
    assert datetime.datetime.now() == datetime.datetime(2017, 4, 14)

要跨时区进行测试,你可以将 tz_offset 参数传递给装饰器。freeze_time 装饰器还接受更简单的语言日期,例如 @freeze_time('April 4, 2017')


上面提到的每个库都提供了一组不同的特性和功能。可能很难决定哪一个最适合你的需求。Maya 的创建者 Kenneth Reitz 说:“所有这些项目都相互补充,并且是朋友。”

这些库共享一些功能,但不共享其他功能。有些擅长时间操作,另一些则擅长解析。但是它们都具有使你更轻松地处理日期和时间的目标。下次你发现自己对 Python 的内置 datetime 模块感到沮丧时,我们希望你选择其中一个库进行实验。

User profile image.
Lacey Williams Henschel 是 REVSYS 的软件工程师,也是 DjangoCon US 组织团队的成员。过去,她曾担任 DjangoCon US 的主席,组织了多个 Django Girls 工作坊,为 Treehouse 教授课程,并撰写了关于技术活动可访问性的文章。
User profile image.
Jeff Triplett 是一位开源开发者,在 REVSYS 担任软件工程师、顾问和合伙人。他是 Python 软件基金会的董事会成员。他偶尔在他的网站上写博客。

3 条评论

这是一个我曾经使用过的 datetime 方法,用于获取脚本运行了多长时间的反馈——当时我有一个脚本正在处理一些大型文本文件,并创建 Scribus 文档。有些运行时间超过一个小时。
在脚本的早期,我放入了
start = datetime.datetime.now().replace(microsecond=0)

然后在接近结尾处
finish = datetime.datetime.now().replace(microsecond=0)
interval = finish - start
scribus.messageBox("Interval",'这花费了 ' + str(interval) + ' 完成',button1=scribus.BUTTON_OK)

它对于发现加速脚本的修改非常有用。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.