381

How can I format a datetime object as a string with milliseconds?

3
  • 1
    stackoverflow.com/questions/311627/…
    – cetver
    Commented Sep 28, 2011 at 19:31
  • 4
    Please write a title that describes your problem and try to keep your question clear and to the point.
    – agf
    Commented Sep 28, 2011 at 19:34
  • 2
    It's worth mentioning here that extra precision is often just fine. For example, Java's Instant.parse can parse represenation created with strftime('%Y-%m-%dT%H:%M:%S.%fZ') Commented Mar 8, 2017 at 9:03

17 Answers 17

667

To get a date string with milliseconds, use [:-3] to trim the last three digits of %f (microseconds):

>>> from datetime import datetime
>>> datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
'2022-09-24 10:18:32.926'

Or shorter:

>>> from datetime import datetime
>>> datetime.utcnow().strftime('%F %T.%f')[:-3]
'2022-09-24 10:18:32.926'

See the Python docs for more "%" format codes and the strftime(3) man page for the full list.

13
  • 9
    Note, if you want to use import datetime instead of from datetime import datetime, you'll have to use this: datetime.datetime.utcnow().strftime("%H:%M:%S.%f")
    – Luc
    Commented Sep 16, 2015 at 11:01
  • 25
    In case microseconds are 0, in windows 2.7 implementation microseconds are not printed out so it trims seconds :(
    – cabbi
    Commented Nov 9, 2015 at 15:37
  • 20
    note that this truncates, not rounds to milliseconds
    – gens
    Commented Oct 1, 2017 at 18:11
  • 3
    As gens mentions, won't this get it wrong it the first drops number is >=5?. In fact if the usec part is > 999500, then you will never get the time right by fiddling with microseconds part
    – Chris
    Commented Nov 21, 2017 at 1:11
  • 8
    @gens for time, truncation is preferred to rounding. E.g. 07:59:59.999 should be truncated to 07:59:59 or 07:59 instead of rounding to 08:00:00 or 08:00, just as 07:59:59 should be truncated to 07:59 instead of rounding to 08:00. Commented Jul 1, 2021 at 21:59
165

With Python 3.6+, you can set isoformat's timespec:

>>> from datetime import datetime
>>> datetime.utcnow().isoformat(sep=' ', timespec='milliseconds')
'2019-05-10 09:08:53.155'
3
  • 2
    Useful with timezone too: date = datetime(2019,5,10) date_with_tz = pytz.timezone('Europe/Rome').localize(date) date_with_tz.isoformat(sep='T', timespec='milliseconds') output: '2019-05-10T00:00:00.000+02:00'
    – Ena
    Commented May 10, 2019 at 9:17
  • 1
    This looks cleaner than the [:-3] in the accepted answer, but you should know that it seemingly does the same >>> datetime.fromisoformat('2021-12-08 20:00:00.678900').isoformat(sep=' ', timespec='milliseconds') leads to '2021-12-08 20:00:00.678'. Is the truncation specified in ISO standard or is it just a bug? The implementation uses integer division: github.com/python/cpython/blob/…
    – Wolf
    Commented Dec 8, 2021 at 19:00
  • As per Wikipedia (en.wikipedia.org/wiki/ISO_8601#cite_note-37), "Separating date and time parts with other characters such as space is not allowed in ISO 8601, but allowed in its profile RFC 3339", so its a good idea to add something like 'T' as separator, and "If the time is in UTC, add a Z directly after the time without a space" (excerpt from Wikipedia). Commented Dec 4, 2022 at 15:46
35

@Cabbi raised the issue that on some systems (Windows with Python 2.7), the microseconds format %f may incorrectly give "0", so it's not portable to simply trim the last three characters. Such systems do not follow the behavior specified by the documentation:

Directive Meaning Example
%f Microsecond as a decimal number, zero-padded to 6 digits. 000000, 000001, …, 999999

The following code carefully formats a timestamp with milliseconds:

>>> from datetime import datetime
>>> (dt, micro) = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f').split('.')
>>> "%s.%03d" % (dt, int(micro) / 1000)
'2016-02-26 04:37:53.133'

To get the exact output that the OP wanted, we have to strip punctuation characters:

>>> from datetime import datetime
>>> (dt, micro) = datetime.utcnow().strftime('%Y%m%d%H%M%S.%f').split('.')
>>> "%s%03d" % (dt, int(micro) / 1000)
'20160226043839901'
2
  • 10
    What I'am actually doing is this: print '%s.%03d'%(dt.strftime("%Y-%m-%d %H:%M:%S"), int(dt.microsecond/1000))
    – cabbi
    Commented Mar 8, 2016 at 8:35
  • 3
    agree with @cabbi, there's no need to convert back and forth with string and int
    – caoanan
    Commented May 21, 2018 at 3:15
31

Using strftime:

>>> from datetime import datetime
>>> datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
'20220402055654344968'
4
  • 23
    FYI, this prints microseconds as the last 6 digits. Add [:-3] to the end to drop the last 3 digits so that it is only displaying milliseconds. Commented Jan 2, 2014 at 21:40
  • 9
    microseconds can be less than 6 digits, so [:-3] is printing out wrong milliseconds
    – cabbi
    Commented Nov 9, 2015 at 15:26
  • 15
    what if we have timezone? Commented Mar 4, 2017 at 11:38
  • 1
    @ᐅdevrimbaris for timezone checkout Lorenzo's answer
    – Ena
    Commented May 10, 2019 at 9:18
11

Use [:-3] to remove the 3 last characters since %f is for microseconds:

>>> from datetime import datetime
>>> datetime.now().strftime('%Y/%m/%d %H:%M:%S.%f')[:-3]
'2013/12/04 16:50:03.141'
1
  • 3
    As mentioned in other answers, if microseconds are 0 then the ".123455" portion may be printed as ".0", causing truncation of the last 3 chars to eat the low seconds digit and ".0". Commented Jul 1, 2021 at 22:06
7

In python 3.6 and above using python f-strings:

from datetime import datetime, timezone

dt = datetime.now(timezone.utc)

print(f"{dt:%Y-%m-%d %H:%M:%S}.{dt.microsecond // 1000:03d}")

The code specific to format milliseconds is:

{dt.microsecond // 1000:03d}

The format string {:03d} and microsecond to millisecond conversion // 1000 is from def _format_time in https://github.com/python/cpython/blob/master/Lib/datetime.py that is used for datetime.datetime.isoformat().

5
import datetime

# convert string into date time format.

str_date = '2016-10-06 15:14:54.322989'
d_date = datetime.datetime.strptime(str_date , '%Y-%m-%d %H:%M:%S.%f')
print(d_date)
print(type(d_date)) # check d_date type.


# convert date time to regular format.

reg_format_date = d_date.strftime("%d %B %Y %I:%M:%S %p")
print(reg_format_date)

# some other date formats.
reg_format_date = d_date.strftime("%Y-%m-%d %I:%M:%S %p")
print(reg_format_date)
reg_format_date = d_date.strftime("%Y-%m-%d %H:%M:%S")
print(reg_format_date)

<<<<<< OUTPUT >>>>>>>

2016-10-06 15:14:54.322989    
<class 'datetime.datetime'>    
06 October 2016 03:14:54 PM    
2016-10-06 03:14:54 PM    
2016-10-06 15:14:54
0
2

I assume you mean you're looking for something that is faster than datetime.datetime.strftime(), and are essentially stripping the non-alpha characters from a utc timestamp.

You're approach is marginally faster, and I think you can speed things up even more by slicing the string:

>>> import timeit
>>> t=timeit.Timer('datetime.utcnow().strftime("%Y%m%d%H%M%S%f")','''
... from datetime import datetime''')
>>> t.timeit(number=10000000)
116.15451288223267

>>> def replaceutc(s):
...     return s\
...         .replace('-','') \
...         .replace(':','') \
...         .replace('.','') \
...         .replace(' ','') \
...         .strip()
... 
>>> t=timeit.Timer('replaceutc(str(datetime.datetime.utcnow()))','''
... from __main__ import replaceutc
... import datetime''')
>>> t.timeit(number=10000000)
77.96774983406067

>>> def sliceutc(s):
...     return s[:4] + s[5:7] + s[8:10] + s[11:13] + s[14:16] + s[17:19] + s[20:]
... 
>>> t=timeit.Timer('sliceutc(str(datetime.utcnow()))','''
... from __main__ import sliceutc
... from datetime import datetime''')
>>> t.timeit(number=10000000)
62.378515005111694
2
  • @oxtopus Good work. Personally, I don't use timeit anymore for simple measuring of time. It's strange that the ratios of times are different with your code: 1 - 0.67 - 0.53 and with mine: 1 - 0.35 - 0.20 , for the methods strftime - replace - slicing
    – eyquem
    Commented Sep 28, 2011 at 21:47
  • Maybe something to do with the str(datetime.datetime.utcnow()) being called in each iteration of the test vs setting it once? Commented Sep 28, 2011 at 22:10
2

In Python 3.11 (maybe older version support it too) it can be done with isoformat(timespec='milliseconds')

From datetime.py:

The optional argument timespec specifies the number of additional terms of the time to include. Valid options are 'auto', 'hours', 'minutes', 'seconds', 'milliseconds' and 'microseconds'.

>>> datetime.now().isoformat(timespec='milliseconds')
'2023-12-01T10:34:18.657'
1
from datetime import datetime
from time import clock

t = datetime.utcnow()
print 't == %s    %s\n\n' % (t,type(t))

n = 100000

te = clock()
for i in xrange(1):
    t_stripped = t.strftime('%Y%m%d%H%M%S%f')
print clock()-te
print t_stripped," t.strftime('%Y%m%d%H%M%S%f')"

print

te = clock()
for i in xrange(1):
    t_stripped = str(t).replace('-','').replace(':','').replace('.','').replace(' ','')
print clock()-te
print t_stripped," str(t).replace('-','').replace(':','').replace('.','').replace(' ','')"

print

te = clock()
for i in xrange(n):
    t_stripped = str(t).translate(None,' -:.')
print clock()-te
print t_stripped," str(t).translate(None,' -:.')"

print

te = clock()
for i in xrange(n):
    s = str(t)
    t_stripped = s[:4] + s[5:7] + s[8:10] + s[11:13] + s[14:16] + s[17:19] + s[20:] 
print clock()-te
print t_stripped," s[:4] + s[5:7] + s[8:10] + s[11:13] + s[14:16] + s[17:19] + s[20:] "

result

t == 2011-09-28 21:31:45.562000    <type 'datetime.datetime'>


3.33410112179
20110928212155046000  t.strftime('%Y%m%d%H%M%S%f')

1.17067364707
20110928212130453000 str(t).replace('-','').replace(':','').replace('.','').replace(' ','')

0.658806915404
20110928212130453000 str(t).translate(None,' -:.')

0.645189262881
20110928212130453000 s[:4] + s[5:7] + s[8:10] + s[11:13] + s[14:16] + s[17:19] + s[20:]

Use of translate() and slicing method run in same time
translate() presents the advantage to be usable in one line

Comparing the times on the basis of the first one:

1.000 * t.strftime('%Y%m%d%H%M%S%f')

0.351 * str(t).replace('-','').replace(':','').replace('.','').replace(' ','')

0.198 * str(t).translate(None,' -:.')

0.194 * s[:4] + s[5:7] + s[8:10] + s[11:13] + s[14:16] + s[17:19] + s[20:]

1
  • 1
    Nice! That is indeed cleaner without sacrificing performance. str.translate() actually faster in my testing. Commented Sep 28, 2011 at 21:47
1

I dealt with the same problem but in my case it was important that the millisecond was rounded and not truncated

from datetime import datetime, timedelta

def strftime_ms(datetime_obj):
    y,m,d,H,M,S = datetime_obj.timetuple()[:6]
    ms = timedelta(microseconds = round(datetime_obj.microsecond/1000.0)*1000)
    ms_date = datetime(y,m,d,H,M,S) + ms
    return ms_date.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
1
python -c "from datetime import datetime; print str(datetime.now())[:-3]"
2017-02-09 10:06:37.006
1
datetime
t = datetime.datetime.now()
ms = '%s.%i' % (t.strftime('%H:%M:%S'), t.microsecond/1000)
print(ms)
14:44:37.134
1

If you are prepared to store the time in a variable and do a little string manipulation, then you can actually do this without using the datetime module.

>>> _now = time.time()
>>> print ("Time : %s.%s\n" % (time.strftime('%x %X',time.localtime(_now)),
... str('%.3f'%_now).split('.')[1])) # Rounds to nearest millisecond
Time : 05/02/21 01:16:58.676

>>> 

%.3f will round to out put the nearest millisecond, if you want more or less precision just change the number of decimal places

>>> print ("Time : %s.%s\n" % (time.strftime('%x %X',time.localtime(_now)),
... str('%.1f'%_now).split('.')[1])) # Rounds to nearest tenth of a second
Time : 05/02/21 01:16:58.7

>>>

Tested in Python 2.7 and 3.7 (obviously you need to leave out the brackets when calling print in version 2.x).

0

The problem with datetime.utcnow() and other such solutions is that they are slow.

More efficient solution may look like this one:

def _timestamp(prec=0):
    t = time.time()
    s = time.strftime("%H:%M:%S", time.localtime(t))
    if prec > 0:
        s += ("%.9f" % (t % 1,))[1:2+prec]
    return s

Where prec would be 3 in your case (milliseconds).

The function works up to 9 decimal places (please note number 9 in the 2nd formatting string).

If you'd like to round the fractional part, I'd suggest building "%.9f" dynamically with desired number of decimal places.

0

Field-width format specification

The UNIX date command allows specifying %3 to reduce the precision to 3 digits:

$ date '+%Y-%m-%d %H:%M:%S.%3N'
2022-01-01 00:01:23.456

Here's a custom function that can do that in Python:

from datetime import datetime

def strftime_(fmt: str, dt: datetime) -> str:
    tokens = fmt.split("%")
    tokens[1:] = [_format_token(dt, x) for x in tokens[1:]]
    return "".join(tokens)

def _format_token(dt: datetime, token: str) -> str:
    if len(token) == 0:
        return ""
    if token[0].isnumeric():
        width = int(token[0])
        s = dt.strftime(f"%{token[1]}")[:width]
        return f"{s}{token[2:]}"
    return dt.strftime(f"%{token}")

Example usage:

>>> strftime_("%Y-%m-%d %H:%M:%S.%3f", datetime.now())
'2022-01-01 00:01:23.456'

NOTE: %% is not supported.

0

It depends on how whether you want to truncate or round microseconds:

  • example time:

    tm = time(10,11,12,microsecond=95900)

  • truncating - as others have mentioned above:

    print(f'{tm:%H:%M:%S.%f}'[:-3])

    10:11:12.095

  • rounding - AFAIK there is no format specifier in Python to do this so you have to round the microseconds then format:

    print(f'{tm:%H:%M:%S}.{tm.microsecond/1000:03.0f}')

    10:11:12.096

Not the answer you're looking for? Browse other questions tagged or ask your own question.