从 NumPy 1.7 起,核心数组数据类型原生支持日期时间功能。这个类型名为 datetime64,因为 datetime.datetime 已被 Python 标准库使用。
datetime64 的约定与假设
与 Python 的 datetime.date 类似,日期使用现行格里高利历,并无限延伸到过去和未来。但 Python datetime.date 只支持公元1年至9999年,datetime64 还支持公元前日期,按天文年份编号表示:公元前2年为 −1,公元前1年为0,公元1年为1。
16:23:32.234 这样的时刻,是从午夜开始计数的小时、分钟、秒和秒的小数部分:00:00:00.000 为午夜,12:00:00.000 为正午。每个日历日恰好86400秒。这是一种“不带时区”的时间,没有显式时区或 UT1、UTC、TAI 等时间尺度概念。
基本日期时间
最基本的创建方式是解析 ISO 8601 日期或日期时间字符串,也可以把整数解释为相对 Unix 纪元(1970-01-01 00:00:00 UTC)的偏移。内部单位根据字符串形式自动选择,可以是日期单位或时间单位:年 Y、月 M、周 W、日 D;时 h、分 m、秒 s、毫秒 ms,以及其他带 SI 前缀的秒单位。
datetime64 还接受任意大小写组合的 NAT,表示“Not A Time”。now 返回当前 UTC 时间,默认秒精度;可以指定 M、D、h 等单位进行截断。细于秒的单位也支持,例如 ms 和 ns,但小数部分显示为零,实际仍截断到整秒。today 返回当前 UTC 日期,默认日精度,也支持与 now 相同的精度参数。
创建示例
简单 ISO 日期:
>>> import numpy as np
>>> np.datetime64('2005-02-25')
np.datetime64('2005-02-25')
整数加日期单位:Unix 纪元之后一年:
>>> np.datetime64(1, 'Y')
np.datetime64('1971')
以月为单位:
>>> np.datetime64('2005-02')
np.datetime64('2005-02')
只指定月份,但强制日单位:
>>> np.datetime64('2005-02', 'D')
np.datetime64('2005-02-01')
日期与时刻:
>>> np.datetime64('2005-02-25T03:30')
np.datetime64('2005-02-25T03:30')
NaT:
>>> np.datetime64('nat', 'D')
np.datetime64('NaT', 'D')
当前 UTC 时间,默认秒精度,并演示日与毫秒精度:
>>> np.datetime64('now')
np.datetime64('2025-08-05T02:22:14') # result will depend on the current time
>>> np.datetime64('now', 'D')
np.datetime64('2025-08-05')
>>> np.datetime64('now', 'ms')
np.datetime64('2025-08-05T02:22:14.000')
当前日期;now 与 today 示例的结果取决于执行时间:
>>> np.datetime64('today')
np.datetime64('2025-08-05') # result will depend on the current date
从字符串创建日期时间数组时,可以使用通用单位的日期时间类型,由输入自动选择单位。
>>> import numpy as np
>>> np.array(['2007-07-13', '2006-01-13', '2010-08-13'], dtype=np.datetime64)
array(['2007-07-13', '2006-01-13', '2010-08-13'], dtype='datetime64[D]')
>>> np.array(['2001-01-01T12:00', '2002-02-03T13:56:03.172'], dtype=np.datetime64)
array(['2001-01-01T12:00:00.000', '2002-02-03T13:56:03.172'],
dtype='datetime64[ms]')
也可从表示 POSIX 时间戳的整数和指定单位创建数组:
>>> import numpy as np
>>> np.array([0, 1577836800], dtype='datetime64[s]')
array(['1970-01-01T00:00:00', '2020-01-01T00:00:00'],
dtype='datetime64[s]')
>>> np.array([0, 1577836800000]).astype('datetime64[ms]')
array(['1970-01-01T00:00:00.000', '2020-01-01T00:00:00.000'],
dtype='datetime64[ms]')
日期时间类型适用于许多常见 NumPy 函数。例如,用 arange 生成一个月的全部日期:
>>> import numpy as np
>>> np.arange('2005-02', '2005-03', dtype='datetime64[D]')
array(['2005-02-01', '2005-02-02', '2005-02-03', '2005-02-04',
'2005-02-05', '2005-02-06', '2005-02-07', '2005-02-08',
'2005-02-09', '2005-02-10', '2005-02-11', '2005-02-12',
'2005-02-13', '2005-02-14', '2005-02-15', '2005-02-16',
'2005-02-17', '2005-02-18', '2005-02-19', '2005-02-20',
'2005-02-21', '2005-02-22', '2005-02-23', '2005-02-24',
'2005-02-25', '2005-02-26', '2005-02-27', '2005-02-28'],
dtype='datetime64[D]')
日期时间对象表示单个时刻。单位不同的两个日期时间仍可能表示同一时刻。从月等较大单位转换为日等较小单位属于“安全”转换,因为仍能精确表示同一时刻:
>>> import numpy as np
>>> np.datetime64('2005') == np.datetime64('2005-01-01')
True
>>> np.datetime64('2010-03-14T15') == np.datetime64('2010-03-14T15:00:00.00')
True
日期时间与时间差运算
两个日期时间相减会得到带时间单位的数。NumPy 核心没有物理量体系,因此创建 timedelta64 补充 datetime64。它的参数是表示单位数量的数值,以及 D、M、Y、h、m 或 s 等单位;数值也可用 NAT 替代:
>>> import numpy as np
>>> np.timedelta64(1, 'D')
np.timedelta64(1,'D')
>>> np.timedelta64(4, 'h')
np.timedelta64(4,'h')
>>> np.timedelta64('nAt', 'D')
np.timedelta64('NaT', 'D')
两种类型配合完成简单日期运算:
>>> import numpy as np
>>> np.datetime64('2009-01-01') - np.datetime64('2008-01-01')
np.timedelta64(366,'D')
>>> np.datetime64('2009') + np.timedelta64(20, 'D')
np.datetime64('2009-01-21')
>>> np.datetime64('2011-06-15T00:00') + np.timedelta64(12, 'h')
np.datetime64('2011-06-15T12:00')
>>> np.timedelta64(1,'W') / np.timedelta64(1,'D')
7.0
>>> np.timedelta64(1,'W') % np.timedelta64(10,'D')
np.timedelta64(7,'D')
>>> np.datetime64('nat', 'D') - np.datetime64('2009-01-01')
np.timedelta64('NaT','D')
>>> np.datetime64('2009-01-01') + np.timedelta64('nat', 'D')
np.datetime64('NaT', 'D')
年 Y 和月 M 两种时间差单位需特殊处理,因为表示的时间长度取决于使用它们的日期。一天等于24小时,但月和年不能直接转成天,除非使用 unsafe 转换。
numpy.ndarray.astype 可执行这种不安全转换;换算按400年闰年周期的平均值计算:
>>> import numpy as np
>>> a = np.timedelta64(1, 'Y')
>>> np.timedelta64(a, 'M')
numpy.timedelta64(12,'M')
>>> np.timedelta64(a, 'D')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Cannot cast NumPy timedelta64 scalar from metadata [Y] to [D] according to the rule 'same_kind'
日期时间单位
Datetime 与 Timedelta 支持大量时间单位,也支持按输入强制转换为其他单位的通用单位。但通用单位自 NumPy 2.5 起弃用,将来会报错,迁移方法见后文。
日期时间总以1970-01-01T00:00为纪元。支持日期围绕纪元形成对称区间,表中称为“时间跨度”。跨度长度等于64位整数取值范围乘以单位长度。例如周的跨度恰为日的七倍,日的跨度恰为小时的24倍。
日期单位
| 代码 | 含义 | 相对跨度 | 绝对跨度 |
|---|---|---|---|
| Y | 年 | ±9.2e18年 | [9.2e18 BC, 9.2e18 AD] |
| M | 月 | ±7.6e17年 | [7.6e17 BC, 7.6e17 AD] |
| W | 周 | ±1.7e17年 | [1.7e17 BC, 1.7e17 AD] |
| D | 日 | ±2.5e16年 | [2.5e16 BC, 2.5e16 AD] |
时间单位
| 代码 | 含义 | 相对跨度 | 绝对跨度 |
|---|---|---|---|
| h | 小时 | ±1.0e15年 | [1.0e15 BC, 1.0e15 AD] |
| m | 分钟 | ±1.7e13年 | [1.7e13 BC, 1.7e13 AD] |
| s | 秒 | ±2.9e11年 | [2.9e11 BC, 2.9e11 AD] |
| ms | 毫秒 | ±2.9e8年 | [2.9e8 BC, 2.9e8 AD] |
| us / μs | 微秒 | ±2.9e5年 | [290301 BC, 294241 AD] |
| ns | 纳秒 | ±292年 | [1678 AD, 2262 AD] |
| ps | 皮秒 | ±106日 | [1969 AD, 1970 AD] |
| fs | 飞秒 | ±2.6小时 | [1969 AD, 1970 AD] |
| as | 阿秒 | ±9.2秒 | [1969 AD, 1970 AD] |
转换为 Python 对象
把 datetime64 或 timedelta64 转为 tuple、list、datetime.datetime 等 Python 对象时,NumPy 遵循严格协议:
| 输入类型 | datetime64 转换结果 | timedelta64 转换结果 |
|---|---|---|
| NaT | None | None |
| ns/ps/fs/as | int | int |
| μs/ms/s/m/h | datetime.datetime | datetime.timedelta |
| D/W(线性单位) | datetime.date | datetime.timedelta |
| Y/M(非线性单位) | datetime.date | int |
| 通用单位 | datetime.date | int |
时间差通用单位自2.5起弃用,将来会报错。
>>> import numpy as np
>>> type(np.datetime64('NaT', 'D').item())
<class 'NoneType'>
>>> type(np.timedelta64('NaT', 'D').item())
<class 'NoneType'>
>>> type(np.timedelta64(123, 'ns').item())
<class 'int'>
>>> type(np.datetime64('2025-01-01T12:00:00.123456').item())
<class 'datetime.datetime'>
>>> type(np.timedelta64(10, 'D').item())
<class 'datetime.timedelta'>
若转为 int、float、str 等 Python 类型,对应返回 NumPy 标量类型 np.int64、np.float64、np.str_:
>>> import numpy as np
>>> type(np.timedelta64(1, 'D').astype(int))
<class 'numpy.int64'>
>>> type(np.datetime64('2025-01-01T12:00:00.123456').astype(float))
<class 'numpy.float64'>
>>> type(np.timedelta64(123, 'ns').astype(str))
<class 'numpy.str_'>
工作日功能
为让日期时间适用于每周只有某些日子有效的场景,NumPy 提供一组 busday(business day,工作日)函数。默认只有星期一至星期五有效。实现基于包含七个布尔标志的 weekmask,也可自定义其他有效日集合;还能检查特定的无效日期,即 holiday 列表。
busday_offset 对日单位 D 的日期时间应用工作日偏移:
>>> import numpy as np
>>> np.busday_offset('2011-06-23', 1)
np.datetime64('2011-06-24')
>>> np.busday_offset('2011-06-23', 2)
np.datetime64('2011-06-27')
输入落在周末或假日时,函数先根据规则把日期滚动到有效工作日,再应用偏移。默认 raise 直接报错,常用的规则是 forward 与 backward:
>>> import numpy as np
>>> np.busday_offset('2011-06-25', 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Non-business day date in busday_offset
>>> np.busday_offset('2011-06-25', 0, roll='forward')
np.datetime64('2011-06-27')
>>> np.busday_offset('2011-06-25', 2, roll='forward')
np.datetime64('2011-06-29')
>>> np.busday_offset('2011-06-25', 0, roll='backward')
np.datetime64('2011-06-24')
>>> np.busday_offset('2011-06-25', 2, roll='backward')
np.datetime64('2011-06-28')
有些问题需要恰当地组合滚动与偏移。求不早于给定日期的第一个工作日:
>>> import numpy as np
>>> np.busday_offset('2011-03-20', 0, roll='forward')
np.datetime64('2011-03-21')
>>> np.busday_offset('2011-03-22', 0, roll='forward')
np.datetime64('2011-03-22')
求严格晚于给定日期的第一个工作日:
>>> np.busday_offset('2011-03-20', 1, roll='backward')
np.datetime64('2011-03-21')
>>> np.busday_offset('2011-03-22', 1, roll='backward')
np.datetime64('2011-03-23')
函数也可计算特定节日。加拿大和美国的母亲节是五月第二个星期日,可以用自定义 weekmask 计算:
>>> import numpy as np
>>> np.busday_offset('2012-05', 1, roll='forward', weekmask='Sun')
np.datetime64('2012-05-13')
若使用固定 weekmask 与假日表批量计算且关注性能,可用 busdaycalendar,以优化形式保存所需数据。
np.is_busday()
用 is_busday 测试日期是否有效:
>>> import numpy as np
>>> np.is_busday(np.datetime64('2011-07-15')) # a Friday
True
>>> np.is_busday(np.datetime64('2011-07-16')) # a Saturday
False
>>> np.is_busday(np.datetime64('2011-07-16'), weekmask="Sat Sun")
True
>>> a = np.arange(np.datetime64('2011-07-11'), np.datetime64('2011-07-18'))
>>> np.is_busday(a)
array([ True, True, True, True, True, False, False])
np.busday_count()
用 busday_count 计算指定日期区间的有效日数:
>>> import numpy as np
>>> np.busday_count(np.datetime64('2011-07-11'), np.datetime64('2011-07-18'))
5
>>> np.busday_count(np.datetime64('2011-07-18'), np.datetime64('2011-07-11'))
-5
若已有日期数组,希望计算其中有效日的数量:
>>> import numpy as np
>>> a = np.arange(np.datetime64('2011-07-11'), np.datetime64('2011-07-18'))
>>> np.count_nonzero(np.is_busday(a))
5
自定义 weekmask
以下几种写法都表示默认的星期一至星期五有效。位置序列必须恰好七项,按周一到周日排列;0表示无效,1表示有效。字符串形式可用“0”“1”,也可用区分大小写的 Mon Tue Wed Thu Fri Sat Sun 缩写;允许任意数量空白:
# Positional sequences; positions are Monday through Sunday.
# Length of the sequence must be exactly 7.
weekmask = [1, 1, 1, 1, 1, 0, 0]
# list or other sequence; 0 == invalid day, 1 == valid day
weekmask = "1111100"
# string '0' == invalid day, '1' == valid day
# string abbreviations from this list: Mon Tue Wed Thu Fri Sat Sun
weekmask = "Mon Tue Wed Thu Fri"
# any amount of whitespace is allowed; abbreviations are case-sensitive.
weekmask = "MonTue Wed Thu\tFri"
datetime64 的局限
假定每天恰好86400秒,使 datetime64 大致兼容 Python datetime 和 POSIX 时间语义;因此也共享它们关于 UTC 与历史时间确定的已知局限。以下是不完全列举。
不能解析正闰秒中的有效 UTC 时间戳
2016-12-31 23:59:60 UTC 是闰秒,所以 23:59:60.450 UTC 是有效时刻,但 datetime64 无法解析:
>>> import numpy as np
>>> np.datetime64("2016-12-31 23:59:60.450")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Seconds out of range in datetime string "2016-12-31 23:59:60.450"
UTC 日期间的时间差可能相差整数个 SI 秒
计算2021-01-01 12:56:23.423 UTC 与2001-01-01 00:00:00.000 UTC 之间的 SI 秒数:
>>> import numpy as np
>>> (
... np.datetime64("2021-01-01 12:56:23.423")
... - np.datetime64("2001-01-01")
... ) / np.timedelta64(1, "s")
631198583.423
正确结果是 631198588.423 SI 秒,因为2001年至2021年间有五个闰秒。
历史日期间的时间差不一定是 SI 秒
计算公元0年1月1日 UT 与1600年1月1日 UT 之间的秒数。UT 指世界时:
>>> import numpy as np
>>> a = np.datetime64("0000-01-01", "us")
>>> b = np.datetime64("1600-01-01", "us")
>>> b - a
numpy.timedelta64(50491123200000000,'us')
所得 50491123200 秒,是584388天乘以86400秒;这相当于与地球自转同步的时钟所计秒数。精确 SI 秒数只能估计,例如依据 Stephenson 等人在2016年 Royal Society’s Proceedings A 472 发表的Measurement of the Earth’s rotation: 720 BC to AD 2015。合理估计为 50491112870 ± 90 秒,与上述计算相差10330秒。
通用单位弃用迁移指南
timedelta64 通用单位自 NumPy 2.5 弃用,将来会报错。本节说明如何更新使用 timedelta64 与 datetime64 通用单位的代码,避免未来错误。
最直接的做法是将通用单位替换为明确的 D、h、m、s 等单位。具体选择取决于代码语境与精度需求:
>>> import numpy as np
>>> # Old code using generic units of timedelta64
>>> np.timedelta64(5, "s") + 1
DeprecationWarning: The 'generic' unit for NumPy timedelta is deprecated, and will raise an error in the future. This includes implicit conversion of bare integers (e.g. `+ 1`).Please use a specific unit instead.
>>> # Updated code using a specific time unit
>>> np.timedelta64(5, "s") + np.timedelta64(1, "s")
np.timedelta64(6,'s')
比较 timedelta64 时,两边应使用相同的明确单位,即使表示零:
>>> np.timedelta64(0, "s") == 0
DeprecationWarning: The 'generic' unit for NumPy timedelta is deprecated, and will raise an error in the future. This includes implicit conversion of bare integers (e.g. `== 0`).Please use a specific unit instead.
np.True_
>>> np.timedelta64(0, "s") == np.timedelta64(0, "s")
np.True_
用 numpy.testing.assert_allclose 比较时间差时,atol 也要指定单位:
>>> arr = np.ones(5, dtype='m8[s]')
>>> np.testing.assert_allclose(arr, np.timedelta64(1, "s"), atol=np.timedelta64(0, "s"))
此前 datetime64 允许不指定单位的 NaT;随着通用单位弃用,现在必须显式给出单位,以与其他日期时间和时间差操作保持一致:
>>> np.datetime64("NAT")
DeprecationWarning: The 'generic' unit for NumPy timedelta is deprecated, and will raise an error in the future. This includes implicit conversion of bare integers (e.g. `+ 1`).Please use a specific unit instead.
>>> np.datetime64("NAT", "D")
np.datetime64('NaT', 'D')











暂无评论内容