Day 060 - Project 60 : Event countdown tracker

If you have any questions, comments or issues with this project please post them here!

Hi, I was able to do the challenge but unsure why when finding the difference, we don’t use the timedelta function?

1 Like

My solution worked, but was different from David’s. I didn’t use the .days parameter.

I asked ChatGPT about it, and learned something.
For reference, David’s code and my code.


DAVID’S SOLUTION:

import datetime

today = datetime.date.today()

print("EVENT COUNTDOWN")
day = int(input("Day: "))
month = int(input("Month: "))
year = int(input("Year: "))
event = datetime.date(year, month, day)

difference = event - today
difference = difference.days

if difference>0:
  print(f"{difference} days to go")
elif difference<0:
  print(f"😭😭😭😭😭😭😭 You missed it by {difference} days!")
  
else:
  print("🥳🥳🥳🥳🥳🥳🥳 Today!")

My solution:

import datetime

today = datetime.date.today() 

holiday = input("What's the event?\n> ").capitalize()
year = int(input("What's the year\n> "))
month = int(input("What's the month\n> "))
day = int(input("Input the day\n> "))

holidate = datetime.date(year, month, day)


difference = abs(holidate - today)

print(today)
print(holidate)
print(difference)

if holidate > today:
  print(f"You have {difference} days until {holiday}!")
elif holidate < today:
  print(f"It's been {difference} days since {holiday}!")
else:
  print(f"{holiday} is today!")

ChatGPT’s thoughts:

In Python, when you subtract two datetime.date objects, the result is a datetime.timedelta object, which represents the difference between two dates as a duration.

In David’s code, the line difference = event - today calculates the difference between event and today, resulting in a datetime.timedelta object. To extract the number of days from the timedelta object and convert it to an integer, the .days attribute is used: difference = difference.days.

On the other hand, in your code, you used abs(holidate - today) to calculate the difference between holidate and today. Here, the abs() function returns an absolute value, which results in a datetime.timedelta object. However, in your code, you didn’t extract the .days attribute, and that’s why your code worked without crashing.

The difference in behavior can be attributed to how you used the difference variable in your code. In David’s code, difference is used later in the conditional statements, which require an integer comparison. Therefore, the .days attribute is necessary to convert the timedelta object to an integer.

In your code, you printed the difference directly without any further calculations or comparisons, so you didn’t encounter any issues. However, if you were to use difference in integer comparisons or calculations later in your code, you might need to use .days as well.

To summarize, the .days attribute is used to extract the number of days from a timedelta object as an integer. It’s necessary if you need to perform integer comparisons or calculations with the result.

2 Likes

“holidate…” this is amazing lol

2 Likes

Sorry for the lengthy post. It branches off in many directions/ approaches from different angles . and What I thought were solutions still had a few small bugs to work out (hence the many edits) :person_facepalming::man_bowing::rofl: It should still be readable I hope.

:round_pushpin:If the user enters a number greater than 9999 (more than 4 digits) for the year it gives the error

ValueError : year xxxxx is out of range.

Assumingly you’d get a similar error for negative numbers . (see bottom of post)

I believe because the way datetime.date works it calls for strictly 1 - 4 digit positive numbers for year.

But theoretically, the user should be able to type in any year in the future no matter how many digits …right? :thinking:

:question::technologist:How could this^^ be worked around? :woman_technologist::question::memo::point_down:(if you wouldn’t mind)

In the meantime, since I don’t want to get the ValueError

I want to use len to ensure that the user can not enter more than 4 digits for year, but using len for year as an int gives the error:

TypeError: object of type 'int' has no len()

So I casted the int year as a new variable string so I could use len to ensure that it is not longer than 4 digits / characters

year = int(input(f"What year will {nameofevent} take place? "))
    ystring = str(year)
    if len(ystring) > 4:
      print("Enter 4 digits or less for year")
   continue

it also treats the user pressing enter without typing anything in as an invalid integer I guess through int(input())

