日期时间和Timedeltas

版本1.7.0中的新功能

从NumPy 1.7开始,核心数组数据类型本身支持日期时间功能。数据类型被称为“datetime64”,因为“datetime”已被Python中包含的日期时间库采用而被命名。

注意

日期时间API在1.7.0中为实验,并且可能会在将来版本的NumPy中发生变化。

基本日期时间

创建日期时间的最基本方法是使用ISO 8601日期或日期时间格式的字符串。内部存储的单位是从字符串的形式自动选择的,可以是date unittime unit日期单位是年('Y'),月('M'),星期('W')和日期('D'),而时间单位是小时('h'),分钟('m' ),秒('s'),毫秒('ms')和一些额外的SI前缀以秒为单位。

简单的ISO日期:

>>> np.datetime64('2005-02-25')
numpy.datetime64('2005-02-25')

使用单位的月份:

>>> np.datetime64('2005-02')
numpy.datetime64('2005-02')

仅指定月份,但强制设置“天”单位:

>>> np.datetime64('2005-02', 'D')
numpy.datetime64('2005-02-01')

从日期和时间开始:

>>> np.datetime64('2005-02-25T03:30')
numpy.datetime64('2005-02-25T03:30')

从字符串创建日期时间数组时,仍然可以通过使用具有通用单位的日期时间类型自动从输入中选择单位。

>>> np.array(['2007-07-13', '2006-01-13', '2010-08-13'], dtype='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='datetime64')
array(['2001-01-01T12:00:00.000-0600', '2002-02-03T13:56:03.172-0600'], dtype='datetime64[ms]')

日期时间类型使用许多常见的NumPy函数,例如arange可用于生成日期范围。

一个月的所有日期:

>>> 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]')

日期时间对象代表一个时刻。如果两个日期时间有不同的单位,它们可能仍然代表相同的时间,并且从一个更大的单位(如月)转换为一个较小的单位(如天)会被视为“安全”投下,因为时间的精确性仍然存在。

>>> np.datetime64('2005') == np.datetime64('2005-01-01')
True
>>> np.datetime64('2010-03-14T15Z') == np.datetime64('2010-03-14T15:00:00.00Z')
True

Datetime和Timedelta算术

NumPy允许减去两个日期时间值,这是一种以时间单位产生数字的操作。由于NumPy的核心没有物理量系统,因此创建timedelta64数据类型以补充datetime64。

Datetime和Timedeltas一起工作以提供简单的日期时间计算方法。

>>> np.datetime64('2009-01-01') - np.datetime64('2008-01-01')
numpy.timedelta64(366,'D')
>>> np.datetime64('2009') + np.timedelta64(20, 'D')
numpy.datetime64('2009-01-21')
>>> np.datetime64('2011-06-15T00:00') + np.timedelta64(12, 'h')
numpy.datetime64('2011-06-15T12:00-0500')
>>> np.timedelta64(1,'W') / np.timedelta64(1,'D')
7.0

有两个Timedelta单位('Y',年和'M',月)被特别对待,因为它们表示变化的时间取决于它们的使用时间。虽然timedelta day单位相当于24小时,但无法将月份单位转换为天,因为不同的月份有不同的天数。

>>> 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数据类型支持大量的时间单位,以及可根据输入数据强制进入任何其他单位的通用单位。

日期时间总是基于POSIX时间存储(尽管提出了允许计算闰秒的TAI模式),具有1970-01-01T00:00Z的时期。这意味着支持的日期总是围绕时期的对称区间,在下表中称为“时间跨度”。

范围的长度是一个64位整数乘以日期或单位长度的范围。例如,'W'(星期)的时间跨度比'D'(日期)的时间跨度长7倍,并且'D'(日期)的时间跨度恰好比时间跨度长24倍'h'(小时)。

以下是日期单位:

含义 时间跨度(相对) 时间跨度(绝对)
Y +/- 9.2e18年 [公元前9.2e18,公元前9.2e18]
M +/- 7.6e17年 [公元前7.6e17,公元前7.6e17]
W +/- 1.7e17年 [公元前1.7e17,公元1.7e17]
D +/- 2.5e16年 [2.5e16 BC,2.5e16 AD]

这里是时间单位:

含义 时间跨度(相对) 时间跨度(绝对)
h 小时 +/- 1.0e15年 [1.0e15 BC,1.0e15 AD]
m 分钟 +/- 1.7e13年 [公元前1.7e13,公元1.7e13]
s 第二 +/- 2.9e11年 [2.9e11 BC,2.9e11 AD]
女士 毫秒 +/- 2.9e8年 [公元前2.9e8,公元前2.9e8]
我们 微秒 +/- 2.9e5年 [290301 BC,294241 AD]
NS 纳秒 +/- 292年 [公元1678年,2262年]
PS 皮秒 +/- 106天 [公元1969年,公元1970年]
FS 飞秒 +/- 2.6小时 [公元1969年,公元1970年]
阿秒 +/- 9.2秒 [公元1969年,公元1970年]

工作日功能

