64

Right now I have:

timestamp = datetime.strptime(date_string, '%Y-%m-%d %H:%M:%S.%f')

This works great unless I'm converting a string that doesn't have the microseconds. How can I specify that the microseconds are optional (and should be considered 0 if they aren't in the string)?

inye
  • 1,754
  • 1
  • 22
  • 29
Digant C Kasundra
  • 1,349
  • 2
  • 15
  • 26

8 Answers8

67

You could use a try/except block:

try:
    timestamp = datetime.strptime(date_string, '%Y-%m-%d %H:%M:%S.%f')
except ValueError:
    timestamp = datetime.strptime(date_string, '%Y-%m-%d %H:%M:%S')
Alexander
  • 96,739
  • 27
  • 183
  • 184
30

What about just appending it if it doesn't exist?

if '.' not in date_string:
    date_string = date_string + '.0'

timestamp = datetime.strptime(date_string, '%Y-%m-%d %H:%M:%S.%f')
stevieb
  • 8,663
  • 3
  • 27
  • 36
  • 13
    This is a good answer, but I'm disappointed that a library designed to take the headache out of transforming date and time strings into date and time objects doesn't deal with these pretty simple use cases. The whole point of such a library is to remove the need to worry about this from the user. – Auspice May 02 '18 at 20:48
  • @Auspice I'm a Perl hacker where we can do these things. I program Python as a forced thing, so I digress ;) – stevieb May 03 '18 at 00:04
  • 2
    I greatly like this answer as opposed to using the try/catch – sniperd May 29 '19 at 20:09
7

I'm late to the party but I found if you don't care about the optional bits this will lop off the .%f for you.

datestring.split('.')[0]

user14608345
  • 91
  • 1
  • 2
6

I prefer using regex matches instead of try and except. This allows for many fallbacks of acceptable formats.

# full timestamp with milliseconds
match = re.match(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z", date_string)
if match:
    return datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%S.%fZ")

# timestamp missing milliseconds
match = re.match(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z", date_string)
if match:
    return datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%SZ")

# timestamp missing milliseconds & seconds
match = re.match(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}Z", date_string)
if match:
    return datetime.strptime(date_string, "%Y-%m-%dT%H:%MZ")

# unknown timestamp format
return false

Don't forget to import "re" as well as "datetime" for this method.

iforapsy
  • 87
  • 1
  • 5
2
datetime(*map(int, re.findall('\d+', date_string)))

can parse both '%Y-%m-%d %H:%M:%S.%f' and '%Y-%m-%d %H:%M:%S'. It is too permissive if your input is not filtered.

It is quick-and-dirty but sometimes strptime() is too slow. It can be used if you know that the input has the expected date format.

jfs
  • 374,366
  • 172
  • 933
  • 1,594
  • This gives incorrect results if, in `date_string`, trailing zeros are omitted from microsecond part. – jez Aug 22 '16 at 19:07
  • 1
    @jez: yes, that is why I said it is "too permissive". It works only if the input has the expected format (none or 6 digits for microseconds). 2- about your edit: look at the question: `datetime` is the class here, not the module. – jfs Aug 22 '16 at 19:14
0

using one regular expression and some list expressions

time_str = "12:34.567"
# time format is [HH:]MM:SS[.FFF]
sum([a*b for a,b in zip(map(lambda x: int(x) if x else 0, re.match(r"(?:(\d{2}):)?(\d{2}):(\d{2})(?:\.(\d{3}))?", time_str).groups()), [3600, 60, 1, 1/1000])])
# result = 754.567
Mila Nautikus
  • 1,481
  • 1
  • 9
  • 18
0

If you are using Pandas you can also filter the the Series and concatenate it. The index is automatically joined.

import pandas as pd

# Every other row has a different format
df = pd.DataFrame({"datetime_string": ["21-06-08 14:36:09", "21-06-08 14:36:09.50", "21-06-08 14:36:10", "21-06-08 14:36:10.50"]})
df["datetime"] = pd.concat([
    pd.to_datetime(df["datetime_string"].iloc[1::2], format="%y-%m-%d %H:%M:%S.%f"),
    pd.to_datetime(df["datetime_string"].iloc[::2], format="%y-%m-%d %H:%M:%S"),
])

datetime_string datetime
0 21-06-08 14:36:09 2021-06-08 14:36:09
1 21-06-08 14:36:09.50 2021-06-08 14:36:09.500000
2 21-06-08 14:36:10 2021-06-08 14:36:10
3 21-06-08 14:36:10.50 2021-06-08 14:36:10.500000
JulianWgs
  • 709
  • 1
  • 11
  • 21
-3

For my similar problem using jq I used the following:

|split("Z")[0]|split(".")[0]|strptime("%Y-%m-%dT%H:%M:%S")|mktime

As the solution to sort my list by time properly.