2

I made a countdown function (which works just fine) that returns the time remaining as a string. I'm using the strptime and strftime functions to sort of parse the data so I could have a variable for how many days, hours, minutes and seconds that are remaining. My code gives me an error as soon as the days exceed 31 (because of the month). Any ideas how I can fix it?

from datetime import datetime, date

a = "31 days, 23:52:51"
b = "32 days, 23:52:51"

d = datetime.strptime(b, "%d days, %H:%M:%S")

t1 = d.strftime("%d")
t2 = d.strftime("%H")
t3 = d.strftime("%M")
t4 = d.strftime("%S")


print(t1)
print(t2)
print(t3)
print(t4)
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
  • 3
    The thing is, you actually want to have a [`timedelta`](https://docs.python.org/3/library/datetime.html#timedelta-objects) object, because `datetime`s are, well, dates... and there is no date with more than 31 days. – MB-F Aug 11 '21 at 09:48
  • @MB-F I actually did try doing it with timedelta objects but apparently, I had to make it a string in order to use strptime so it was no use. – Daniel Frost Aug 11 '21 at 09:49
  • 1
    strptime and strftime are only useful for parsing dates, but they should not be used here because, as MB-F mentioned, you are dealing with a timedelta here. I think the only way to do this is to parse the strings yourself. There are several options in this [thread](https://stackoverflow.com/questions/8906926/formatting-timedelta-objects) – Alfred Rodenboog Aug 11 '21 at 09:52
  • @AlfredRodenboog Noted! I'll check it out. Thanks! – Daniel Frost Aug 11 '21 at 09:53
  • @DanielFrost You can ignore string "31 days" from parsing if it's an int not a Day. `%d` actually looks for calendar date. Or maybe you can run regex on string and take out parts as needed so you won't need date parsing at all. – Nick Aug 11 '21 at 09:53

4 Answers4

2

No month has 32 days, hence your error. For time differences however, just use timedelta. You can freely add a timedelta to a datetime object.

import re
from datetime import datetime, timedelta

now = datetime.now()

r = "^(\d+) days?, (\d+):(\d+):(\d+)$"

cases = ["1 day, 11:12:13", "31 days, 23:52:51", "32 days, 23:52:51"]

for s in cases:
    m = re.match(r, s)
    days, hours, minutes, seconds = [int(x) for x in m.groups()]
    td = timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)
    print(td)  # gives you the intended output (use str(td) if you want the string)
    # the code below duplicates that print manually 
    days2 = td.days
    hours2, seconds2 = divmod(td.seconds, 3600)
    minutes2, seconds2 = divmod(seconds2, 60)
    print(f'{days2} day{"s" if 1 < days2 else ""}, {hours2}:{minutes2:0>2}:{seconds2:0>2}')
    # and finally the print of the datetime object made of current time plus our time delta
    print(now+td)

Output (will change based on your clock):

1 day, 11:12:13
1 day, 11:12:13
2021-08-12 23:23:33.986973
31 days, 23:52:51
31 days, 23:52:51
2021-09-12 12:04:11.986973
32 days, 23:52:51
32 days, 23:52:51
2021-09-13 12:04:11.986973
SzieberthAdam
  • 3,999
  • 2
  • 23
  • 31
1

If x in x days is not a day in the month, but just a number of days, you can't pass it to strptime and have to parse it separately.

For example, split the string on the first , and pass the only the second half to strptime:

num_days, hours = b.split(',', maxsplit=1)

d = datetime.strptime(hours.strip(), "%H:%M:%S")

t1 = int(num_days.split()[0])
t2 = d.strftime("%H")
t3 = d.strftime("%M")
t4 = d.strftime("%S")

Also, in case you want to work with t2, t3, t4 as numbers, you should rather use

t2 = d.hour
t3 = d.minute
t4 = d.second
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
1

I think you can use timedelta class and store in it your date. Then you can extract from it days and seconds beyond the day. From this seconds you can have hour and minutes and seconds with modulo division.

from datetime import datetime, date, timedelta

a = "31 days, 23:52:51"
b = "32 days, 23:52:51"


d = timedelta(days=float(b[0:2]), hours=float(b[9:11]), minutes=float(b[12:14]), 
seconds=float(b[15:]) )

print(d.days)
print(d.seconds // 3600)
print(d.seconds % 3600 //60)
print(d.seconds % 60)
0

Thanks to everyone who answered I parsed the days myself in the silliest way imaginable! So I simply went like this:

index = b.find("days")
d = datetime.strptime(b[index:], "days, %H:%M:%S")
days = int(b[0:index - 1])

And in case it would be 'day' and not 'days' I added an if/else block just to make sure. Thanks everyone!