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.683973time.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
实例属性
yearmonthday
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)
实例属性
hourminutesecond- 以及 
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.762657date(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对象的实例方法 - 其余名称的函数均为类方法
 
 - 名称中以