User:Xyy23330121/Python/日期与时间

来自维基学院


许多程序需要处理日期与时间。Python 提供了两个模块可以用于处理时间。

本页面的内容太多,所以大部分内容都被改为了超链接索引。如果读者需要详细了解一些信息,请直接点击表格中或文章中的超链接。

时间的储存方式[编辑 | 编辑源代码]

纪元秒数(UNIX 时间戳)[编辑 | 编辑源代码]

许多平台都会使用纪元秒数来存储时间。纪元秒数是自 1970 年 1 月 1 日 00:00:00 以来,经过的秒数(用浮点数表示)。简单来讲:0.0 代表 1970 年 1 月 1 日 00:00:00,而 1.0 代表 1970 年 1 月 1 日 00:00:01 。

添加闰秒的条件(选读)
闰秒是 UTC 时间标准的一部分,它用于调节经由原子钟测量的精确时间与太阳时的误差。一天是 原子秒(即 秒),但地球自转有时会不可预知地慢几十毫秒。当地球自转 天花费的时间大于 秒时,就应当在第 天加上一秒。
在实际操作时,则是顺延到月份的最后一天再插入闰秒。

这一记录方法通常是不包含闰秒的。闰秒是 UTC 标准下、对满足一定条件的一天、在其最后最后添加一秒的规则。在所有符合 POSIX 标准的平台上,闰秒都不会被记录在总秒数中。比如:

  • 2016 年 12 月 31 日有闰秒,使得比如 2016 年 12 月 31 日 23:59:60 的时间是存在的(而非错误地没有进位)。而在 POSIX 标准的平台中,这一秒被直接忽略掉——根本不表示这一秒之间的时间。

ISO 8601 标准[编辑 | 编辑源代码]

时间还可以由 ISO 8601 标准给出的的字符串来储存和进行交流。

Python 储存时间的方式[编辑 | 编辑源代码]

除上述方法以外,Python 还通过几种方式储存时间。这里简单列出这些方法的特征,读者应按照自身需求来选择对应的方法。

纳秒数[编辑 | 编辑源代码]

和其它程序语言中,整数在 -32768 ~ 32767 之间不同,Python 的整数可以表示任意整数(只要系统内存能存储)。所以 Python 可以使用整数来解决纪元秒数中、浮点数精度的问题。

备注:1,000,000,000 纳秒(即 1e9 纳秒)等于 1 秒。

time.struct_time[编辑 | 编辑源代码]

time 模块中,包含各个时间属性的对象。除基本的年、月、日、时、分、秒外,该对象还包含:

  1. 在一年中第几个星期
  2. 是星期几
  3. 在一年中第几天
  4. 夏令时信息
  5. 时区信息

除此之外,该对象自带的输出方式较为多样,可以简单地把该对象按自定义格式转化为字符串并输出。

datetime.datetime[编辑 | 编辑源代码]

datetime 模块中,包含各个时间属性的对象。除基本的年、月、日、时、分、秒外,该对象还包含“微秒数”,从而它能表示比 time.struct_time 更精确的时间。但该方法对时区的支持较为繁琐。

该对象在时间计算相关的支持较为丰富,比如,它支持直接求两个时间的间隔。

该对象可以被认为是以下两类对象的结合:

datetime.date[编辑 | 编辑源代码]

datetime 模块中,表示日期的类型。

datetime.time[编辑 | 编辑源代码]

datetime 模块中,表示时间的类型。

纪元秒数相关方法[编辑 | 编辑源代码]

获取当前时间[编辑 | 编辑源代码]

time.time() -> float[编辑 | 编辑源代码]

获得纪元秒数。对闰秒的处理基于具体平台。Windows 和大多数 Unix 系统均忽略闰秒。

time.time_ns() -> int[编辑 | 编辑源代码]

获得纪元“纳秒数”,使用它的好处是不会因浮点数舍入而丢失精度。

转换为字符串[编辑 | 编辑源代码]

time.ctime([secs])[编辑 | 编辑源代码]

把纪元秒数转换为形如 "Thu Jan 11 00:00:00 1970" 的内容。

如果不输入 t,则使用当前时间生成。如果日期仅有 1 位,则会用空格进行缩进,比如:"Thu Jan 1 00:00:00 1970"

time.struct_time 的使用[编辑 | 编辑源代码]

time.struct_time 的属性[编辑 | 编辑源代码]