为了允许日期时间在一周中某几天有效的上下文中使用,NumPy包含一组“行车日”(工作日)功能。

busday函数的默认值是唯一的有效日期是星期一到星期五(通常的工作日)。该实施基于包含7个布尔标志以指示有效日期的“周遮罩”自定义星期掩码可能会指定其他有效日期集合。

“公共汽车日”功能还可以检查“假日”日期的列表,具体日期不是有效日期。

功能busday_offset允许您在日期时间内以“D”(日)为单位应用工作日内指定的偏移量。

>>> np.busday_offset('2011-06-23', 1)
numpy.datetime64('2011-06-24')
>>> np.busday_offset('2011-06-23', 2)
numpy.datetime64('2011-06-27')

When an input date falls on the weekend or a holiday, busday_offset first applies a rule to roll the date to a valid business day, then applies the offset. 默认规则是'raise',这只会引发异常。最常用的规则是“前进”和“后退”。

>>> 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')
numpy.datetime64('2011-06-27')
>>> np.busday_offset('2011-06-25', 2, roll='forward')
numpy.datetime64('2011-06-29')
>>> np.busday_offset('2011-06-25', 0, roll='backward')
numpy.datetime64('2011-06-24')
>>> np.busday_offset('2011-06-25', 2, roll='backward')
numpy.datetime64('2011-06-28')

在某些情况下,适当使用卷筒和偏移量是获得理想答案所必需的。

在某个日期或之后的第一个工作日:

>>> np.busday_offset('2011-03-20', 0, roll='forward')
numpy.datetime64('2011-03-21','D')
>>> np.busday_offset('2011-03-22', 0, roll='forward')
numpy.datetime64('2011-03-22','D')

严格限定日期后的第一个工作日:

>>> np.busday_offset('2011-03-20', 1, roll='backward')
numpy.datetime64('2011-03-21','D')
>>> np.busday_offset('2011-03-22', 1, roll='backward')
numpy.datetime64('2011-03-23','D')

该功能对计算某些类型的假期也很有用。在加拿大和美国,母亲节是在五月的第二个星期天,可以用一个自定义的星期计算来计算。

>>> np.busday_offset('2012-05', 1, roll='forward', weekmask='Sun')
numpy.datetime64('2012-05-13','D')

When performance is important for manipulating many business dates with one particular choice of weekmask and holidays, there is an object busdaycalendar which stores the data necessary in an optimized form.

np.is_busday():¶ T0>

要测试datetime64值以查看它是否为有效日期,请使用is_busday

>>> 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], dtype='bool')

np.busday_count():¶ T0>

要查找指定范围的datetime64日期中有多少有效日期,请使用busday_count

>>> 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

如果您有一个datetime64日期值的数组,并且您希望计算其中有多少个有效日期,则可以这样做:

>>> a = np.arange(np.datetime64('2011-07-11'), np.datetime64('2011-07-18'))
>>> np.count_nonzero(np.is_busday(a))
5

Custom Weekmasks

以下是自定义星期屏蔽值的几个示例。这些示例指定星期一至星期五的“总线日”默认值为有效日期。

一些例子:

# 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"

使用NumPy 1.11 进行更改

在以前版本的NumPy中,datetime64类型始终以UTC存储时间。默认情况下,从一个字符串创建一个datetime64对象或打印它将从本地时间转换为本地时间:

# old behavior
>>>> np.datetime64('2000-01-01T00:00:00')
numpy.datetime64('2000-01-01T00:00:00-0800')  # note the timezone offset -08:00

datetime64用户的共识认为,这种行为是不受欢迎的,并且与通常使用datetime64的方式不一致(例如,通过pandas_)。对于大多数使用情况,首选时区初始日期时间类型,类似于Python标准库中的datetime.datetime类型。因此,datetime64不再假定输入是在本地时间,也不打印当地时间:

>>>> np.datetime64('2000-01-01T00:00:00')
numpy.datetime64('2000-01-01T00:00:00')

为了向后兼容,datetime64仍然分析时区偏移量,它通过转换为UTC来处理偏移量。然而,由此产生的日期时间是天真的时区:

>>> np.datetime64('2000-01-01T00:00:00-08')
DeprecationWarning: parsing timezone aware datetimes is deprecated; this will raise an error in the future
numpy.datetime64('2000-01-01T08:00:00')

作为这种变化的必然结果,我们不再禁止在日期时间与日期单位和日期时间之间建立时间单位。随着时区天真的日期时间,从日期到时间的投射规则不再含糊不清。

pandas_http://pandas.pydata.org

1.6和1.7日期时间的差异

NumPy 1.6版本包含比1.7更原始的日期时间数据类型。本节记录了许多已发生的变化。

字符串解析

NumPy 1.6中的日期时间字符串解析器在接受它时非常宽松,并且默默地允许无效输入而不会产生错误。NumPy 1.7中的解析器对于只接受ISO 8601日期非常严格,并且有一些便利的扩展。默认情况下,1.6总是创建微秒(us)单位,而1.7会根据字符串的格式检测单位。这是一个比较。:

