224

I am able to parse strings containing date/time with time.strptime

>>> import time
>>> time.strptime('30/03/09 16:31:32', '%d/%m/%y %H:%M:%S')
(2009, 3, 30, 16, 31, 32, 0, 89, -1)

How can I parse a time string that contains milliseconds?

>>> time.strptime('30/03/09 16:31:32.123', '%d/%m/%y %H:%M:%S')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.5/_strptime.py", line 333, in strptime
    data_string[found.end():])
ValueError: unconverted data remains: .123
Martin Thoma
  • 91,837
  • 114
  • 489
  • 768
ilkinulas
  • 3,155
  • 3
  • 20
  • 25

7 Answers7

351

Python 2.6 added a new strftime/strptime macro %f. The docs are a bit misleading as they only mention microseconds, but %f actually parses any decimal fraction of seconds with up to 6 digits, meaning it also works for milliseconds or even centiseconds or deciseconds.

time.strptime('30/03/09 16:31:32.123', '%d/%m/%y %H:%M:%S.%f')

However, time.struct_time doesn't actually store milliseconds/microseconds. You're better off using datetime, like this:

>>> from datetime import datetime
>>> a = datetime.strptime('30/03/09 16:31:32.123', '%d/%m/%y %H:%M:%S.%f')
>>> a.microsecond
123000

As you can see, .123 is correctly interpreted as 123 000 microseconds.

iFreilicht
  • 9,961
  • 6
  • 32
  • 63
DNS
  • 34,791
  • 17
  • 84
  • 123
  • 4
    Thanks http://docs.python.org/library/datetime.html : New in version 2.6: time and datetime objects support a %f format code which expands to the number of microseconds in the object, zero-padded on the left to six places. – ilkinulas Mar 30 '09 at 18:01
  • 5
    Woa I can tell the the Python docs needs updating. [Docs for the time module](http://docs.python.org/library/time.html#time.strftime) doesn't say anything about `%f`. – phunehehe Mar 22 '12 at 08:01
  • 16
    The Python docs, as of version 2.7.3, are a bit misleading. For strptime, %f can actually represent any number of decimal places, not just 6, as one might expect for microseconds. So the code above would parse 32.123 seconds and store it as 123,000µs, which is what we want. – Michael Scheper Mar 05 '13 at 03:21
  • 10
    The number in `%f` is padded with zeros on the *right* (not left!) to 6 decimal places. 1 gets parsed to 100000, 12 gets parsed to 120000, and 1234567 produces `ValueError: unconverted data remains: 7` – user443854 Apr 04 '13 at 14:45
  • @user443854 That's what one would expect; 1.1 is one second and 100000 microseconds. – DNS Apr 04 '13 at 15:23
  • 25
    Is it just me or was the question about milliseconds, not microseconds? – Purrell Mar 10 '15 at 21:53
  • 4
    @MichaelScheper: "any number of decimal places" as long as it is less than 7 ;) i.e., `.%f` works for `.1`, `.12`, `.123`, `.1234`, `.12345`, `.123456` but it stops working for `.1234567`. – jfs Mar 03 '16 at 14:13
  • 4
    @Purrell: It's just you. My point was that `%f` will work for strings like '23.123'. (Sorry that I didn't explain that 123,000µs = 123ms; sometimes I forget that a couple of countries still haven't adopted SI units, and are thus less familiar with standard exponential prefixes.) – Michael Scheper Mar 14 '16 at 23:23
  • 1
    The question explicitly asks about milliseconds, not microseconds. This answer is incorrect. – Wayne Workman Sep 17 '18 at 18:05
  • 1
    How did this answer get 254 upvotes when it clearly answers a different question? The question is about milliseconds not microseconds. – Rafid Nov 14 '18 at 20:19
  • @WayneWorkman The answer is not incorrect, it specifically shows that `%f` works for milliseconds just as well. I edited it to make this clearer. – iFreilicht Mar 23 '21 at 09:22
  • @Rafid It doesn't. If you read the code example, you would've noticed that the string to be parsed contains milliseconds, but `%f` parses it correctly. I edited the answer to clarify this. – iFreilicht Mar 23 '21 at 09:23
12

I know this is an older question but I'm still using Python 2.4.3 and I needed to find a better way of converting the string of data to a datetime.

