Python strptime errors on parsing strftime output - python

I am trying to store a date in a human readable format. For that I save and read back a string containing a date. I am using date.min to denote a date before any other.
from datetime import datetime, date
d = date.min
s = datetime.strftime(d, "%Y-%m-%d")
print(s)
# 1-01-01
d2 = datetime.strptime(s, "%Y-%m-%d")
# ValueError: time data '1-01-01' does not match format '%Y-%m-%d'
However, when I try to parse the date using strptime that was output by strftime, I only get an error. It seems that strptime is expecting leading zeros like 0001, which strftime is not outputting.
It might be possible to use None. Are there any other ways to work around what seems like a bug to me?

You need to add leading zeros:
try replacing:
s = {datetime.strftime(d, "%Y-%m-%d")}
with:
s = f'{d.year:04d}-{datetime.strftime(d, "%m-%d")}'
If you want to work with dates easily, I can really suggest the 'Arrow' library.
https://pypi.org/project/arrow/

Python 3.9 on Linux exhibits the problem as expected in the many comments on the question. One workaround that should work on all platforms is to use ISO format for the date string with date.isoformat():
>>> from datetime import date, datetime
>>> s = date.min.isoformat()
>>> s
'0001-01-01'
>>> d = datetime.strptime(s, "%Y-%m-%d")
>>> d
datetime.datetime(1, 1, 1, 0, 0)
>>> assert d.date() == date.min
You can also use date.fromisoformat() instead of strptime():
>>> date.fromisoformat(date.min.isoformat())
datetime.date(1, 1, 1)

The strftime can't add leading zeros to the year. It calls the underlying C function and its behavior on adding leading zeros to the year is platform specific. You can work around this by formatting the date object by yourself. Just do a check if d.year is less than 1000 and add how many leading zeros needed:
d = date.min
year_str = ''.join(['0' for _ in range(4 - len(str(d.year)))]) + str(d.year)
s = '{year}-{md}'.format(year=year_str, md=datetime.strftime(d, "%m-%d"))
d2 = datetime.strptime(s, "%Y-%m-%d")
# now d2 is equal to datetime.datetime(1, 1, 1, 0, 0)

Related

how to modify day which is inside a date string

i have seen some threats about this already but im not quite sure how to do mine. i have a string date (DD/MM/YY) then i need to subtract the day by 2 and i need to change it to (MM/DD/YY). For example, if i have a string of 01/04/2021, i need the final output to be 03/30/2021. i have tried using datetime.date but it seems like i cannot put a string of 01/04/2021 in it. Any helps? Here is what i got so far, but it doesn't really work i don't quite understand datetime library so its a little bit confusing, sorry in advance.
import datetime as dt
from dateutil.relativedelta import relativedelta
date_1 = '01/04/2021'
date_2 = list(date_1)
date_2[0:2], date_2[3:5] = date_2[3:5], date_2[0:2]
date_2 = ''.join(date_2) # change date_1 to MM/DD/YY
print(dt.date(int(date_2[0:4]),int(date_2[5:7]), int(date_2[8:10])) - relativedelta(days=2)) # i tried to minus the day out, but the code fails,
#Here is the error, in case it helps, ValueError: invalid literal for int() with base 10: '04/0'
You can use timedelta, strptime and strftime.
from datetime import datetime
from datetime import timedelta
d = '01/04/2021'
print((datetime.strptime(d, '%d/%m/%Y') - timedelta(days=2)).strftime('%m/%d/%y'))
#03/30/21
Explanation
datetime.strptime: This function allows you to take a string, provide the formatting and return a datetime object.
datetime.strptime(d, '%d/%m/%Y')
datetime.datetime(2021, 4, 1, 0, 0)
datetime.timedelta: Allows us to add and subtract time on a datetime object.
datetime.strptime(d, '%d/%m/%Y') - timedelta(days=2)
datetime.datetime(2021, 3, 30, 0, 0)
datetime.strftime: Allows us to format our datetime object as a string in the format we specify
datetime(2021, 3, 30, 0, 0).strftime('%m/%d/%y')
'03/30/21'
Joining these all together, we can convert your string to a date, make the change to the date that we want, and then convert it back to a string.

