Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Python Dates and Times in Python Let's Build a Timed Quiz App Harder Time Machine

Python time_machine() challenge. Is there a better way?

The attached code works to pass the challenge, but it feels cobbled together.

I tried to find a way to pass the 'unit' variable directly through the timedelta function because the if/elif structure doesn't seem very DRY. But I can't find a way to convert the 'unit' string into a variable (ie: datetime.timedelta('minutes' = num) doesn't work).

Also, using the 'starter' variable in the return statement, even though it's outside the scope of the function, seems weird.

Like I said, it ran fine, but am I missing something? Thanks.

time_machine.py
import datetime

starter = datetime.datetime(2015, 10, 21, 16, 29)

# Remember, you can't set "years" on a timedelta!
# Consider a year to be 365 days.

## Example
# time_machine(5, "minutes") => datetime(2015, 10, 21, 16, 34)

def time_machine(num, unit):
  if unit == 'minutes':
    future = datetime.timedelta(minutes = num)
  elif unit == 'hours':
    future = datetime.timedelta(hours = num)
  elif unit == 'days':
    future = datetime.timedelta(days = num)
  elif unit == 'years':
    future = datetime.timedelta(days = num*365)
  return starter + future

Alex, thanks for asking this question. I was in the same boat where I wanted to find a more streamlined alternative to this challenge instead of my current clunky solution (which looks like your code) .

3 Answers

William Li
PLUS
William Li
Courses Plus Student 26,865 Points

I think your code is alright.

But since you are asking to DRY up code and the possibility of passing in unit directly to timedelta, does the following look better to you?

def time_machine(num, unit):
  if unit in ("minutes", "hours", "days"):
    keyword = {unit : num}
  elif unit == "years":
    keyword = {"days" : num * 365}      
  return starter + datetime.timedelta(**keyword)

Thank you yes, that's exactly what I meant.

I guess I didn't realize unpacking a dictionary passes the key as a variable, rather than a string. For example, if I create the dictionary, future = {'minutes' : 5}, to pass as a variable, this doesn't work:

warp = datetime.timedelta(future)

While this does:

warp = datetime.timedelta(**future)

This behavior is... unexpected. I suppose I don't understand unpacking as well as I hope to yet, but I'm happy to know there's a way to do this. Thanks again.

Kenneth Love
Kenneth Love
Treehouse Guest Teacher
future = {'minutes': 5}

warp = datetime.timedelta(**future)

warp = datetime.timedelta(minutes=5)

The two timedelta lines are exactly the same to Python. The ** turns the dict into keyword arguments.

I see. It makes more sense if I think about unpacking a dictionary as passing keyword=value directly. I couldn't find any way to output the unpacking without reverting the results back into strings so it was hard to tell what it was doing, exactly. I see how this could be a very powerful feature.

Thanks!

John Alexander Davidson
John Alexander Davidson
5,368 Points

Thanks for sharing. I'm trying to work out why your code passes the challenge and mine doesn't. We wrote quite similar pieces but I keep getting an '<variable> isn't referenced before assignment'. When I assign it I keep failing the challenge.

import datetime

starter = datetime.datetime(2015, 10, 21, 16, 29)

# Remember, you can't set "years" on a timedelta!
# Consider a year to be 365 days.

## Example
# time_machine(5, "minutes") => datetime(2015, 10, 21, 16, 34)
def time_machine(num, string):
  if string.lower == 'years':
    add = datetime.timedelta(days = 365*num)
  elif string.lower == 'days':
    add = datetime.timedelta(days = num)
  elif string.lower == 'hours':
    add = datetime.timedelta(hours = num)
  elif string.lower == 'minutes':
    add = datetime.timedelta(minutes = num)
  return starter + add
Kenneth Love
Kenneth Love
Treehouse Guest Teacher

Because the method is .lower(), not .lower (which would be an attribute).