可以通过索引和属性名来访问 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 实例。
可以选择生成零时区的对象或本地时间的对象。
纪元秒数 可以 可以 time.struct_time 和表示纪元秒数的浮点数之间的转换。
可以选择转换为零时区的对象或本地时间的对象。
字符串格式化 可以 可以 “字符串格式化”指的是读者自定义的输出方式,
它会输出形如 "Thu Jan 11 00:00:00 1970" 的字符串。
其使用方法和 printf 式字符串格式化相似。

datetime.datetime 的使用[编辑 | 编辑源代码]

dt.datetime 的属性
属性
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 模块中有 datetime 类,又有 date 类——而 datetime 类的实例又有 date 方法。于是,datetime.datetime.date (实例方法)和 datetime.date (类型)是完全不同的两个东西。

为防止混淆以及简便起见,本章节以下的内容均使用了 import datetime as dt

dt.datetime 的属性[编辑 | 编辑源代码]

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.datetime 的转化[编辑 | 编辑源代码]

以下是 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.datetime dt.date dt.time
属性 可以 可以 可以 对已有实例输入属性和数值,返回属性被修改后的副本
时区 可以 不可以 不可以 返回表示相同时间,但时区不同的对象

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 对象,保留时区信息。
dt.timedelta 的属性
属性名 解释 范围
days 天数 -99999999 到 999999999
之间的整数
seconds 秒数 0 到 86399 之间的整数
(一天为 86400 秒)
microseconds 微秒数 0 到 999999 之间的整数

dt.datetime 的加减运算[编辑 | 编辑源代码]

dt 模块为了对日期进行计算,设计了 dt.timedelta 类型来表示时间差。两个 dt.datetime 实例相减,会输出对应时间差的 dt.timedelta 对象。除此以外:

  1. dt.datetime 可以加上 dt.timedelta,来输出时间后移 dt.timedelta 后、对应的 dt.datetime 实例
  2. dt.datetime 可以减去 dt.timedelta,来输出时间前移 dt.timedelta 后、对应的 dt.datetime 实例。

创建 dt.timedelta[编辑 | 编辑源代码]

dt.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)

可以用以上函数创建对应的 dt.timedelta 实例。以上的毫秒、分钟等都会被转化为相等的属性值来储存。

dt.timedelta 的运算[编辑 | 编辑源代码]

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 实例之间可以进行比较运算。时间上靠后的实例 > 时间上靠前的实例。

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.time 的运算[编辑 | 编辑源代码]

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 是时区信息的抽象基类。有以下方法的,被视为是 dt.tzinfo 的子类型。

  1. utcoffset()
    返回时区与 UTC 零时区之间时间差的 dt.timedelta 对象。
  2. dst()
    如果使用夏令时,返回夏令时信息(比如夏令时调快1小时即为 dt.timedelta(hours = 1)
    如果不使用夏令时,返回dt.timedelta(0)
    如果未知,返回 None
  3. tzname()
    返回时区名称的字符串。如果未知,返回 None

class dt.timezone(offset, name=None)[编辑 | 编辑源代码]

dt 模块中有一个 dt.tzinfo 抽象基类的简单子类:dt.timezone。该类型不能处理夏令时。

创建该类型的方法,就是将表示与 UTC 零时区时差的 dt.timedelta 对象和可选的时区名称传入 dt.timezone 类型构造器即可。

从 dt.datetime 直接获取时区信息[编辑 | 编辑源代码]

除了调用 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() -> float[编辑 | 编辑源代码]

返回一个以秒为单位的时钟的值,该值不受系统时钟的影响。

time.monotonic_ns() -> int[编辑 | 编辑源代码]

类似 time.monotonic,但单位是纳秒,且不会有精度丢失。

time.perf_counter() -> float[编辑 | 编辑源代码]

类似 time.monotonic,但使用的时钟为性能计数器——它用于测量较短的持续时间,且具有最高有效精度。它会包含系统睡眠时间。

time.perf_counter_ns() -> float[编辑 | 编辑源代码]

类似 time.perf_counter,但单位是纳秒,且不会有精度丢失。

time.process_time() -> float[编辑 | 编辑源代码]

返回当前进程的系统 CPU 时间和用户 CPU 时间的总和。不包括睡眠时间,且只作用于进程范围。返回值没有严格定义的参考点。

time.process_time_ns() -> int[编辑 | 编辑源代码]

类似 time.process_time,但单位是纳秒,且不会有精度丢失。

暂停进程[编辑 | 编辑源代码]

time.sleep(secs)[编辑 | 编辑源代码]

使当前程序暂停 secs 秒。

參考文獻[编辑 | 编辑源代码]