Datetime和Timedelta

1.7.0 新版功能.

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

注解

datetime API在1.7.0中是实验性的,并且可能在未来版本的NumPy中进行更改。

基本的日期时间

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

Example

一个简单的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')

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

Example

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

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

Example

所有日期为一个月:

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

datetime对象表示单个时刻。如果两个日期时间有不同的单位,它们可能仍然代表相同的时间,从一个更大的单位 (如月份)转换到一个较小的单位(如天)被认为是一种“安全”的转换,因为时间的时刻仍然被精确地表示。

Example

>>> 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允许两个Datetime值相减,这个操作产生一个带有时间单位的数字。 由于NumPy的核心没有物理量系统, 因此创建了timedelta64数据类型以补充datetime64。

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

Example

>>> 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日单位相当于24小时,但无法将一个月单位转换为天,因为不同月份的天数不同。

Example

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

Datetime总是根据POSIX时间存储的(虽然建议采用TAI模式来计算闰秒),但时值为 1970-01-01T00:00Z 。 这意味着支持的日期总是围绕着时代的对称间隔,在下表中称为“时间跨度

The length of the span is the range of a 64-bit integer times the length of the date or unit. For example, the time span for ‘W’ (week) is exactly 7 times longer than the time span for ‘D’ (day), and the time span for ‘D’ (day) is exactly 24 times longer than the time span for ‘h’ (hour).

以下是日期单位:

Code Meaning Time span (relative) Time span (absolute)
Y year +/- 9.2e18 years [9.2e18 BC, 9.2e18 AD]
M month +/- 7.6e17 years [7.6e17 BC, 7.6e17 AD]
W week +/- 1.7e17 years [1.7e17 BC, 1.7e17 AD]
D day +/- 2.5e16 years [2.5e16 BC, 2.5e16 AD]

以下是时间单位:

Code Meaning Time span (relative) Time span (absolute)
h hour +/- 1.0e15 years [1.0e15 BC, 1.0e15 AD]
m minute +/- 1.7e13 years [1.7e13 BC, 1.7e13 AD]
s second +/- 2.9e11 years [2.9e11 BC, 2.9e11 AD]
ms millisecond +/- 2.9e8 years [ 2.9e8 BC, 2.9e8 AD]
us microsecond +/- 2.9e5 years [290301 BC, 294241 AD]
ns nanosecond +/- 292 years [ 1678 AD, 2262 AD]
ps picosecond +/- 106 days [ 1969 AD, 1970 AD]
fs femtosecond +/- 2.6 hours [ 1969 AD, 1970 AD]
as attosecond +/- 9.2 seconds [ 1969 AD, 1970 AD]

工作日函数

为了允许在一周中只有特定天数有效的上下文中使用日期时间,NumPy包含一组 busday (工作日)函数。

busday 函数的默认值是唯一有效的日期是周一到周五(通常的工作日)。该实现基于一个 weekmask , 包含7个布尔标志,用于指示有效天数; 可以指定其他有效天数集。

busday 函数还可以检查 holiday (假日)日期列表,特定日期是无效日期。

函数 busday_offset 允许您将工作日中指定的偏移量应用于日期时间,单位为 `D`(天)

Example

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

当输入日期落在周末或假日时, busday_offset 首先应用规则将日期滚动到有效的工作日,然后应用偏移量。 默认规则是 raise ,它只会引发异常。 最常用的规则是 forwardbackward

Example

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

在某些情况下,需要适当使用滚动和偏移以获得所需的答案。

Example

The first business day on or after a date:

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

该函数对于计算某些日子如假期也很有用。 在加拿大和美国,母亲节是在5月的第二个星期天,可以使用自定义的周掩码来计算。

Example

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

当性能对于使用一个特定选择的周掩码和假日来操纵许多业务日期很重要时, 有一个对象 busdaycalendar ,它以优化的形式存储所需的数据。

np.is_busday():

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

Example

>>> 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():

要查找指定日期时间64日期范围内有效天数,请使用 busday_count

Example

>>> 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天值,并且您想要计算其中有多少是有效日期,则可以执行以下操作:

Example

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

自定义Weekmasks

以下是自定义周掩码值的几个示例。 这些示例指定周一至周五的 busday 默认值为有效天数。

Some examples:

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

作为此更改的必然结果,我们不再禁止在日期时间与日期单位和日期时间与时间单位之间进行转换。对于纯时区的日期时间,从日期到时间的投射规则不再模糊。

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

日期算术运算

日期时间的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]')