Типы данных datetime и timedelta
Введено в версии 1.7.0.
Начиная с NumPy 1.7, существуют основные массивно-ориентированные типы данных, которые изначально поддерживают функциональность datetime. Тип данных называется datetime64, так как datetime уже используется стандартной библиотекой Python.
Конвенции и предположения для datetime64
Подобно классу Python date, даты выражаются в современном григорианском календаре, бесконечно продолжающемся как в будущее, так и в прошлое. [1] В отличие от Python 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 и т.д.). [2]
Основные типы данных datetime
Самый простой способ создания datetime — из строк в формате даты или времени ISO 8601. Также возможно создание datetime из целого числа, сдвинутого относительно эпохи Unix (00:00:00 UTC 1 января 1970 г.). Единица для внутреннего хранения автоматически выбирается из формы строки и может быть либо единицей даты, либо единицей времени. Единицы дат — годы (‘Y’), месяцы (‘M’), недели (‘W’) и дни (‘D’), а единицы времени — часы (‘h’), минуты (‘m’), секунды (‘s’), миллисекунды (‘ms’) и некоторые дополнительные единицы времени, основанные на секундах с использованием префиксов СИ. Тип данных datetime64 также принимает строку “NAT” (в любой комбинации прописных и строчных букв) для значения «Нет времени».
Пример
Простая дата в формате ISO:
>>> np.datetime64('2005-02-25')
np.datetime64('2005-02-25')
Из целого числа и единицы даты, 1 год от эпохи 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')
np.datetime64('NaT')
При создании массива datetime из строки можно по-прежнему автоматически выбирать единицу из входных данных, используя тип datetime с общими единицами.
Пример
>>> 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', '2002-02-03T13:56:03.172'],
dtype='datetime64[ms]')
Массив datetime можно создать из целых чисел, представляющих метки времени POSIX с заданной единицей.
Пример
>>> 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]')
Тип datetime работает со многими стандартными функциями 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]')
Объект datetime представляет собой единственный момент во времени. Если два datetime имеют разные единицы, они могут представлять один и тот же момент времени, и преобразование из большей единицы, такой как месяцы, в меньшую, такую как дни, считается «безопасным» преобразованием, поскольку момент времени по-прежнему представлен точно.
Пример
>>> np.datetime64('2005') == np.datetime64('2005-01-01')
True
>>> np.datetime64('2010-03-14T15') == np.datetime64('2010-03-14T15:00:00.00')
True
Устарело начиная с версии 1.11.0: NumPy не хранит информацию о часовом поясе. Для обеспечения обратной совместимости datetime64 по-прежнему анализирует смещения часовых поясов, которые обрабатываются путем преобразования в UTC±00:00 (время по Гринвичу). Это поведение устарело и в будущем будет вызывать ошибку.
Арифметика datetime и timedelta
NumPy позволяет вычитать два значения datetime, операция, которая производит число с единицей времени. Поскольку в NumPy нет системы физических величин в ядре, был создан тип данных timedelta64 для дополнения datetime64. Аргументы для timedelta64 — это число, представляющее количество единиц, и единица даты/времени, например (D)ень, (M)есяц, (Y)год, (h)асы, (m)инуты или (s)екунды. Тип данных timedelta64 также принимает строку “NAT” вместо числа для значения «Нет времени».
Пример
>>> np.timedelta64(1, 'D') np.timedelta64(1,'D')
>>> np.timedelta64(4, 'h') np.timedelta64(4,'h')
>>> np.timedelta64('nAt')
np.timedelta64('NaT')
Datetime и Timedelta работают вместе, чтобы обеспечить способы выполнения простых расчетов с датой и временем.
Пример
>>> 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') - np.datetime64('2009-01-01')
np.timedelta64('NaT','D')
>>> np.datetime64('2009-01-01') + np.timedelta64('nat')
np.datetime64('NaT')
Существуют две единицы Timedelta (‘Y’, годы и ‘M’, месяцы), которые обрабатываются особым образом, так как количество времени, которое они представляют, изменяется в зависимости от того, где они используются. В то время как единица timedelta «день» эквивалентна 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
Типы данных Datetime и Timedelta поддерживают большое количество единиц времени, а также общие единицы, которые могут быть преобразованы в любые другие единицы на основе входных данных.
Datetime всегда хранятся с эпохой 1970-01-01T00:00. Это означает, что поддерживаемые даты всегда представляют собой симметричный интервал вокруг эпохи, который в таблице ниже называется «временной интервал».
Длина интервала — это диапазон 64-битного целого числа умноженный на длину даты или единицы. Например, временной интервал для ‘W’ (недели) ровно в 7 раз больше, чем временной интервал для ‘D’ (дней), а временной интервал для ‘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 до н.э., 2,5e16 н.э.] |
А вот единицы времени:
Код | Значение | Временной интервал (относительно) | Временной интервал (абсолютно) |
|---|---|---|---|
h | час | +/- 1,0e15 лет | [1,0e15 до н.э., 1,0e15 н.э.] |
m | минута | +/- 1,7e13 лет | [1,7e13 до н.э., 1,7e13 н.э.] |
s | секунда | +/- 2,9e11 лет | [2,9e11 до н.э., 2,9e11 н.э.] |
ms | миллисекунда | +/- 2,9e8 лет | [2,9e8 до н.э., 2,9e8 н.э.] |
us / мкс | микросекунда | +/- 2,9e5 лет | [290301 до н.э., 294241 н.э.] |
ns | наносекунда | +/- 292 года | [1678 н.э., 2262 н.э.] |
ps | пикосекунда | +/- 106 дней | [1969 н.э., 1970 н.э.] |
fs | фемтосекунда | +/- 2,6 часа | [1969 н.э., 1970 н.э.] |
as | аттосекунда | +/- 9,2 секунды | [1969 н.э., 1970 н.э.] |
Функциональность рабочих дней
Для использования datetime в контекстах, где допустимы только определённые дни недели, NumPy включает набор функций «рабочих дней» (busday).
По умолчанию функции busday учитывают только понедельник по пятницу (обычные рабочие дни). Реализация основана на «weekmask», содержащем 7 логических флагов, указывающих допустимые дни; возможны настраиваемые weekmask, которые задают другие наборы допустимых дней.
Функции «busday» дополнительно могут проверять список дат «выходных», конкретных дат, которые не являются рабочими днями.
Функция busday_offset позволяет применять сдвиги, заданные в рабочих днях, к datetime с единицей ‘D’ (день).
Пример
>>> np.busday_offset('2011-06-23', 1)
np.datetime64('2011-06-24')
>>> np.busday_offset('2011-06-23', 2)
np.datetime64('2011-06-27')
Когда входная дата попадает на выходные или праздничный день, busday_offset сначала применяет правило для переноса даты на ближайший рабочий день, а затем применяет сдвиг. Правило по умолчанию — ‘raise’, которое просто генерирует исключение. Наиболее часто используемые правила — ‘forward’ и ‘backward’.
Пример
>>> 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')
В некоторых случаях для получения желаемого результата необходимо соответствующее использование правила переноса и сдвига.
Пример
Первый рабочий день на или после даты:
>>> 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.
Пример
>>> np.busday_offset('2012-05', 1, roll='forward', weekmask='Sun')
np.datetime64('2012-05-13')
Когда производительность важна для обработки многих дат рабочих дней с конкретным выбором weekmask и праздников, существует объект busdaycalendar, который хранит необходимые данные в оптимизированной форме.
np.is_busday():
Для проверки значения 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])
np.busday_count():
Чтобы определить количество рабочих дней в заданном диапазоне дат 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
Настраиваемые weekmask
Ниже приведены несколько примеров значений custom weekmask. Эти примеры указывают на «busday» по умолчанию — понедельник по пятницу как допустимые дни.
Некоторые примеры:
# 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 time»; поэтому все они обладают одними и теми же хорошо известными недостатками по отношению к шкале времени UTC и определению исторического времени. Краткое, неполное резюме приведено ниже.
-
Невозможно разобрать допустимые метки времени UTC, возникающие во время положительной секунды високосного года.
Пример
«2016-12-31 23:59:60 UTC» был високосной секундой, поэтому «2016-12-31 23:59:60.450 UTC» — это допустимое значение временной метки, которое не может быть разобрано
datetime64:>>> 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" -
Вычисления Timedelta64 между двумя датами UTC могут быть неверными на целое число SI секунд.
Пример
Вычислите количество SI секунд между «2021-01-01 12:56:23.423 UTC» и «2001-01-01 00:00:00.000 UTC»:
>>> ( ... np.datetime64("2021-01-01 12:56:23.423") ... - np.datetime64("2001-01-01") ... ) / np.timedelta64(1, "s") 631198583.423однако правильный ответ —
631198588.423SI секунд, потому что между 2001 и 2021 годами было 5 високосных секунд. -
Вычисления Timedelta64 для дат в прошлом не возвращают SI секунд, как можно ожидать.
Пример
Вычислите количество секунд между «000-01-01 UT» и «1600-01-01 UT», где UT — универсальное время:
>>> a = np.datetime64("0000-01-01", "us") >>> b = np.datetime64("1600-01-01", "us") >>> b - a numpy.timedelta64(50491123200000000,'us')Полученные результаты,
50491123200секунд, получаются как количество прошедших дней (584388) умноженное на86400секунд; это количество секунд часов, синхронизированных с вращением Земли. Точное значение в SI секундах можно только оценить, например, используя данные, опубликованные в Measurement of the Earth’s rotation: 720 BC to AD 2015, 2016, Royal Society’s Proceedings A 472, by Stephenson et.al.. Разумная оценка составляет50491112870 ± 90секунд, с разницей в 10330 секунд.
© 2005–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/reference/arrays.datetime.html