Format a datetime into a string with milliseconds
Asked Answered
F

16

371

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

Featherweight answered 28/9, 2011 at 19:28 Comment(3)
#312127Tocci
Please write a title that describes your problem and try to keep your question clear and to the point.Falcongentle
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')Harless
I
658

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.

Interpellate answered 23/8, 2013 at 15:20 Comment(13)
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")Lugworm
In case microseconds are 0, in windows 2.7 implementation microseconds are not printed out so it trims seconds :(Caesaria
@cabbi, you could use this instead: (dt, micro) = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f').split('.') ; dt = "%s.%03d" % (dt, int(micro) / 1000) ; print dt. I added this as an answer.Delisadelisle
note that this truncates, not rounds to millisecondsPicard
(dt, micro) = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f').split('.') ; dt = "%s.%03d" % (dt, round(int(micro) / 1000.0)) ; print dtHammonds
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 partPappose
@Picard 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.Launceston
I like this solution most, because it tries to use the same format string for parsing and representing the dates. Nevertheless, I am not able to use the %z format specifier at the end for the timezone. I think there should be another format specifier for the miliseconds... for example %F.Oversold
@Picard the datetime.isoformat(sep='T', timespec='auto') which is suggested in next answer does the same: truncation. Maybe this helps locating timestamps via text search?Hescock
nitpick: %Y-%m-%d %H:%M:%S.%f == %F %T.%fHalftimbered
Where is the %F %T documented? I can't find it in the Python's page for datetime. Is it deprecated? It works even in Python 3.10, though. I do see it in pages on POSIX datetime format codes.Merengue
@Merengue At the bottom of the linked Python docs, it mentions to look at the manpage for more.Cab
When I use the code from this answer my file name looks like this: 2024-02-17 11/22/38 (slashes instead of colons!). Here is my code: timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S'). Using . or _ works, but not : for some reason. Hah!Centralization
M
163

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'
Mezereon answered 10/5, 2019 at 9:15 Comment(3)
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'Shiv
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/…Hescock
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).Awaken
D
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'
Delisadelisle answered 26/2, 2016 at 4:32 Comment(2)
What I'am actually doing is this: print '%s.%03d'%(dt.strftime("%Y-%m-%d %H:%M:%S"), int(dt.microsecond/1000))Caesaria
agree with @cabbi, there's no need to convert back and forth with string and intHydrosphere
K
31

Using strftime:

>>> from datetime import datetime
>>> datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
'20220402055654344968'
Kangaroo answered 28/9, 2011 at 19:35 Comment(4)
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.Himself
microseconds can be less than 6 digits, so [:-3] is printing out wrong millisecondsCaesaria
what if we have timezone?Hypso
@ᐅdevrimbaris for timezone checkout Lorenzo's answerShiv
E
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'
Edmanda answered 4/12, 2013 at 16:50 Comment(1)
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".Launceston
S
6

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().

Salvage answered 2/10, 2020 at 7:38 Comment(0)
A
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
Acaudal answered 6/10, 2016 at 8:15 Comment(0)
N
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
Norwood answered 28/9, 2011 at 20:33 Comment(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 - slicingRambow
Maybe something to do with the str(datetime.datetime.utcnow()) being called in each iteration of the test vs setting it once?Norwood
S
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'
Save answered 1/12, 2023 at 9:39 Comment(0)
R
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:]

Rambow answered 28/9, 2011 at 21:36 Comment(1)
Nice! That is indeed cleaner without sacrificing performance. str.translate() actually faster in my testing.Norwood
R
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]
Redraft answered 28/7, 2015 at 22:37 Comment(0)
S
1
python -c "from datetime import datetime; print str(datetime.now())[:-3]"
2017-02-09 10:06:37.006
Sindhi answered 9/2, 2017 at 18:9 Comment(0)
B
1
datetime
t = datetime.datetime.now()
ms = '%s.%i' % (t.strftime('%H:%M:%S'), t.microsecond/1000)
print(ms)
14:44:37.134
Bellwether answered 5/3, 2019 at 14:45 Comment(0)
J
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).

Jaipur answered 2/5, 2021 at 0:26 Comment(0)
R
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.

Resist answered 7/8, 2019 at 13:19 Comment(0)
C
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.

Cab answered 2/4, 2022 at 5:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.