The solution if datetime doesn't support %f and without needing a try/except is:

    (dt, mSecs) = row[5].strip().split(".") 
    dt = datetime.datetime(*time.strptime(dt, "%Y-%m-%d %H:%M:%S")[0:6])
    mSeconds = datetime.timedelta(microseconds = int(mSecs))
    fullDateTime = dt + mSeconds 

This works for the input string "2010-10-06 09:42:52.266000"

Andrew Stern
  • 678
  • 9
  • 18
4

To give the code that nstehr's answer refers to (from its source):

def timeparse(t, format):
    """Parse a time string that might contain fractions of a second.

    Fractional seconds are supported using a fragile, miserable hack.
    Given a time string like '02:03:04.234234' and a format string of
    '%H:%M:%S', time.strptime() will raise a ValueError with this
    message: 'unconverted data remains: .234234'.  If %S is in the
    format string and the ValueError matches as above, a datetime
    object will be created from the part that matches and the
    microseconds in the time string.
    """
    try:
        return datetime.datetime(*time.strptime(t, format)[0:6]).time()
    except ValueError, msg:
        if "%S" in format:
            msg = str(msg)
            mat = re.match(r"unconverted data remains:"
                           " \.([0-9]{1,6})$", msg)
            if mat is not None:
                # fractional seconds are present - this is the style
                # used by datetime's isoformat() method
                frac = "." + mat.group(1)
                t = t[:-len(frac)]
                t = datetime.datetime(*time.strptime(t, format)[0:6])
                microsecond = int(float(frac)*1e6)
                return t.replace(microsecond=microsecond)
            else:
                mat = re.match(r"unconverted data remains:"
                               " \,([0-9]{3,3})$", msg)
                if mat is not None:
                    # fractional seconds are present - this is the style
                    # used by the logging module
                    frac = "." + mat.group(1)
                    t = t[:-len(frac)]
                    t = datetime.datetime(*time.strptime(t, format)[0:6])
                    microsecond = int(float(frac)*1e6)
                    return t.replace(microsecond=microsecond)

        raise
Community
  • 1
  • 1
Phil H
  • 18,593
  • 6
  • 62
  • 99
3

DNS answer above is actually incorrect. The SO is asking about milliseconds but the answer is for microseconds. Unfortunately, Python`s doesn't have a directive for milliseconds, just microseconds (see doc), but you can workaround it by appending three zeros at the end of the string and parsing the string as microseconds, something like:

datetime.strptime(time_str + '000', '%d/%m/%y %H:%M:%S.%f')

where time_str is formatted like 30/03/09 16:31:32.123.

Hope this helps.

Rafid
  • 16,077
  • 21
  • 63
  • 98
  • 4
    I thought the same thing initially, but see the comments on the answer and the [docs](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior). It's *left-zero-padded* microseconds, so `.123` is correctly interpreted as 123,000 microseconds – aschmied Mar 10 '19 at 17:22
1

For python 2 i did this

print ( time.strftime("%H:%M:%S", time.localtime(time.time())) + "." + str(time.time()).split(".",1)[1])

it prints time "%H:%M:%S" , splits the time.time() to two substrings (before and after the .) xxxxxxx.xx and since .xx are my milliseconds i add the second substring to my "%H:%M:%S"

hope that makes sense :) Example output:

13:31:21.72 Blink 01


13:31:21.81 END OF BLINK 01


13:31:26.3 Blink 01


13:31:26.39 END OF BLINK 01


13:31:34.65 Starting Lane 01


Papatrexas
  • 11
  • 1
1

My first thought was to try passing it '30/03/09 16:31:32.123' (with a period instead of a colon between the seconds and the milliseconds.) But that didn't work. A quick glance at the docs indicates that fractional seconds are ignored in any case...

Ah, version differences. This was reported as a bug and now in 2.6+ you can use "%S.%f" to parse it.

MarkusQ
  • 21,092
  • 3
  • 53
  • 67
1

from python mailing lists: parsing millisecond thread. There is a function posted there that seems to get the job done, although as mentioned in the author's comments it is kind of a hack. It uses regular expressions to handle the exception that gets raised, and then does some calculations.

You could also try do the regular expressions and calculations up front, before passing it to strptime.

C R
  • 1,902
  • 5
  • 30
  • 39
nstehr
  • 7,560
  • 2
  • 16
  • 22
  • yes, i know that thread. But i am looking for a simpler way. is there any module in the standart python lib that makes the time parsing with milliseconds? – ilkinulas Mar 30 '09 at 17:48