53

I have some log files with times in the format HH:MM::SS.nano_seconds (e.g. 01:02:03.123456789). I would like to create a datetime in python so I can neatly do math on the time (e.g. take time differences). strptime works well for microseconds using %f. Do the Python datetime and time modules really not support nanoseconds?

Kevin
  • 74,910
  • 12
  • 133
  • 166
user1332148
  • 1,256
  • 2
  • 11
  • 24
  • 1
    possible duplicate of [Get POSIX/Unix time in seconds and nanoseconds in Python?](http://stackoverflow.com/questions/2394485/get-posix-unix-time-in-seconds-and-nanoseconds-in-python) – Mike Pennington May 16 '12 at 03:15
  • 1
    @MikePennington That question deals with getting a clock time with nanoseconds, not parsing them and doing math on them independent of an actual clock. – Danica May 16 '12 at 04:32
  • @Dougal, the question is very relevant since they point out that nanosecond time precision requires platform support, and most do not. In that question, string formatting of nanoseconds is discussed as well – Mike Pennington May 16 '12 at 04:34
  • 6
    @MikePennington Well, it doesn't solve the question or give a reason why it's hard to solve -- just shows why standard datetime approaches don't do it. This problem only needs to deal with nanoseconds in the abstract, not do anything involving actual system times. It's a useful link but not a duplicate. – Danica May 16 '12 at 04:38
  • 1
    Maybe a solution with **PEP 564 - Add new time functions with nanosecond resolution** (October 2017) https://www.python.org/dev/peps/pep-0564/ – oHo Feb 27 '18 at 12:55

6 Answers6

36

You can see from the source that datetime objects don't support anything more fine than microseconds. As pointed out by Mike Pennington in the comments, this is likely because computer hardware clocks aren't nearly that precise. Wikipedia says that HPET has frequency "at least 10 MHz," which means one tick per 100 nanoseconds.

If you can live with throwing out the last three digits (which probably aren't too meaningful anyway), you could parse this by just slicing the input string to have only six digits after the decimal point and parsing with %f. Otherwise, it looks like you'll have to implement the subtraction yourself.


Much later update: numpy and pandas now each have (somewhat different) support for timestamps that includes the possibility of tracking nanoseconds, which are often good solutions. See the other answers for how.

Python 3.7+ also has time.time_ns and related functions in time (PEP 564), but still no support for nanoseconds in datetime.

Danica
  • 28,423
  • 6
  • 90
  • 122
  • Thanks. That's what I suspected. – user1332148 May 16 '12 at 16:42
  • 44
    The system clock is not the only source of time one might want to use datetime and timedelta with. It is annoying (bordering on myopic idiocy) that they aren't implemented using nsecs instead of usecs. – travc Jan 18 '13 at 07:27
  • 4
    @travc: There is open CPython issue: [datetime module has no support for nanoseconds](https://bugs.python.org/issue15443) – jfs Jul 11 '15 at 16:58
  • 7
    Here in 2016, this is still a problem. I agree with @travc that it is idiocy to not support nanoseconds, since many scientists require such precision when working with Python – sirgogo Jun 28 '16 at 21:40
  • 1
    @sirgogo, just a note that numpy's [`datetime64` dtype](http://docs.scipy.org/doc/numpy/reference/arrays.datetime.html) supports down to attoseconds (sort of). – Danica Jun 28 '16 at 21:45
  • 1
    Thanks, yeah that is useful. But on the other hand, it doesn't support converting from strings (e.g. "Nov") to date-time representations. It would be nice to have just one module that does both. Maybe its **time** to bug the numpy guys. – sirgogo Jun 28 '16 at 21:51
  • @sirgogo Pandas should have functions that do that. – Danica Jun 28 '16 at 21:52
  • 5
    See also **PEP 564 - Add new time functions with nanosecond resolution** (October 2017) https://www.python.org/dev/peps/pep-0564/ – oHo Feb 27 '18 at 12:54
  • @sirgogo why do I feel like parsing "Nov" is one step closer to turning into PHP :) – l3utterfly Aug 02 '19 at 16:19
  • parsing nanosecond time strings works fine in python (via pandas) and c++ (via boost datetime) and it is important because this is what financial institutions provide and it helps separate and sort tick data – nurettin Jul 29 '20 at 07:24
  • @travc i got me this [1.5 THz a/d](https://www.keysight.com/us/en/assets/7018-02294/brochures/5990-4592.pdf) it is annoying that these functions are implemented using usecs when i need femto-seconds (10^-15). – Trevor Boyd Smith Nov 16 '21 at 21:11
  • I second the system clock bit, I'm trying to parse Wireshark packet timestamps from a pcap capture. They have stuff going like `May 11, 2022 11:48:24.043295000 PDT` – jxramos May 13 '22 at 03:55
14

This is an old thread, but still...

You can use Pandas functionality to achieve this. I had timestamps like '2019-03-22T14:00:01.700311864Z' which I converted to a Timestamp by:

    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

This works fine.

WolfiG
  • 1,059
  • 14
  • 31
  • 1
    This is obvious but I've initially missed it: if your datetime string does not contain the "Z" suffix, you should also remove the "Z" from the "format" string. – BrunoF Feb 12 '21 at 18:07
  • This may not work if you have some timezone following the seconds, pandas seems to have struggled with `pd.to_datetime("May 11, 2022 11:48:24.043295000 PDT", "%b %d, %Y %H:%M:%S.%f %Z")` – jxramos May 13 '22 at 04:36
9

You can quite naturally use nanoseconds and even more precise time units (ps, fs, as) with numpy. Numpy has its own Datetimes and Timedeltas implementation, so you can try 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)

Then you can use this function in a following way:

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

Arithmetic is also possible:

>>> t1, t2 = str_to_ns("1:0:12.12345678"), str_to_ns("1:0:12.12")
>>> t1 - t2
numpy.timedelta64(3456780,'ns')

I agree that it's not that natural, but in this manner you can achieve arbitrary high time precision with just numpy.

Yury Kirienko
  • 1,810
  • 1
  • 22
  • 32
6

If you don't actually care about the nanoseconds, but you still want to be able to parse datetimes that have >6 decimal places in the seconds, you can use the python-dateutils library.

For example, trying to use standard lib datetime package:

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

But with python-dateutils, it actually parses it without throwing an error:

>>> from dateutil.parser import isoparse
>>> isoparse('2021-02-14T02:27:57.96119078Z')
datetime.datetime(2021, 2, 14, 2, 27, 57, 961190, tzinfo=tzutc())

Note that it doesn't preserve the nanoseconds (nor does it round correctly - it just chops off after 6 decimal places), but it at least won't break parsing >6 decimal places.

Jack Hafner
  • 61
  • 1
  • 1
2
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')

milahu
  • 2,447
  • 1
  • 18
  • 25
0

I could remove any digits after the 6th by means of regular expression substitution:

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")
Raúl Salinas-Monteagudo
  • 3,412
  • 1
  • 24
  • 22