Python 中内置了许多和操作时间有关的 API,它们分布在
time
,
datetime
等标准库中,用法繁多且容易混淆,本文将力求清晰地阐述这些 API 的关键部分和区别,帮助你了解并掌握其用法。
下文将分别介绍每个模块的主要目的、核心对象、常用方法以及用途,并在最后做分析对比,如果已经了解这些细节可以直接跳转到结尾的总结对比部分。
另外本文将避免涉及字符串格式化、时区、冬夏令时等更复杂深入的话题。
概括来说,
time
模块通过系统底层的计时器获取自
epoch
以来经过的总秒数(可能为浮点数),即我们常说的 POSIX 时间戳(timestamp)。它的用法较为低阶,适合用做精确计时。对 Unix 系统来说,
epoch
为
1970年1月1日 00:00:00(UTC)
,因此该模块也可以将时间戳转换为具体的日期时间,但表示日期时间的对象结构非常简单,不适合进行复杂的操作和表示。
time
模块的 API 中只有一个类:
time.struct_time
。
struct_time
是一个转换
epoch
以来经过秒数得到的结构化的时间对象,它提供了类似
namedtuple
的 API,可以通过下标或属性名称获取对象的年月日时分秒等属性。调用
gmtime()
,
localtime()
,
strptime()
等方法可得到
struct_time
实例。
>>> st = time.localtime()
time.struct_time(tm_year=2021, tm_mon=4, tm_mday=29, tm_hour=12, tm_min=39, tm_sec=14, tm_wday=3, tm_yday=119, tm_isdst=0)
>>> st.tm_mon
>>> st[1]
从示例中可以看到,struct_time
实例实质是一个数字组成的类元祖序列,该模块中接收 struct_time
实例作为参数的函数都可以直接接收一个同样长度的元祖。它只能简单的记录通过换算时间戳得到的年月日时分等属性,没有提供支持额外操作的其他方法,因此实践中的用途非常有限。
>>> st1 = time.localtime()
>>> st2 = time.localtime()
>>> st2 - st1
Traceback (most recent call last):
File "<input>", line 1, in <module>
st2 - st1
TypeError: unsupported operand type(s) for -: 'time.struct_time' and 'time.struct_time'
time.time()
以浮点数的形式返回自 epoch
以来经过的时间秒数。常见用法是通过计算两次调用之间的间隔来得出程序执行时间。
>>> time.time()
1619665423.683973
>>> now = time.time()
>>> time.gmtime(now)
time.struct_time(tm_year=2021, tm_mon=4, tm_mday=29, tm_hour=4, tm_min=51, tm_sec=54, tm_wday=3, tm_yday=119, tm_isdst=0)
>>> time.gmtime()
time.struct_time(tm_year=2021, tm_mon=4, tm_mday=29, tm_hour=4, tm_min=51, tm_sec=56, tm_wday=3, tm_yday=119, tm_isdst=0)
time.strftime(format[, t])
将一个 struct_time
对象按指定的 format
编码格式化为字符串,t
的默认值是 time.localtime()
的返回值。
>>> time.strftime('%H:%M:%S')
'13:10:37'
time.strptime(string[, format])
将一个字符串按指定的 format
编码解析为 struct_time
对象,format
的默认值为 "%a %b %d %H:%M:%S %Y"
。
>>> time.strptime("30 Nov 00", "%d %b %y")
time.struct_time(tm_year=2000, tm_mon=11, tm_mday=30, tm_hour=0, tm_min=0,
tm_sec=0, tm_wday=3, tm_yday=335, tm_isdst=-1)
如上示例,解析时未提供的时间单位将使用默认值填充。
datetime
模块支持日期和时间的运算,但实现的重点是为输出格式化和操作提供高效的属性提取。
datetime
模块提供了一些用于操作日期和时间的类。该模块的绝大部分功能都围绕着以下 4 个类(以及另外两个关于时区的类)的方法和属性来实现。一个容易让人混淆的点是,虽然它们全都是 Python 类,但在命名中并未遵循首字母大写的惯例,在导入时看上去就像是 datetime
下的子包或者子模块。
我们将简要介绍每一个类常用的实例构造方式、支持的操作符、实例方法以及实例属性。
表示日期类型。
实例化 date
类,需要传入日期对应的年月日参数。
>>> date(2021, 4, 29)
datetime.date(2021, 4, 29)
支持与另一 date
对象进行 ==
,≤
,<
,≥
,>
等比较操作。
支持与 timedelta
对象进行加减操作,结果依然为 date
对象。
支持与另一 date
对象进行相减操作,得到 timedelta
对象。
支持哈希。
strftime(self, fmt)
按指定的 fmt
格式化编码返回当前 date
对象的字符串表示。
>>> d1 = date.today()
>>> d1.strftime('%Y-%m-%d')
'2021-04-29'
支持哈希。
time
对象不支持与 time
或 timedelta
进行加减操作,如果我们想计算两个 time
对象之间的时间间隔,可以使用 datetime.combine()
将它们处理为日期相同的 datetime
对象再进行计算:
>>> datetime.combine(date.today(), t2) - datetime.combine(date.today(), t1)
datetime.timedelta(seconds=4440)
strftime(self, fmt)
按指定的 fmt
格式化编码返回当前 time
对象的字符串表示。
>>> t = time.fromisoformat('17:32:10')
>>> t.strftime('%Hh %Mm %Ss')
'17h 32m 10s'
replace(self,hour=None,minute=None,second=None,microsecond=None, tzinfo=True, *,fold=None)
返回替换当前 time
对象的某一属性后的副本。
>>> t.replace(hour=20)
datetime.time(20, 27, 55)
表示包含日期时分的时间类型,是 date
的子类,因此也继承了 date
的所有属性和方法。它的实例还可以视作 date
和 time
实例的组合体,因此同时具备了两种对象的大部分方法和属性。
下文的介绍中不包含从 date
继承的方法和属性。
实例化 datetime
类并传入对应参数,接收参数为 date
和 time
实例化参数的组合,其中日期参数为必填参数,其他参数有默认值。
>>> datetime(year=2021, month=4, day=29)
datetime.datetime(2021, 4, 29, 0, 0)
>>> datetime.now()
datetime.datetime(2021, 4, 29, 16, 4, 53, 648203)
>>> datetime.utcnow()
datetime.datetime(2021, 4, 29, 8, 5, 1, 671572)
调用 datetime.fromtimestamp(timestamp)
或 datetime.utcfromtimestamp(timestamp)
类方法并传入时间戳,区别为实例的对应时区不同。
>>> import time
>>> datetime.utcfromtimestamp(time.time())
datetime.datetime(2021, 4, 29, 8, 6, 4, 798136)
>>> datetime.fromtimestamp(time.time())
datetime.datetime(2021, 4, 29, 16, 6, 26, 251251)
datetime
支持与 date
进行相等比较,但结果一定为 False
,除此之外只支持与另一 datetime
对象执行 ==
,≤
,<
,≥
,>
等比较操作。
支持与 timedelta
相加,结果为 datetime
;支持与 timedelta
对象进行加减,结果依然为 datetime
对象,与另一 datetime
对象进行相减,得到 timedelta
对象。
同样支持哈希。
除了从 date
继承的 strftime()
、timetuple()
、isoformat()
和 replace()
等方法外,还拥有以下方法:
timestamp(self)
返回一个浮点数格式的 POSIX 时间戳。
>>> dt = datetime.now()
>>> dt.timestamp()
1619685580.762657
>>> td = dt1 - dt2
datetime.timedelta(days=-1, seconds=86395, microseconds=573188)
>>> td.total_seconds()
-4.426812
>>> abs(td)
datetime.timedelta(seconds=4, microseconds=426812)
>>> d1 = timedelta(minutes=3, seconds=35)
datetime.timedelta(days=0, seconds=215, microseconds=0)
>>> d2 = timedelta(days=1)
datetime.timedelta(days=1)
>>> d2.seconds
time
模块,获取系统时间戳,主要用于计时或表示某一时间点,可以通过数值元祖表示结构化的日期时间,但不支持进一步的转换或操作。
datetime
模块,基于时间戳构建高阶的日期、时间、间隔等对象,支持丰富的转换方式和操作。
date
只表示日期。支持与 date
或 timedelta
进行加减操作.
time
只表示时分。不支持与 time
或 timedelta
进行加减操作,计算间隔需要先转换成 datetime
对象。
datetime
同时表示日期和时分的时间对象。同时具备 date
和 time
对象的行为和属性,可以从中解析出单独的 date
和 time
对象。
timedelta
表示两个时间之间的间隔。只通过 days
、seconds
,microseconds
这 3 种单位来表示。
字符串格式化与解析:
time.struct_time
、datetime.date
、datetime.time
、datetime.datetime
等对象都可以通过 strftime()
(string format)实例方法或函数转换为指定格式的字符串。
特定格式的字符串仅可以通过 strptime()
(string parse)类方法或函数直接转换为time.struct_time
、datetime.datetime
对象。
ISO 格式字符串格式化与解析:
datetime.date
、datetime.time
、datetime.datetime
等对象都可以通过 isoformat()
实例方法转换为 ISO 8601 格式的字符串。
ISO 8601 格式的字符串可以通过 fromisoformat()
类方法直接转换为datetime.date
、datetime.time
、datetime.datetime
对象。
用一张时序图总结上文内容:
[ts]
表示该参数具有默认值是可选的。
请注意区分图中的实例方法、类方法以及模块函数:
名称中以 time.
开头的均为 time
模块的函数
名称中以 obj.
开头的均为 date
、time
或 datetime
对象的实例方法
其余名称的函数均为类方法
作者:Waynerv
链接:https://waynerv.com/posts/differences-bettween-time-and-datetime-in-python/
许可:CC BY-NC-SA 4.0