Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Dates and Times in Python (2014) 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,868 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).