Why datetime isocalendar transition back will shift 1 week?

I start datetime.now() (today is 6/11) and transition to week day:
>>> now=datetime.now().isocalendar()
>>> now
(2019, 24, 2)
but when I transition back, I found it shift 1 week:
>>> res = datetime.strptime(now[0]+'_'+now[1]+'_'+now[2], "%Y_%W_%w")
>>> res
datetime.datetime(2019, 6, 18, 0, 0)
can someone enplane it?
very thanks!
from datetime import daytime
iso_string = str(now[0]).zfill(4) + '_' + str(now[1]).zfill(2) + '_' + str(now[2])
res = datetime.strptime(iso_string, "%G_%V_%u").date()
strptime has special directives for ISO week numbers: "%G_%V_%u"
From trying out, it seems like leading zeros don't really matter, but the official standard says they do, so be sure to add them with zfill().
This only works for python>=3.6, previous versions of strptime seem to be incompatible with ISO weeks.
For earlier versions of python, I think it is best practice to use the isoweek module:
from isoweek import Week
res = Week(now[0], now[1]).day(now[2]-1)

How to parse time string without date and date string without time?

Is there any way to automatically parse strings with time only to datetime.time object (or something similar)? Same for datetime.date.
I've tried dateutil, arrow, moment, pandas.to_datetime.
All these parsers create timestamps with a current date.
>>> from dateutil.parser import parse
>>> parse('23:53')
datetime.datetime(2019, 1, 8, 23, 53) # datetime.time(23, 53) expected
>>> parse('2018-01-04')
datetime.datetime(2018, 1, 4, 0, 0) # datetime.date(2018, 1, 4) expected
UPD:
Thanks for the responses. Think that I should clarify the problem.
The program doesn't know what will be in the input (timestamp, date or time), and it should decide to set appropriate type. The problem is to distinguish these types.
For example, I can parse 23:53 and get a timestamp. How can I decide to extract the time from it or not?
You can use fromisoformat() from datetime.
import datetime
datetime.time.fromisoformat('23:53')
datetime.date.fromisoformat('2018-01-04')
What you basically want is for '23:53' to become a datetime.time object and for '2018-01-04' to become a datetime.date object. This cannot be achieved by using dateutil.parser.parse():
Returns a datetime.datetime object or, if the fuzzy_with_tokens option is True, returns a tuple, the first element being a datetime.datetime object, the second a tuple containing the fuzzy tokens.
From the documentation. So you'll always get a datetime.datetime object when using dateutil.parser.parse()
I would guess you need to interpret the input string yourself to define wether you're trying to parse a time or a date. When you do that, you can still use the dateutil.parser.parse() function to get the object you want:
from dateutil.parser import parse
my_time = parse('23:53')
my_time.time() # datetime.time(23, 53)
my_time.date() # datetime.date(2019, 1, 8)
Here you have an example. Just set the date attributes with replace, and select the output with strftime.
import datetime
date = datetime.datetime.now()
newdate = date.replace(hour=11, minute=59)
print(newdate.strftime('%H:%M'))
newdate2 = date.replace(year=2014, month=1, day=3)
print(newdate2.strftime('%Y-%m-%d'))
You can use either time or datetime modules, but one thing to bear in mind, is that these always create an object, that specifies a moment in time. (Also, if parsing strings, consider using the strptime function and displaying as string, strftime function respectively)
e.g.
>>> hours = time.strptime("23:59", "%H:%M")
>>> days = time.strptime("2018-01-04", "%Y-%m-%d")
>>> time.strftime("%H:%M", hours)
'23:59'
>>> time.strftime("%H:%M %Y", hours)
'23:59 1900'
Not recommended, but if you wish to separate these two object for some reason and wish to only care for a specific portion of your assignement, you can still adress the respective numbers with
>>> hours.tm_hour
23
>>> hours.tm_min
59
>>> days.tm_mon
1
>>> days.tm_mday
4
>>> days.tm_year
2018
A far better approach, in my opinion would be formatting the complete date string and using the strptime to form a complete timestamp - even if you get the time and date as separate inputs:
>>> ttime = "22:45"
>>> dday = "2018-01-04"
You can use the % formatter, or the "new" python f-Strings
>>> complete_t_string = "{} {}".format(dday, ttime)
>>> complete_t_string
'2018-01-04 22:45'
Now that we have a complete string, we can specify how it should be read and create a complete timestamp:
>>> complete_time = time.strptime(complete_t_string, "%Y-%m-%d %H:%M")
>>> complete_time
time.struct_time(tm_year=2018, tm_mon=1, tm_mday=4, tm_hour=22, tm_min=45, tm_sec=0, tm_wday=3, tm_yday=4, tm_isdst=-1)
EDIT:
Somebody will probably kill me, but if you absolutely know that you will only get two types of values, you could just do a simple try / except construct. It can probably be written more Pythonically:
try:
time.strptime(t_string, "%H:%M")
except ValueError:
time.strptime(t_string, "%Y-%m-%d")