year = None
today = datetime.date.today() # Today's date
nameofevent = input("What is the name of the event? ")
while True:
  try:
    time.sleep(1)
    os.system("clear")
    year = int(input(f"What year will {nameofevent} take place? "))
    ystring = str(year)
    if len(ystring) > 4:
      print("You must enter a 1 - 4 digit number for year.")
      continue
    break
  except ValueError:
    print("That's not a valid integer. Please try again.")
    continue


(then it’ll jump back to What year will (event) take place? after a pause and then clearing.
a ,(what year will a take place,) is just what i typed in for the event name.

There’s also errors for month being greater than 12 or less than 1 :

And days being greater than 31 or less than 1 (or the last day of the month as their max range):

I used a lazy method of just saying :

if day >31:
      print("You must enter a number between 1 and 31")
      continue
break

(this only checks if its greater than 31 and not if its less than 1)

I assume you’d have to set all the months to if day > 30 or if day >31 depending on how many days are in the month, and you’d also have to check if it was a leap year for february. Or get the appropriate limits(days) for each month and then just use that as the maximum. Or if you could find a way to get how many days in a month you could use a counter and a for loop.

In my code I managed the months by creating a new variable newmonth and corresponding it to the month’s numerical value(if user enters 1 for month : newmonth = “January” and so on for all 12 months and then used an else : continue to handle anything below 1 or above 12

if month == 1:
      newmonth = "January"
      break
elif month == 2:
      newmonth = "February"
      break
     ...
elif month == 12:
      newmonth = "December"
      break
else:
      print("Please enter a month(number) between 1 and 12 ")
      continue

This should handle some/ most of the errors that could occur from entering invalid characters or numbers beyond the range of time.date

I’m sure this is simply just not the focus of the lesson and that’s why its not checked for but it might lead to some confusion so I thought I’d post about it. I assume the solution is just meant to work if the user enters everything correctly and to be used as a guideline.

I can’t remember if we’ve used except ValueError yet either but we just learned about try and except and have encountered valueerrors so i think now wouldn’t be a bad time to use it or learn it or re-use it.
So heres a brief AI generated description in reference to MY solution.
:point_down:

except ValueError is used to catch and handle specific exceptions of type ValueError . So, if the code inside the try block raises a ValueError , the code inside the corresponding except block will be executed to handle that specific error. It helps prevent YOUR (my) program from crashing when encountering unexpected input that triggers a ValueError .

Here’s the link to my version for reference :
My Day 60 Repl

If you notice anything wrong/ could be done in another way / done better, please let me know.

Another method of checking if :

(if) year is > 4 digits by taking it as a string and appending it to a list and using if len(list) greater than 4 then exit()

import datetime
today = datetime.date.today()
print("EVENT COUNTDOWN")
day = int(input("Day: "))
month = int(input("Month: "))
yearlist = []
year = input("Year: ")    # take it as a string and convert to int later?
for digit in year:
  yearlist.append(digit)
year = int(year)        # <----------- ^^
if len(yearlist) > 4:
  print("not possible to calculate difference for year greater than 9999")
  exit()
event = datetime.date(year, month, day)
difference = event - today
difference = difference.days
if difference>0:
  print(f"{difference} days to go")
elif difference<0:
  print(f"😭😭😭😭😭😭😭 You missed it by {difference} days!")

else:
  print("🥳🥳🥳🥳🥳🥳🥳 Today!")

And probably the simplest method would be just …

checking if year is greater than > 9999

I’ve modified the below code(from the above screenshot) to remove the leftover code from the last method.

import datetime
today = datetime.date.today()
print("EVENT COUNTDOWN")
day = int(input("Day: "))
month = int(input("Month: "))
year = int(input("Year: "))   
if year > 9999:
  print("Not possible to use 5 digit number for year")
  exit()   
event = datetime.date(year, month, day)
difference = event - today
difference = difference.days
if difference>0:
  print(f"{difference} days to go")
elif difference<0:
  print(f"😭😭😭😭😭😭😭 You missed it by {difference} days!")

else:
  print("🥳🥳🥳🥳🥳🥳🥳 Today!")

I believe all of the above solutions would still give a value error if the user enters any negative number

So I think we could take the input(for year, month day) as their absolute values, which would then run it by the greater than maxes(if day > 31) or just check if they’re less than 1 (if day <1 ) , but I guess we should make an exception to negative values for year because we could treat negative years as bc .

abs(int( works to get a negative number through my year input
but if the user wanted to know how many days had passed since a “negative” year (bc) then it would translate it into its absolute value, (a positive) so the calculation would be wrong … instead of how many days from - 50 to now it would be how many days from (positive) 50 until now (a miscalculation of 100 years):rage::grin::laughing:
:question:How do you use negative values for years (for bc) with datetime :question:

a = user year
b = now year

a is a negative number

if abs(a) is greater than b
(if abs(a) > b:)
example :
a = - 2500
we’ll just pretend it’s now the year 2000 to make it easier than 2023.
#b = 2000 #so the answer we’d be looking for is (a) is 4500 years ago.
(Where if we just left a positive (to get through date.time) when we calculate the difference between a and b wed get a is 500 years from now(in the future) )

#converts (a)negative2500 to positive 2500 and checks if it is greater than b(2000)
(if a2500 > b2000:)
difference = a + b? 4500?

now lets make an example where positive a is less than b.

a = - 1500
b = 2000
a = abs(a) convert a to positive
a = 1500

(el)if a < b
(if 1500 is less than 2000:)
difference = a +b (3500)
(if it was now left with the subtraction method where it subtracts the smaller number from the larger one it would say a was 500 years ago)

So when using negative values for years(as bc) instead of subtracting the smaller number from the larger
a - b or b - a

we just add them together whether a is greater than b or less than b . .
But then theres still the problem of actually getting the months and days.
This still would not handle if the user entered a negative number(-a) that when converted to positive is equal to b so if its 2023, and the user enters, - 2023 since it’s not less than nor greater than it would calculate it as (a - b2023-2023 ) 0 difference when in reality it would still be a + b (2023 +2023) 4046 year difference between negative 2023 and positive 2023.

We don’t have to worry about b being a negative number thankfully.

it sounds kind of complicated but its really quite simple when you look at it like this :

a = int(input("Input a negative number please "))
b = 2023
posa = abs(a)
print(a , "was", posa + b, "years ago.")

Or without specifically asking for a negative number( while still checking for it and calculating it properly)

a = int(input("Input any whole number please "))
b = 2023
if a < 0:  #check if a is a negative number
  posa = abs(a)
  print(a , "was", posa + b, "years ago.")
else:  #<--------------------- if a is not a negative number
  if a > b:
    print(a, "is", (a - b), "years in the future")

  elif a < b:
    print(a, "was", (b - a), "years ago")
    
  else:
    print("Same year")

(the smallest yellow text reads "remember whenever A is a negative number, we convert it to itself as a positive number.
(so if A was -5 (negative 5) it becomes 5 (positive 5)
We simply use abs(a) to get its positive form… (absolute value)

if a <0:
  a = abs(a)

or you can create a new variable

if a<0 :
   newa = abs(a)
or
if a <0:
   posa = abs(a)

(the image is wrong)
(it is meant to say posa = abs(a) not posa - abs(a)
sorry :slightly_frowning_face: )

(We’ll use newa)

Regardless of all of this the issue still remains, I have no way to feed a negative number to datetime.date for year to get an accurate calculation of time passed , or to feed it a positive number greater than 9999. We’ve forced the user to enter a number less than 9999, and we’ve calculated the negative number as a positive number but this is not a true solution to the problem.

And yes I like popping bubble wrap :rofl: if you were wondering.
And thank you for all the lessons. Everyone @ Replit and On Replit and on forums. I really appreciate the help and the lessons.

2 Likes