Python dateutil date conversion - python

I'm trying to see if a list of dates are valid dates. I'm using the dateutil library, but I'm getting weird results. For example, when I try the following:
import dateutil.parser as parser
x = '10/84'
date = (parser.parse(x))
print(date.isoformat())
I get the result 1984-10-12T00:00:00 which is wrong. Does anyone know why this 12 gets added to the date?

The parse() method parses the string and updates a default datetime object, using the parsed information. If the default is not passed into this function, it uses first second of today.
This means that the 12 in your result, is today (when you're running the code), only the year and the month are updated from parsing the string.
If you need to parse the date string but you're not sure if it's a valid date value, then you may use a try ... except block to catch parse errors.
import dateutil.parser as parser
x = '10/84'
try:
date = (parser.parse(x))
print(date.isoformat())
except ValueError as err:
pass # handle the error

12 is the current date . dateutil takes components from current date/time to account for missing date or year in the date (it does not do this for the month, only date or year). Like another example would be a date like - Janauary 20 - this would get parsed as 2015/01/12 taking the 2015 year from the current datetime.
Sadly I have not yet found any options or such to stop this behavior.
I believe the best option for you would be to come up with a list of the valid datetime formats that you are expecting , and then manually try datetime.datetime.strptime on them , excepting ValueError . Example -
def isdate(dt, fmt):
try:
datetime.datetime.strptime(dt, fmt)
return True
except ValueError:
return False
validformats = [...]
dates =[...]
for x in dates:
if any(isdate(x,fmt) for fmt in validformats):
print(x, 'is valid date')

Related

Time value does not match the format. ValueError in python

I am trying to convert a string to datetime object using the strptime function.
I am encountering a ValueError that says format doesn't match, so I did double checking and confirmed that the format in the string matches the format I am passing as the parameter for strptime.
I have also referenced this question: time data does not match format but there the month and year were swapped.
So does this only work with the '%y-%m-%d %H:%M:%S' format or is it dynamic as per the user input like in my case '%y-%m-%d-%H:%M:%S' ?
input:-
from datetime import datetime
stg = "2022-10-31-01:17:46"
do = datetime.strptime(stg, '%y-%m-%d-%H:%M:%S')
output
ValueError: time data '2022-09-31-01:17:46' does not match format '%y-%m-%d-%H:%M:%S'
Expected output:
#while printing 'do'
2020-09-31-01:17:46
You're almost there. You need %Y instead of %y since you're providing the year with the century (2022 instead of 22).
Your code would be
from datetime import datetime
stg = "2022-10-31-01:17:46"
do = datetime.strptime(stg, '%Y-%m-%d-%H:%M:%S')

How to write a python function that returns the number of days between two dates

I am new to functions and I am trying to write a function that returns the number of days between two dates:
My attempt:
import datetime
from dateutil.parser import parse
def get_x_days_ago (date_from, current_date = None):
td = current_date - parse(date_from)
if current_date is None:
current_date = datetime.datetime.today()
else:
current_date = datetime.datetime.strptime(date_from, "%Y-%m-%d")
return td.days
print(get_x_days_ago(date_from="2021-04-10", current_date="2021-04-11"))
Expected outcome in days:
1
So there seem to be multiple issues, and as I said in the comments, a good idea would be to separate the parsing and the logic.
def get_x_days_ago(date_from, current_date = None):
if current_date is None:
current_date = datetime.datetime.today()
return (current_date - date_from).days
# Some other code, depending on where you are getting the dates from.
# Using the correct data types as the input to the get_x_days_ago (datetime.date in this case) will avoid
# polluting the actual logic with the parsing/formatting.
# If it's a web framework, convert to dates in the View, if it's CLI, convert in the CLI handling code
date_from = parse('April 11th 2020')
date_to = None # or parse('April 10th 2020')
days = get_x_days_ago(date_from, date_to)
print(days)
The error you get is from this line (as you should see in the traceback)
td = current_date - parse(date_from)
Since current_date="2021-04-11" (string), but date_from is parsed parse(date_from), you are trying to subtract date from the str.
P.S. If you have neither web nor cli, you can put this parsing code into def main, or any other point in code where you first get the initial strings representing the dates.
It looks like you're already aware that you can subtract a datetime from a datetime. I think, perhaps, you're really looking for this:
https://stackoverflow.com/a/23581184/2649560

How to provide default time when using datetime.strptime?

I'm using datetime.strptime to parse and obtain DateTime values from strings, in the form of %Y-%m-%dT%H:%M:%SZ but the data is dirty and sometimes doesn't have the time parameter, is sometimes received in yyyy/mm/dd format instead of yyyy-mm-dd format. I can think of hacky regex and try-catch ways to parse this and get what I need, but is there a clean way to use datetime.strptime and obtain the datetime in '%Y-%m-%dT%H:%M:%SZ' format with 00:00:00 or something as the default time if there is no time information?
Currently doing:
time = datetime.strptime(data['time'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=pytz.utc)
which throws an error if the data is in an unexpected format.
Just catch the ValueError and try again with an augmented value.
fmt = '%Y-%m-%dT%H:%M:%SZ'
try:
time = datetime.strptime(data['time'], fmt)
except ValueError:
time = datetime.strptime(data['time'] + "T00:00:00Z", fmt)
Alternatively, try the same string with a date-only format, since the resulting value will already default to 00:00:00.
date_and_time = '%Y-%m-%dT%H:%M:%SZ'
date_only = '%Y-%m-%d'
try:
time = datetime.strptime(data['time'], date_and_time)
except ValueError:
time = datetime.strptime(data['time'], date_only)
The second approach is a bit easier to adapt to multiple possible formats. Make a list, and iterate over them until one succeeds.
formats = ['%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%d', ...]
for fmt in formats:
try:
time = datetime.strptime(data['time'], fmt)
break
except ValueError:
pass
else:
# raise ValueError(f'{data["time"]} does not match any expected format')
time = datetime.now() # Or some other completely artificial value
If you're okay with third-party dependencies, you may also try the dateutil library:
import dateutil.parser
time = parser.isoparse(data['time']).replace(tzinfo=pytz.utc)
Or, if you want to have more control over the default values:
import dateutil.parser
time = parser.parse(data['time'], default=datetime.datetime(2019, 10, 14, 20, 14, 50), yearfirst=True).replace(tzinfo=pytz.utc)
Both of them allow more missing fields in the date string (like YYYY or YYYY-MM, etc.). See https://dateutil.readthedocs.io/en/stable/parser.html for more details.

How to convert date format and add hh:mm:ss to it

My issue is that user can input any date format, 12-feb-2015 or 12/10/2015, i need to convert this in below format :
12-feb-2015 00:00:00
this further would be fed in a MySQL query which would then be used to fetch data in given date ranges
so i have 2 questions :
is there any standard way to convert any input format to my required one?
how can i append hh:mm:ss to it?
i saw lot of methods on SO thread but none seem to help me out.
Normally SO isn't a code writing service but... :)
This is only a start to what you could do. I'm unaware of any way to have one test catch multiple formats. Instead I've always "gone through" the available formats. Since we're talking about two, here's something to kick-start your thinking:
from datetime import datetime
def parse_date(thedate):
result = None
#try each format
try:
result = datetime.strptime(thedate, "%d-%b-%Y")
except ValueError:
pass
except:
raise
# Let the last one "blow" up
if result is None:
result = datetime.strptime(thedate, "%d/%m/%Y")
print "{} parsed into {}".format(thedate,result.strftime("%d-%b-%Y %H:%M:%S"))
parse_date("12/10/2015") yields 12/10/2015 parsed into 12-Oct-2015 00:00:00
and
parse_date("12-feb-2015") yields 12-feb-2015 parsed into 12-Feb-2015 00:00:00
That should get you going. Check out the strptime/strftime formats here (scroll down to the strftime function).
The main problem with your approach is that any kind of format may be infinite kinds how much you can imagine.
Inspired in #al-g's answer, I propose an approach using a set of known data formats.
from datetime import datetime
def convert(dtm):
formats = ['%d/%m/%Y', '%d-%m-%Y', '%d/%m/%Y %H:%M:%S', '%d-%m-%Y %H:%M:%S']
for fmt in formats:
try:
return datetime.strptime(dtm, fmt).strftime('%d/%b/%Y %H:%M:%S')
except ValueError:
pass
except:
raise
print 'Format not recognized'
>>> convert('15072015')
Format not recognized
>>> convert('15-07-2015')
'15/Jul/2015 00:00:00'
>>> convert('15/07/2015')
'15/Jul/2015 00:00:00'
You can update the set of formats every time you find new one.

Difficulty with the replace method

I must have the user enter a date in mm/dd/yy format and then output the string in long-date format like January, ##, ####. I cannot for the life of me get the month to replace as a the word.
def main():
get_date=input('Input a date in mm/dd/yy format!\nIf you would like to enter a 1-digit number, enter a zero first, then the number\nDate:')
month= int(get_date[:2])
day=int(get_date[3:5])
year=int(get_date[6:])
validate(month, day, year)#validates input
get_month(get_date)
def validate(month,day,year):
while month>12 or month<1 or day>31 or day<1 or year!=15:
print("if you would like to enter a one-digit number, enter a zero first, then the number\n theres only 12 months in a year\n only up to 31 days in a month, and\n you must enter 15 as the year")
get_date=input('Input a date in mm/dd/yy format!:')
month= int(get_date[:2])
day=int(get_date[3:5])
year=int(get_date[6:])
def get_month(get_date):
if get_date.startswith('01'):
get_date.replace('01','January')
print(get_date)
I have tried a plethora of things to fix this but I cannot make January appear instead of 01.
Strings in Python are immutable, they don't change once they're created. That means any function that modifies it must return a new string. You need to capture that new value.
get_date = get_date.replace('01','January')
You can do this (and simplify the code) using python's date module.
The strptime function will parse a date from a string using format codes. If it's can't parse it correctly, it will raise a value error, so no need for your custom validation function
https://docs.python.org/2.7/library/datetime.html#datetime.datetime.strptime
The strftime function will print out that date formatted according to the same codes.
https://docs.python.org/2.7/library/datetime.html#datetime.datetime.strftime
Updated, your code would look something like this:
from datetime import datetime
parsed = None
while not parsed:
get_date=input('Input a date in mm/dd/yy format!\nIf you would like to enter a 1-digit number, enter a zero first, then the number\nDate:')
try:
parsed = datetime.strptime(get_date, '%m/%d/%y')
except ValueError:
parsed = None
print parsed.strftime('%B %d, %Y')
Why don't you use datetime module ?
year = 2007; month=11; day=3
import datetime
d = datetime.date(year, month, day)
print d.strftime("%d %B %Y")
You might be better off using Python's datetime module for this:
from datetime import datetime
entered_date = input('Input a date in mm/dd/yy format!\nIf you would like to enter a 1-digit number, enter a zero first, then the number\nDate:')
d = datetime.strptime(entered_date, '%m/%d/%y')
entered_date = d.strftime('%B, %d, %Y')
e.g.
'February, 29, 2016'
This way you catch invalid dates (such as 02/29/15) as well as badly-formatted ones.

Categories

Resources