datetime.strptime formatting

My datetime in my CSV file is like the following:
2011/1/1 0:00
2011/1/1 0:30
2011/1/1 1:00
when I run:
date = datetime.strptime(row[0], '%Y/%m/%d %H:%M')
I get datetime output as:
[datetime.datetime(2011, 1, 1, 0, 0)]
[datetime.datetime(2011, 1, 1, 0, 30)]
How can i format it to the original datetime?
You have already parsed a string into a datetime object. This is done by using datetime.datetime.strptime(). To format the object back into a string you can use the same syntax but using method datetime.datetime.strftime(), e.g.:
date.strftime('%Y/%m/%d %H:%M')
See also documentation.
If you want exactly your input string (without leading 0), you can put a hyphen between percentage operator and directive character where necessary, e.g.:
date.strftime('%Y/%-m/%-d %-H:%M')
This is well explained in: Python strftime - date without leading 0 but it is platform dependent.
Try printing date in string format:
from datetime import datetime
row = "2011/1/1 0:30"
date = datetime.strptime(row, '%Y/%m/%d %H:%M')
print str(date)
output:
'2011-01-01 00:30:00'
What you are currently doing is creating a datetime object from a string and formatter as shown here. Likely somewhere in your code you put this object in a list and referenced it. Python doesn't know that you want to print the container(the list) with it in a certain string format.
If I'm understanding your question you want to print/return the element and not the container. Shown below:
import datetime
l = []
today = datetime.date.today()
l.append(today)
#what you have
print(l)
#addressing just the first element
print(l[0])

String to time stamp conversion

I have a data file with about 5.6million time-stamps in the format "2016-10-17 15:00:40.739". They are all strings at the moment for some reason and I need to convert them all to date times as I will later need to calculate the difference between groups of them (e.g: stamp1 -> stamp2 = 2hours, 4minutes etc).
I found another question "Converting string into datetime" but mine are in a different format and I cannot get that answer to work for me.
Any help is much appreciated.
Use numpy's datetime64:
>>> np.datetime64('2016-10-17 15:00:40.739')
numpy.datetime64('2016-10-17T15:00:40.739')
You can easily find differences by simply subtracting, or using numpy's timedelta64:
>>> np.datetime64('2016-10-17 15:00:40.739') - np.datetime64('2016-10-15 15:00:40.739')
numpy.timedelta64(172800000,'ms')
>>> np.datetime64('2016-10-17 15:00:40.739') + np.timedelta64(1,'D')
numpy.datetime64('2016-10-18T15:00:40.739')
Try this:
from datetime import datetime
a = "2016-10-17 15:00:40.739"
b = datetime.strptime(a,'%Y-%m-%d %H:%M:%S.%f')
print(b)
>>> datetime.datetime(2016, 10, 17, 15, 0, 40, 739000)
To define the format of your dates. Follow this guide: https://www.tutorialspoint.com/python/time_strptime.htm
You can use the dateutil module to convert the string date to datetime object.
from dateutil import parser
dt = parser.parse("2016-10-17 15:00:40.739")
print dt
print type(dt)
Output:
2016-10-17 15:00:40.739000
<type 'datetime.datetime'>

Categories