Разбор строк datetime, содержащих наносекунды
У меня есть несколько файлов журнала со временем в формате HH:MM::SS.nano_seconds (например, 01:02:03.123456789). Я хотел бы создать datetime в Python, чтобы я мог аккуратно делать математику по времени (например, принимать разницы во времени). strptime хорошо работает в течение микросекунд, используя% f. Действительно ли модули даты и времени Python не поддерживают наносекунды?
6 ответов
Из источника видно, что объекты datetime не поддерживают ничего более прекрасного, чем микросекунды. Как отметил Майк Пеннингтон в комментариях, это происходит потому, что реальные аппаратные часы не настолько точны. Википедия говорит, что частота HPET "не менее 10 МГц", что означает один такт на 100 наносекунд.
Если вы можете смириться с выбрасыванием последних трех цифр (которые, вероятно, в любом случае не слишком значимы), вы можете разобрать это, просто нарезав входную строку, чтобы иметь только шесть цифр после десятичной точки, и выполните синтаксический анализ с %f
, В противном случае, похоже, вам придется самостоятельно вычитать.
Это старая ветка, но все же...
Для этого вы можете использовать функциональность Pandas. У меня были временные метки вроде '2019-03-22T14:00:01.700311864Z', которые я преобразовал в метку времени:
firstStamp = pd.to_datetime(firstStampString, format='%Y-%m-%dT%H:%M:%S.%fZ')
lastStamp = pd.to_datetime(lastStampString, format='%Y-%m-%dT%H:%M:%S.%fZ')
deltaTime = lastStamp - firstStamp
Это прекрасно работает.
Вы можете вполне естественно использовать наносекунды и даже более точные единицы времени (ps, fs, as) с numpy
, Numpy имеет собственную реализацию Datetime и Timedeltas, так что вы можете попробовать np.datetime64
:
import numpy as np
def str_to_ns(time_str):
"""
input: time in a format `hh:mm:ss.up_to_9_digits`
"""
h, m, s = time_str.split(":")
int_s, ns = s.split(".")
ns = map(lambda t, unit: np.timedelta64(t, unit),
[h,m,int_s,ns.ljust(9, '0')],['h','m','s','ns'])
return sum(ns)
Тогда вы можете использовать эту функцию следующим образом:
>>> src = "1:2:34.123456789"
>>> out = str_to_ns(src)
>>> print(out)
3754123456789 nanoseconds
>>> out / np.timedelta64(1,'h')
1.0428120713302778
>>> out / np.timedelta64(1,'m')
62.568724279816664
>>> out / np.timedelta64(1,'s')
3754.123456789
Арифметика также возможна:
>>> t1, t2 = str_to_ns("1:0:12.12345678"), str_to_ns("1:0:12.12")
>>> t1 - t2
numpy.timedelta64(3456780,'ns')
Я согласен, что это не так естественно, но таким образом вы можете достичь произвольной высокой точности по времени, просто numpy
,
Если вас на самом деле не интересуют наносекунды, но вы все равно хотите иметь возможность анализировать время, которое имеет>6 десятичных знаков в секундах, вы можете использовать библиотеку python-dateutils .
Например, при попытке использовать стандартный пакет lib datetime:
>>> from datetime import datetime
>>> datetime.strptime('2021-02-14T02:27:57.96119078Z', '%Y-%m-%dT%H:%M:%S.%fZ')
ValueError: time data '2021-02-14T02:27:57.96119078Z' does not match format '%Y-%m-%dT%H:%M:%S.%fZ'
Но с python-dateutils он фактически анализирует его, не выдавая ошибки:
>>> from dateutil.parser import isoparse
>>> isoparse('2021-02-14T02:27:57.96119078Z')
datetime.datetime(2021, 2, 14, 2, 27, 57, 961190, tzinfo=tzutc())
Обратите внимание, что он не сохраняет наносекунды (и не округляет правильно - он просто обрезается после 6 знаков после запятой), но, по крайней мере, не нарушает синтаксический анализ>6 знаков после запятой.
def parse_nanodate(s):
"""
parse date, ignore nanoseconds
sample input: 2020-12-31T16:20:00.000000123Z
--> 123ns will be ignored
"""
if s[-1] == 'Z':
# add explicit UTC timezone, to make strptime happy
s += '+0000'
return datetime.datetime.strptime(
s[0:26]+s[29:], '%Y-%m-%dT%H:%M:%S.%fZ%z')
Я мог бы удалить любые цифры после 6-й с помощью замены регулярного выражения:
def parse_nanosecond_ts(ts):
ts = re.sub(
r"^([^ ]+ [0-9]+:[0-9]+:[0-9]+\.[0-9]{0,6})[0-9]*( .*)$",
"\\1\\2",
ts,
)
return datetime.datetime.strptime(ts,
"%Y-%m-%d %H:%M:%S.%f %z %Z")