# NumPy 1.6.1
>>> np.datetime64('1979-03-22')
1979-03-22 00:00:00
# NumPy 1.7.0
>>> np.datetime64('1979-03-22')
numpy.datetime64('1979-03-22')

# NumPy 1.6.1, unit default microseconds
>>> np.datetime64('1979-03-22').dtype
dtype('datetime64[us]')
# NumPy 1.7.0, unit of days detected from string
>>> np.datetime64('1979-03-22').dtype
dtype('<M8[D]')

# NumPy 1.6.1, ignores invalid part of string
>>> np.datetime64('1979-03-2corruptedstring')
1979-03-02 00:00:00
# NumPy 1.7.0, raises error for invalid input
>>> np.datetime64('1979-03-2corruptedstring')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Error parsing datetime string "1979-03-2corruptedstring" at position 8

# NumPy 1.6.1, 'nat' produces today's date
>>> np.datetime64('nat')
2012-04-30 00:00:00
# NumPy 1.7.0, 'nat' produces not-a-time
>>> np.datetime64('nat')
numpy.datetime64('NaT')

# NumPy 1.6.1, 'garbage' produces today's date
>>> np.datetime64('garbage')
2012-04-30 00:00:00
# NumPy 1.7.0, 'garbage' raises an exception
>>> np.datetime64('garbage')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Error parsing datetime string "garbage" at position 0

# NumPy 1.6.1, can't specify unit in scalar constructor
>>> np.datetime64('1979-03-22T19:00', 'h')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: function takes at most 1 argument (2 given)
# NumPy 1.7.0, unit in scalar constructor
>>> np.datetime64('1979-03-22T19:00', 'h')
numpy.datetime64('1979-03-22T19:00-0500','h')

# NumPy 1.6.1, reads ISO 8601 strings w/o TZ as UTC
>>> np.array(['1979-03-22T19:00'], dtype='M8[h]')
array([1979-03-22 19:00:00], dtype=datetime64[h])
# NumPy 1.7.0, reads ISO 8601 strings w/o TZ as local (ISO specifies this)
>>> np.array(['1979-03-22T19:00'], dtype='M8[h]')
array(['1979-03-22T19-0500'], dtype='datetime64[h]')

# NumPy 1.6.1, doesn't parse all ISO 8601 strings correctly
>>> np.array(['1979-03-22T12'], dtype='M8[h]')
array([1979-03-22 00:00:00], dtype=datetime64[h])
>>> np.array(['1979-03-22T12:00'], dtype='M8[h]')
array([1979-03-22 12:00:00], dtype=datetime64[h])
# NumPy 1.7.0, handles this case correctly
>>> np.array(['1979-03-22T12'], dtype='M8[h]')
array(['1979-03-22T12-0500'], dtype='datetime64[h]')
>>> np.array(['1979-03-22T12:00'], dtype='M8[h]')
array(['1979-03-22T12-0500'], dtype='datetime64[h]')

单位转换

日期时间的1.6实现不能正确转换单位。:

# NumPy 1.6.1, the representation value is untouched
>>> np.array(['1979-03-22'], dtype='M8[D]')
array([1979-03-22 00:00:00], dtype=datetime64[D])
>>> np.array(['1979-03-22'], dtype='M8[D]').astype('M8[M]')
array([2250-08-01 00:00:00], dtype=datetime64[M])
# NumPy 1.7.0, the representation is scaled accordingly
>>> np.array(['1979-03-22'], dtype='M8[D]')
array(['1979-03-22'], dtype='datetime64[D]')
>>> np.array(['1979-03-22'], dtype='M8[D]').astype('M8[M]')
array(['1979-03'], dtype='datetime64[M]')

日期时间算术

datetime的1.6实现只适用于算术操作的一小部分。这里我们展示一些简单的例子。:

# NumPy 1.6.1, produces invalid results if units are incompatible
>>> a = np.array(['1979-03-22T12'], dtype='M8[h]')
>>> b = np.array([3*60], dtype='m8[m]')
>>> a + b
array([1970-01-01 00:00:00.080988], dtype=datetime64[us])
# NumPy 1.7.0, promotes to higher-resolution unit
>>> a = np.array(['1979-03-22T12'], dtype='M8[h]')
>>> b = np.array([3*60], dtype='m8[m]')
>>> a + b
array(['1979-03-22T15:00-0500'], dtype='datetime64[m]')

# NumPy 1.6.1, arithmetic works if everything is microseconds
>>> a = np.array(['1979-03-22T12:00'], dtype='M8[us]')
>>> b = np.array([3*60*60*1000000], dtype='m8[us]')
>>> a + b
array([1979-03-22 15:00:00], dtype=datetime64[us])
# NumPy 1.7.0
>>> a = np.array(['1979-03-22T12:00'], dtype='M8[us]')
>>> b = np.array([3*60*60*1000000], dtype='m8[us]')
>>> a + b
array(['1979-03-22T15:00:00.000000-0500'], dtype='datetime64[us]')