Python 中内置了许多和操作时间有关的 API,它们分布在 time
, datetime
等标准库中,用法繁多且容易混淆,本文将力求清晰地阐述这些 API 的关键部分和区别,帮助你了解并掌握其用法。
下文将分别介绍每个模块的主要目的、核心对象、常用方法以及用途,并在最后做分析对比,如果已经了解这些细节可以直接跳转到结尾的总结对比部分。
另外本文将避免涉及字符串格式化、时区、冬夏令时等更复杂深入的话题。
time 模块
概括来说,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
实例。
|
|
从示例中可以看到,struct_time
实例实质是一个数字组成的类元祖序列,该模块中接收 struct_time
实例作为参数的函数都可以直接接收一个同样长度的元祖。它只能简单的记录通过换算时间戳得到的年月日时分等属性,没有提供支持额外操作的其他方法,因此实践中的用途非常有限。
|
|
常见用途与函数
计时
time.time()
以浮点数的形式返回自epoch
以来经过的时间秒数。常见用法是通过计算两次调用之间的间隔来得出程序执行时间。1 2
>>> time.time() 1619665423.683973
time.sleep(seconds)
暂停调用线程的执行,暂停时间为给定的秒数。经常用于测试模拟,实际的暂停时间可能超出给定秒数。time.perf_counter()
是计算较短时间间隔的更好方法,结果更为精确,在计算执行时间时可替代上述的time.time()
。1 2 3 4
>>> start = time.perf_counter() >>> end = time.perf_counter() >>> end - start 2.731515233999744
在
struct_time
和时间戳之间进行转换time.gmtime([secs])
将给定秒数转换为一个 UTC 时区struct_time
对象,若未提供秒数将使用time.time()
得到的返回值。1 2 3 4 5
>>> 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.localtime([secs])
将给定秒数转换为一个本地时区的struct_time
对象,若未提供秒数将使用time.time()
得到的返回值。1 2
>>> time.localtime() time.struct_time(tm_year=2021, tm_mon=4, tm_mday=29, tm_hour=12, tm_min=53, tm_sec=38, tm_wday=3, tm_yday=119, tm_isdst=0)
time.mktime(t)
将一个struct_time
对象转换为秒数,该对象将被当做本地时区处理,效果刚好与time.localtime([secs])
相反。1 2
>>> time.mktime(time.localtime()) 1619672313.0
在
struct_time
和字符串之间进行转换time.strftime(format[, t])
将一个struct_time
对象按指定的format
编码格式化为字符串,t
的默认值是time.localtime()
的返回值。1 2
>>> time.strftime('%H:%M:%S') '13:10:37'
time.strptime(string[, format])
将一个字符串按指定的format
编码解析为struct_time
对象,format
的默认值为"%a %b %d %H:%M:%S %Y"
。1 2 3
>>> 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
模块支持日期和时间的运算,但实现的重点是为输出格式化和操作提供高效的属性提取。
datetime
模块提供了一些用于操作日期和时间的类。该模块的绝大部分功能都围绕着以下 4 个类(以及另外两个关于时区的类)的方法和属性来实现。一个容易让人混淆的点是,虽然它们全都是 Python 类,但在命名中并未遵循首字母大写的惯例,在导入时看上去就像是 datetime
下的子包或者子模块。
我们将简要介绍每一个类常用的实例构造方式、支持的操作符、实例方法以及实例属性。
date
表示日期类型。
实例构造方式
实例化
date
类,需要传入日期对应的年月日参数。1 2
>>> date(2021, 4, 29) datetime.date(2021, 4, 29)
调用
date.fromtimestamp(timestamp)
类方法,需要传入的参数为通过time
模块获取的epoch
以来秒数(即时间戳)。1 2
>>> date.fromtimestamp(time.time()) datetime.date(2021, 4, 29)
调用
date.today()
类方法,实质是以当前时间戳作为参数调用date.fromtimestamp()
类方法。调用
date.fromisoformat(date_string)
类方法,这是一种较为直观的创建方法:1 2
>>> date.fromisoformat('2021-04-29') datetime.date(2021, 4, 29)
支持的操作符
- 支持与另一
date
对象进行==
,≤
,<
,≥
,>
等比较操作。 - 支持与
timedelta
对象进行加减操作,结果依然为date
对象。 - 支持与另一
date
对象进行相减操作,得到timedelta
对象。 - 支持哈希。
实例方法
strftime(self, fmt)
按指定的fmt
格式化编码返回当前date
对象的字符串表示。1 2 3
>>> d1 = date.today() >>> d1.strftime('%Y-%m-%d') '2021-04-29'
isoformat(self)
返回当前date
对象的iso
字符串表示。1 2
>>> d1.isoformat() '2021-04-29'
timetuple(self)
将当前date
对象转换成time
模块的struct_time
对象并返回,时分秒等属性使用默认值填充。1 2
>>> d1.timetuple() time.struct_time(tm_year=2021, tm_mon=4, tm_mday=29, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=119, tm_isdst=-1)
replace(self, year=None, month=None, day=None)
返回替换当前date
对象的某一属性后的副本。1 2
>>> d1.replace(day=30) datetime.date(2021, 4, 30)
weekday(self)
返回当前date
对象所属的星期,从 0 开始。1 2
>>> d1.weekday() 3
实例属性
year
month
day
time
表示时间(时分秒)类型。
实例构造方式
time
不支持通过时间戳构造实例。
实例化
time
类并传入对应参数。需要传入时间对应的时分秒微秒等参数,参数均有取值范围且默认值为 0。1 2
>>> date(2021, 4, 29) datetime.date(2021, 4, 29)
通过调用
fromisoformat(cls, time_string)
类方法,从iso
字符串中创建一个实例:1 2
>>> time.fromisoformat('17:32:10') datetime.time(17, 32, 10)
支持的操作符
- 支持与另一
time
对象进行==
,≤
,<
,≥
,>
等比较操作。 - 支持哈希。
time
对象不支持与 time
或 timedelta
进行加减操作,如果我们想计算两个 time
对象之间的时间间隔,可以使用 datetime.combine()
将它们处理为日期相同的 datetime
对象再进行计算:
|
|
实例方法
strftime(self, fmt)
按指定的fmt
格式化编码返回当前time
对象的字符串表示。1 2 3
>>> t = time.fromisoformat('17:32:10') >>> t.strftime('%Hh %Mm %Ss') '17h 32m 10s'
isoformat(self)
返回当前time
对象的iso
字符串表示。1 2 3
>>> t = time(hour=17, minute=27, second=55) >>> t.isoformat() '17:27:55'
replace(self,hour=None,minute=None,second=None,microsecond=None, tzinfo=True, *,fold=None)
返回替换当前time
对象的某一属性后的副本。1 2
>>> t.replace(hour=20) datetime.time(20, 27, 55)
实例属性
hour
minute
second
- 以及
micorsecond
,tzinfo
,fold
等属性
datetime
表示包含日期时分的时间类型,是 date
的子类,因此也继承了 date
的所有属性和方法。它的实例还可以视作 date
和 time
实例的组合体,因此同时具备了两种对象的大部分方法和属性。
下文的介绍中不包含从 date
继承的方法和属性。
实例构造方式
实例化
datetime
类并传入对应参数,接收参数为date
和time
实例化参数的组合,其中日期参数为必填参数,其他参数有默认值。1 2
>>> datetime(year=2021, month=4, day=29) datetime.datetime(2021, 4, 29, 0, 0)
调用
datetime.now()
或datetime.utcnow()
类方法,区别为实例的对应时区不同。1 2 3 4
>>> 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)
类方法并传入时间戳,区别为实例的对应时区不同。1 2 3 4 5
>>> 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.fromisoformat(time_string)
类方法,从iso
字符串中创建一个实例:1 2
>>> datetime.fromisoformat('2021-04-29 16:09:32') datetime.datetime(2021, 4, 29, 16, 9, 32)
通过调用
datetime.combine(date, time)
类方法,从date
实例和time
实例中创建一个新的datetime
实例。1 2
>>> datetime.combine(date.today(), time(16, 12)) datetime.datetime(2021, 4, 29, 16, 12)
通过调用
datetime.strptime(date_string, format)
类方法,解析格式化字符串并创建一个新的实例。
支持的操作符
datetime
支持与date
进行相等比较,但结果一定为False
,除此之外只支持与另一datetime
对象执行==
,≤
,<
,≥
,>
等比较操作。- 支持与
timedelta
相加,结果为datetime
;支持与timedelta
对象进行加减,结果依然为datetime
对象,与另一datetime
对象进行相减,得到timedelta
对象。 - 同样支持哈希。
实例方法
除了从 date
继承的 strftime()
、timetuple()
、isoformat()
和 replace()
等方法外,还拥有以下方法:
timestamp(self)
返回一个浮点数格式的 POSIX 时间戳。1 2 3
>>> dt = datetime.now() >>> dt.timestamp() 1619685580.762657
date(self)
返回一个代表日期部分的date
对象。1 2
>>> dt.date() datetime.date(2021, 4, 29)
time(self)
返回一个代表时分部分的time
对象。1 2
>>> dt.time() datetime.time(16, 39, 40, 762657)
实例属性
同时具有date
和 time
实例的所有属性。
timedelta
表示两个 datetime
对象之间的差异。
实例构造方式
实例化
timedelta
类并传入对应参数,接收参数与datetime
类基本相同但不包括年,默认值均为 0。1 2
>>> timedelta(days=2) datetime.timedelta(days=2)
对两个
datetime
执行相减:1 2 3 4
>>> dt1 = datetime.now() >>> dt2 = datetime.now() >>> dt2 -dt1 datetime.timedelta(seconds=4, microseconds=476390)
支持的操作符
只支持与另一
timedelta
进行比较,进行==
,≤
,<
,≥
,>
等比较操作。timedelta
对象支持支持加减操作,datetime
与timedelta
相加或相减仍然返回datetime
。timedelta
还支持乘除模除等操作符。支持哈希。
timedelta
是有符号的,支持abs()
函数,可返回两个datetime
之间的绝对间隔。1 2 3 4 5 6 7 8 9
>>> dt1 = datetime.now() >>> dt2 = datetime.now() >>> td = dt1 - dt2 >>> td datetime.timedelta(days=-1, seconds=86395, microseconds=573188) >>> td.total_seconds() -4.426812 >>> abs(td) datetime.timedelta(seconds=4, microseconds=426812)
实例方法
total_seconds(self)
返回该时间间隔的所有秒数。
|
|
实例属性
timedelta
只通过 days
、seconds
,microseconds
这 3 种单位进行组合来保存时间间隔,可通过对应属性获取数值。
|
|
总结对比
time
与 datetime
模块的区别:
time
模块,获取系统时间戳,主要用于计时或表示某一时间点,可以通过数值元祖表示结构化的日期时间,但不支持进一步的转换或操作。datetime
模块,基于时间戳构建高阶的日期、时间、间隔等对象,支持丰富的转换方式和操作。
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
对象的实例方法 - 其余名称的函数均为类方法
- 名称中以