I would like to parse a date that can come in several formats, that I know beforehand. If I could not parse, I return nil. In ruby, I do like this:
DATE_FORMATS = ['%m/%d/%Y %I:%M:%S %p', '%Y/%m/%d %H:%M:%S', '%d/%m/%Y %H:%M', '%m/%d/%Y', '%Y/%m/%d']
def parse_or_nil(date_str)
parsed_date = nil
DATE_FORMATS.each do |f|
parsed_date ||= DateTime.strptime(date_str, f) rescue nil
end
parsed_date
end
This is concise and works. How can I do the same thing in Python?
I would just try dateutil. It can recognize most of the formats:
from dateutil import parser
parser.parse(string)
if you end up using datetime.strptime as suggested #RocketDonkey:
from datetime import datetime
def func(s,flist):
for f in flist:
try:
return datetime.strptime(s,f)
except ValueError:
pass
You can use try/except to catch the ValueError that would occur when trying to use a non-matching format. As #Bakuriu mentions, you can stop the iteration when you find a match to avoid the unnecessary parsing, and then define your behavior when my_date doesn't get defined because not matching formats are found:
You can use try/except to catch the ValueError that would occur when trying to use a non-matching format:
from datetime import datetime
DATE_FORMATS = ['%m/%d/%Y %I:%M:%S %p', '%Y/%m/%d %H:%M:%S', '%d/%m/%Y %H:%M', '%m/%d/%Y', '%Y/%m/%d']
test_date = '2012/1/1 12:32:11'
for date_format in DATE_FORMATS:
try:
my_date = datetime.strptime(test_date, date_format)
except ValueError:
pass
else:
break
else:
my_date = None
print my_date # 2012-01-01 12:32:11
print type(my_date) # <type 'datetime.datetime'>
After your tip, RocketDonkey, and the one from Bakuriu, I could write a shorter version. Any problem with it?
def parse_or_none(date):
for date_format in DATE_FORMATS:
try:
return datetime.strptime(date, date_format)
except ValueError:
pass
return None
Modifying root's answer to handle m/d/y:
from dateutil import parser
parser.parse(string, dayfirst=False)
There's also a yearfirst option, to prefer y/m/d.
Related
I am using a script that someone else created (and another person has been using successfully for months), and I can't figure out this error:
Exception has occurred: ValueError
time data '2022-12-28 14:43:08.908271' does not match format '%m/%d/%Y %I:%M:%S %p' (match)
File ". . .\res-script.py", line 19, in <module>
end = pd.to_datetime(yesterday, format="%m/%d/%Y %I:%M:%S %p").normalize()
I'm trying this and still getting the same error:
today_date = datetime.today().strftime('%Y-%m-%d')
yesterday = datetime.today()
end = pd.to_datetime(yesterday, format='%Y-%m-$d %H:%M:%S').normalize()
After a few people pointing out the obvious, we have it fixed! Either of these will work:
end = pd.to_datetime(yesterday, format='%Y-%m-%d %H:%M:%S.%f').normalize()
or
end = pd.to_datetime(yesterday).normalize()
For a uni project we have to create a program conforming to some tests that we have been given. The tests use magic numbers as inputs for the functions. I know how to get it to return a datetime object. Just dont know how to raise the error!
my code:
import datetime
def time_to_datetime(datetimeSTR):
datetime_obj = datetime.datetime.strptime(datetimeSTR, '%Y/%m/%d %H:%M')
if datetimeSTR != datetime.datetime.strptime(datetimeSTR, '%Y/%m/%d %H:%M'):
raise ValueError('The time must have the format YYYY/MM/DD HH:MM')
else:
return datetime_obj
Test Code:
import datetime
import os
import unittest # Standard unittest framework.
import utils # The module implementing JourneyOptions class.
class TestTimeToDatetime(unittest.TestCase):
"""Tests for the time_to_datetime function."""
def test_invalid_time_is_rejected(self):
with self.assertRaises(ValueError) as cm:
utils.time_to_datetime('2019/06/09 12:60')
self.assertEqual(
'The time must have the format YYYY/MM/DD HH:MM',
str(cm.exception))
def test_valid_time_yields_a_dattime_object(self):
d = utils.time_to_datetime('2019/06/09 12:59')
self.assertTrue(isinstance(d, datetime.datetime))
these are the results i get:
======================================================================
ERROR: test_valid_time_yields_a_dattime_object (__main__.TestTimeToDatetime)
Traceback (most recent call last):
File "C:/Users/s5115426/Desktop/tests/test_utils.py", line 21, in test_valid_time_yields_a_dattime_object
d = utils.time_to_datetime('2019/06/09 12:59')
File "C:\Users\s5115426\Desktop\tests\utils.py", line 25, in time_to_datetime
raise ValueError('The time must have the format YYYY/MM/DD HH:MM')
ValueError: The time must have the format YYYY/MM/DD HH:MM
======================================================================
FAIL: test_invalid_time_is_rejected (__main__.TestTimeToDatetime)
Traceback (most recent call last):
File "C:/Users/s5115426/Desktop/tests/test_utils.py", line 18, in test_invalid_time_is_rejected
str(cm.exception))
AssertionError: 'The time must have the format YYYY/MM/DD HH:MM' != 'unconverted data remains: 0'
- The time must have the format YYYY/MM/DD HH:MM
+ unconverted data remains: 0
Any help would be much appreciated!
The error that you are making is that strptime raises a value error if the date string does not match the given format. for example:
date_str = '2019/06/09 12:60'
datetime_obj = datetime.datetime.strptime(date_str, '%Y/%m/%d %H:%M')
.
.
ValueError: unconverted data remains: 0
datetime_obj never gets set because strptime never returns a value.
Also, as noted, you are comparing the response from strptime to the inout string -- that will never work.
Try a simpler approach:
import datetime
def time_to_datetime(datetimeSTR):
datetime_obj = datetime.datetime.strptime(datetimeSTR, '%Y/%m/%d %H:%M')
return datetime_obj
Or you can catch any exception and reraise (imo, bad practice, but might be worth doing to illustrate.
import datetime
def time_to_datetime(datetimeSTR):
try:
datetime_obj = datetime.datetime.strptime(datetimeSTR, '%Y/%m/%d %H:%M')
except ValueErr as err:
print(err)
raise ValueError('The time must have the format YYYY/MM/DD HH:MM')
else:
return datetime_obj
This way, you see how strptime behaves for various inputs in your test and you have error handling in your function .... though, as I indicated, reraising an error is (imo) bad practice.
I am trying to create a number of constraints for some other code based on twitter handle sets.
I am having issues with the following code because:
TypeError: can't compare datetime.datetime to str
It seems that even though I have changed Last_Post to a datetime object initially, when i compare it to datetime.datetime.today() it is converting to string. Yes, I have checked the to ensure that Last_post is converting properly. Im not really sure what is going on. Help?
for handle in handles:
try:
user = api.get_user(handle)
#print json.dumps(user, indent = 4)
verified = user["verified"]
name = user['name']
language = user['lang']
follower_count = user['followers_count']
try:
last_post = user['status']['created_at']
last_post = datetime.strptime(last_post, '%a %b %d %H:%M:%S +0000 %Y')
except:
last_post = "User has not posted ever"
location = user['location']
location_ch = location_check(location)
if location_ch is not "United States":
location_output.append(False)
else:
location_output.append(True)
new_sum.append(follower_count)
if language is not "en":
lang_output.append(False)
else:
lang_output.append(True)
if datetime.datetime.today() - datetime.timedelta(days=30) > last_post:
recency.append(False)
else:
recency.append(True)
I think you need to convert the twitter date to a timestamp:
import time
ts = time.strftime('%Y-%m-%d %H:%M:%S', time.strptime(tweet['created_at'],'%a %b %d %H:%M:%S +0000 %Y'))
I got this function below which helps me to change a date format from one to another:
def change_time_format(self, date_string, input_format, output_format):
if date_string == None:
return None
try:
date = datetime.datetime.strptime(date_string, input_format)
timestamp = calendar.timegm(date.timetuple())
new_formatted_date = datetime.datetime.fromtimestamp(timestamp).strftime(output_format)
return new_formatted_date
except Exception as E:
print E
return None
And when i'm calling it like this:
dtu.change_time_format('2017-01-01 12:34:56', '%Y-%m-%d %H:%M:%S', '%d%m%Y %H%M%S')
I'm getting the input as:'01012017 153456' which is wrong and needs to be: '01012017 123456', there is a difference of 3 hours for some reason.
I can't find how to fix it, I searched for several ways over the web and I couldn't find nothing.
From here:
return datetime.datetime.strptime(date_string, input_format).strftime(output_format)
I am writing a method in a Python module which tries to make live easier to the users. This method implements the creation of events in that calendar.
def update_event(start_datetime=None, end_datetime=None, description=None):
'''
Args:
start_date: string datetime in the format YYYY-MM-DD or in RFC 3339
end_date: string datetime in the format YYYY-MM-DD or in RFC 3339
description: string with description (\n are transforrmed into new lines)
'''
If the user specifies the start_date or the end_date a check up should be made in order to determine if the date is in YYYY-MM-DD format or in the datetime RFC 3339 format.
if (start_date is not None):
# Check whether we have an date or datetime value
# Whole day events are represented as YYYY-MM-DD
# Other events are represented as 2014-04-25T10:47:00.000-07:00
whole_day_event = False
try:
new_start_time = datetime.datetime.strptime(start_date,'YYYY-MM-DD')
# Here the event date is updated
try:
new_start_time = datetime.datetime.strptime(start_date,'%Y-%m-%dT%H:%M:%S%z')
#Here the event date is updated
except ValueError:
return (ErrorCodeWhatever)
except ValueError:
return (ErrorCodeWhatever)
Is this a good way of doing this? Can I check what kind of date I am receiving in a nicer way?
Thanks!
dateutil.parser.parse can be used to attempt to parse strings into datetime objects for you.
from dateutil.parser import parse
def update_event(start_datetime=None, end_datetime=None, description=None):
if start_datetime is not None:
new_start_time = parse(start_datetime)
return new_start_time
d = ['23/04/2014', '24-04-2013', '25th April 2014']
new = [update_event(i) for i in d]
for date in new:
print(date)
# 2014-04-23 00:00:00
# 2013-04-24 00:00:00
# 2014-04-25 00:00:00
Extending #Ffisegydd answer you can also specify your target datetime format that you want like this :-
from dateutil.parser import parse
def update_event(start_datetime=None, end_datetime=None, description=None):
if start_datetime is not None:
new_start_time = parse(start_datetime)
return new_start_time
d = ['06/07/2021 06:40:23.277000','06/07/2021 06:40','06/07/2021']
new = [update_event(i) for i in d]
for date in new:
print(date.strftime('%Y/%m/%d %H:%M:%S.%f'))