Разбор строк 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")
Другие вопросы по тегам