User:Xyy23330121/Python/日期与时间
许多程序需要处理日期与时间。Python 提供了两个模块可以用于处理时间。
本页面的内容太多,所以大部分内容都被改为了超链接索引。如果读者需要详细了解一些信息,请直接点击表格中或文章中的超链接。
许多平台都会使用纪元秒数来存储时间。纪元秒数是自 1970 年 1 月 1 日 00:00:00 以来,经过的秒数(用浮点数表示)。简单来讲:0.0
代表 1970 年 1 月 1 日 00:00:00,而 1.0
代表 1970 年 1 月 1 日 00:00:01 。
在实际操作时,则是顺延到月份的最后一天再插入闰秒。
这一记录方法通常是不包含闰秒的。闰秒是 UTC 标准下、对满足一定条件的一天、在其最后最后添加一秒的规则。在所有符合 POSIX 标准的平台上,闰秒都不会被记录在总秒数中。比如:
- 2016 年 12 月 31 日有闰秒,使得比如 2016 年 12 月 31 日 23:59:60 的时间是存在的(而非错误地没有进位)。而在 POSIX 标准的平台中,这一秒被直接忽略掉——根本不表示这一秒之间的时间。
时间还可以由 ISO 8601 标准给出的的字符串来储存和进行交流。
除上述方法以外,Python 还通过几种方式储存时间。这里简单列出这些方法的特征,读者应按照自身需求来选择对应的方法。
和其它程序语言中,整数在 -32768 ~ 32767 之间不同,Python 的整数可以表示任意整数(只要系统内存能存储)。所以 Python 可以使用整数来解决纪元秒数中、浮点数精度的问题。
备注:1,000,000,000 纳秒(即 1e9
纳秒)等于 1 秒。
time 模块中,包含各个时间属性的对象。除基本的年、月、日、时、分、秒外,该对象还包含:
- 在一年中第几个星期
- 是星期几
- 在一年中第几天
- 夏令时信息
- 时区信息
除此之外,该对象自带的输出方式较为多样,可以简单地把该对象按自定义格式转化为字符串并输出。
datetime 模块中,包含各个时间属性的对象。除基本的年、月、日、时、分、秒外,该对象还包含“微秒数”,从而它能表示比 time.struct_time 更精确的时间。但该方法对时区的支持较为繁琐。
该对象在时间计算相关的支持较为丰富,比如,它支持直接求两个时间的间隔。
该对象可以被认为是以下两类对象的结合:
datetime 模块中,表示日期的类型。
datetime 模块中,表示时间的类型。
获得纪元秒数。对闰秒的处理基于具体平台。Windows 和大多数 Unix 系统均忽略闰秒。
获得纪元“纳秒数”,使用它的好处是不会因浮点数舍入而丢失精度。
把纪元秒数转换为形如 "Thu Jan 11 00:00:00 1970"
的内容。
如果不输入 t
,则使用当前时间生成。如果日期仅有 1 位,则会用空格进行缩进,比如:"Thu Jan 1 00:00:00 1970"
可以通过索引和属性名来访问 time.struct_time 实例的内容。这些内容均为只读,不可修改。
索引 | 属性 | 值 |
---|---|---|
0 | tm_year | 整数,比如 1970 |
1 | tm_mon | 1 ~ 12 的整数 |
2 | tm_day | 1 ~ 31 的整数 |
3 | tm_hour | 0 ~ 23 的整数 |
4 | tm_min | 0 ~ 59 的整数 |
5 | tm_sec | 0 ~ 61 的整数 60 代表闰秒,而 61 是历史遗留问题 |
6 | tm_wday | 0 ~ 6 的整数 其中,周一为 0 |
7 | tm_yday | 1 ~ 366 的整数 |
8 | tm_isdst | 夏令时信息: 0 代表不实行夏令时 1 代表实行夏令时 -1 代表未知,会按情况自动判断 |
N/A | tm_zone | 时区名称的缩写,比如“中国标准时间” |
N/A | tm_gmtoff | 以秒为单位的时区信息,比如 28800。 28800 秒是 8 小时,即 UTC+8 时区。 |
这是 time.struct_time 与各种其它类型或表示方式之间的转化
类型 | 转换 | 注释 | |
---|---|---|---|
转换为 time.struct_time | 从 time.struct_time 转换 | ||
当前时间 | 可以 | 没必要 | 用当前时间生成对应的 time.struct_time 实例。 可以选择生成零时区的对象或本地时间的对象。 |
纪元秒数 | 可以 | 可以 | time.struct_time 和表示纪元秒数的浮点数之间的转换。 可以选择转换为零时区的对象或本地时间的对象。 |
字符串格式化 | 可以 | 可以 | “字符串格式化”指的是读者自定义的输出方式, 它会输出形如 "Thu Jan 11 00:00:00 1970" 的字符串。其使用方法和 printf 式字符串格式化相似。 |
属性 | 值 |
---|---|
year | 1 ~ 9999 之间的整数 |
month | 1 ~ 12 之间的整数 |
day | 对于不同月份,为1 ~ 28 至 1 ~ 31 之间的整数 |
hour | 0 ~ 23 之间的整数 |
minute | 0 ~ 59 之间的整数 |
second | 0 ~ 59 之间的整数 |
microsecond | 0 ~ 999999 之间的整数 |
tzinfo | 时区信息对象 |
fold | 用于夏令时等导致时间重复的情况。 |
本章将把 datetime.datetime、datetime.date、datetime.time 放在一起讲。
在 datetime 模块中有 datetime 类,又有 date 类——而 datetime 类的实例又有 date 方法。datetime.datetime.date 和 datetime.date 是完全不同的两个东西。在实际阅读代码时,如此多的重复文字容易造成混淆。因此,为防止混淆以及简便起见,本章节以下的内容均使用了 import datetime as dt
。
dt.datetime 的各个属性也是只读的。我们可以见右侧表格。
tzinfo 是时区信息,时区信息的详细内容将在下面讲述。
fold 属性是用于处理时间重复的情况的。以美国为例,因夏令时等原因,导致 2024年11月3日13:00 出现两次的情况,则以 0 标记第一次对应的时间,而以 1 标记第二次对应的时间。
在中国,就算是需要按季节区分作息时间的情况,也有“夏季作息时间表”(而非夏令时)。我们一般不需要考虑这些内容——让 fold 留空即可。
dt.date 仅有以上属性中的年月日。而 dt.time 仅有以上属性中、年月日以外的部分。
除了属性给出的信息之外,我们还可以获取以下信息:
信息 | 获取信息使用的实例方法 | 注释 | |
---|---|---|---|
dt.datetime | dt.date | ||
星期 | dt.datetime.weekday() | dt.date.weekday() | 返回一个整数:星期一为0,星期天为6。 |
ISO 星期 | dt.datetime.isoweekday() | dt.date.isoweekday() | 返回一个整数:星期一为1,星期天为7 |
以下是 dt 模块中已经给出的转化方法。标注“不可以”的是没有直接给出的转化方法,必须用多个步骤才能进行转化。
类型 | 转化为 dt 中类型的实例 | 由 dt 中类型的实例转化 | 注释 | ||||
---|---|---|---|---|---|---|---|
dt.datetime | dt.date | dt.time | dt.datetime | dt.date | dt.time | ||
当前时间 | 可以 | 可以 | 不可以 | 没必要 | 没必要 | 没必要 | 用当前时间生成对应的实例 |
纪元秒数 | 可以 | 可以 | 不可以 | 可以 | 不可以 | 不可以 | |
ISO 8601 字符串 | 可以 | 可以 | 可以 | 可以 | 可以 | 可以 | 特别的,使用str()函数来转换,对 dt.date 和 dt.time 也会返回相同的结果。对 dt.datetime,则会把日期部分和时间部分之间的分隔符改为空格。 |
属性 | 可以 | 可以 | 可以 | 直接调用属性即可 | 输入各个属性的数值,生成对应的对象 | ||
天数序号 | 可以 | 可以 | 没必要 | 可以 | 可以 | 没必要 | 输入自公元元年以来的天数,返回对应的 dt.datetime 对象。 |
time.struct_time | 可以 | 可以 | 不可以 | 不可以 | 不可以 | 不可以 | 输入自公元元年以来的天数,返回对应的 dt.datetime 对象。 |
字符串格式化 | 可以 | 不可以 | 不可以 | 可以 | 可以 | 可以 | 特别的,这些对象除 printf 式方法外,还支持 str.format 方法。 |
ISO 周历 | 可以 | 可以 | 没必要 | 可以 | 可以 | 没必要 | 这里“ISO 周历”指的是 ISO 8601 标准下,星期表示法提供的年、周、星期三个数字。 |
类型 | 修改的对象 | 注释 | ||
---|---|---|---|---|
dt.datetime | dt.date | dt.time | ||
属性 | 可以 | 可以 | 可以 | 对已有实例输入属性和数值,返回属性被修改后的副本 |
时区 | 可以 | 不可以 | 不可以 | 返回表示相同时间,但时区不同的对象 |
classmethod dt.datetime.combine(date, time, tzinfo=time.tzinfo)
将 dt.date 对象和 dt.time 对象组合成 dt.datetime 对象。组合时,可以额外设置一次时区信息。除此之外,我们还有以下方法。
方法 | 结果 |
---|---|
dt.datetime.date() | 返回对应 dt.datetime 实例的 dt.date 对象。 |
dt.datetime.time() | 返回对应 dt.datetime 实例的 dt.time 对象,时区信息为 None 。
|
dt.datetime.timetz() | 返回对应 dt.datetime 实例的 dt.time 对象,保留时区信息。 |
属性名 | 解释 | 范围 |
---|---|---|
days | 天数 | -99999999 到 999999999 之间的整数 |
seconds | 秒数 | 0 到 86399 之间的整数 (一天为 86400 秒) |
microseconds | 微秒数 | 0 到 999999 之间的整数 |
dt 模块为了对日期进行计算,设计了 dt.timedelta 类型来表示时间差。两个 dt.datetime 实例相减,会输出对应时间差的 dt.timedelta 对象。除此以外:
- dt.datetime 可以加上 dt.timedelta,来输出时间后移 dt.timedelta 后、对应的 dt.datetime 实例
- dt.datetime 可以减去 dt.timedelta,来输出时间前移 dt.timedelta 后、对应的 dt.datetime 实例。
dt.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)
可以用以上函数创建对应的 dt.timedelta 实例。以上的毫秒、分钟等都会被转化为相等的属性值来储存。
运算 | 运算符 | 结果 |
---|---|---|
加法 | td1 + td2 | 返回相当于两者之和的 dt.timedelta 对象。 |
减法 | td1 - td2 | 返回相当于两者之差的 dt.timedelta 对象。 |
整数乘法 | td * t | 返回相当于 td 重复 t 次的 dt.timedelta 对象。 |
浮点乘法 | td * t | 类似整数乘法,但是结果会被舍入。 如果被舍入的首位是五、按照偶入奇不入规则。 |
数字除法 | td / t | 等同于 td * (1 / t) |
带余除法 求商 |
td1 // td2 | |
带余除法 求模 |
td1 % td2 | 返回一个 dt.timedelta 对象 |
带余除法 | divmod(td1, td2) | 返回一个 (商, 模) 的元组。 |
正 | +td | 返回 td |
负 | -td | 返回 dt.timedelta(-td.days, -td.seconds, -td.microseconds) |
绝对值 | abs(td) | 若 td.days >= 0 返回 td。反之,返回 -td |
str | str(td) | 返回形如 [D day[s], ][H]H:MM:SS[.UUUUUU] 的字符串当 td.days < 0 时,D 也小于零。 |
repr | repr(td) | 若 td.days >= 0 返回 td。反之,返回 -td |
除了和 dt.datetime 进行加减法计算之外,dt.timedelta 还可以进行其它计算。
在使用右侧运算时,要注意使计算结果不要超出 dt.timedelta 的限制。
两个 dt.datetime 实例之间可以进行比较运算。时间上靠后的实例 > 时间上靠前的实例。
dt.datetime 的相等是时间上的相等,并不需要两者的所有属性是完全相同的。尤其是时区不同的情况,比如:
import datetime as dt
dt1 = dt.datetime(1970,1,1,8,tzinfo=dt.timezone(
dt.timedelta(hours=8),name="中国标准时间"))
dt2 = dt.datetime(1970,1,1,8,tzinfo=dt.timezone(
dt.timedelta(hours=8)))
dt3 = dt.datetime(1970,1,1,0,tzinfo=dt.timezone(
dt.timedelta(0)))
dt4 = dt.datetime(1970,1,1,0)
dt5 = dt.datetime(1970,1,1,0)
print(dt1 == dt2 == dt3) #输出:True
print(dt1 == dt4) #输出:False
print(dt4 == dt5) #输出:False
要注意,未设置 tzinfo
的对象会被视为“时区未知”,在与“已知时区”的对象进行“等于运算”时,总会输出 False
。
关于时区信息的设置,参见下面的章节。
dt.date 也支持上面 dt.datetime 的运算。把上面的 dt.datetime 实例全换成 dt.date 实例是不会出错的。
但有几点需要注意:
- dt.date 在运算时,被视为是 dt.date 所对应当天的 00:00:00。
- dt.date 和 dt.timedelta 相加/相减时,会输出“结果所在天数的”dt.date 对象。
- dt.date 不能和 dt.datetime 作上述运算。
而 dt.time 不支持上面的加、减、大于、小于等运算。它只支持等于 / 不等于运算。在运算时,时区名称以外的部分必须要对应相等,等于运算的结果才为 True
。时区名称在属性 tzinfo 中。
dt.tzinfo 是时区信息的抽象基类。有以下方法的,被视为是 dt.tzinfo 的子类型。
- utcoffset()
返回时区与 UTC 零时区之间时间差的 dt.timedelta 对象。 - dst()
如果使用夏令时,返回夏令时信息(比如夏令时调快1小时即为dt.timedelta(hours = 1)
)
如果不使用夏令时,返回dt.timedelta(0)
如果未知,返回None
- tzname()
返回时区名称的字符串。如果未知,返回None
dt 模块中有一个 dt.tzinfo 抽象基类的简单子类:dt.timezone。该类型不能处理夏令时。
创建该类型的方法,就是将表示与 UTC 零时区时差的 dt.timedelta 对象和可选的时区名称传入 dt.timezone 类型构造器即可。
除了调用 dt.datetime.tzinfo 属性的方法之外,我们可以用同名方法,从 dt.datetime 类型中获取更多信息。
特别的,如果 tzinfo 属性为 None
,这些方法都会返回 None
类型 | 获取信息使用的实例方法 | 注释 | |
---|---|---|---|
dt.datetime | dt.time | ||
UTC时间差 | dt.datetime.utcoffset() | dt.time.utcoffset() | 返回本地时间与零时区时间差的 dt.timedelta 对象 |
夏令时 | dt.datetime.dst() | dt.time.dst() | 返回夏令时信息的 dt.timedelta 对象 |
时区名称 | dt.datetime.tzname() | dt.time.tzname() | 返回时区名称 |
除去日期与时间的表示与计算之外,还有许多其它功能会十分实用。
以下函数返回的数值没有严格定义的参考点,所以这些函数的用法都是两次返回值作减法、从而得到中间经过的时间。
返回一个以秒为单位的时钟的值,该值不受系统时钟的影响。
类似 time.monotonic
,但单位是纳秒,且不会有精度丢失。
类似 time.monotonic
,但使用的时钟为性能计数器——它用于测量较短的持续时间,且具有最高有效精度。它会包含系统睡眠时间。
类似 time.perf_counter
,但单位是纳秒,且不会有精度丢失。
返回当前进程的系统 CPU 时间和用户 CPU 时间的总和。不包括睡眠时间,且只作用于进程范围。返回值没有严格定义的参考点。
类似 time.process_time
,但单位是纳秒,且不会有精度丢失。
使当前程序暂停 secs
秒。