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 Python Dates and Times The Birthday App Challenge yourself

Roderick Hung
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Roderick Hung
Front End Web Development Techdegree Graduate 23,181 Points

How to sort birthdays in chronological order in challenge 3?

How can I print upcoming birthdays in chronological order.

 for person in people_list:

        birthday_dt = date_str_to_obj(person)
        now = datetime.datetime.now()
        birthday_this_year = birthday_dt.replace(year=now.year)
        birthday_next_year = birthday_dt.replace(year=now.year+1)

        difference = birthday_this_year - now

        turning_age = relativedelta.relativedelta(now, birthday_dt).years +1


        if 0 < ((birthday_this_year-now).days) <= days or 0 < ((birthday_next_year-now).days) <= days:
            print(F"{person['name']} turns {turning_age} in  {difference.days} days on {birthday_dt.strftime('%B %d')}")

output
======== UPCOMING BIRTHDAYS ========
Marcelle turns 20 in  59 days on March 10
Arby turns 34 in  85 days on April 05
Fred turns 34 in  31 days on February 11
Jane turns 40 in  19 days on January 30
====================================

How can I make the output print:

======== UPCOMING BIRTHDAYS ========
Jane turns 40 in  19 days on January 30
Fred turns 34 in  31 days on February 11
Marcelle turns 20 in  59 days on March 10
Arby turns 34 in  85 days on April 05
====================================

3 Answers

Roderick Hung
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Roderick Hung
Front End Web Development Techdegree Graduate 23,181 Points

I found that the code above has a bug if I changed the "now" variable to datetime.datetime(year=2023, month=11, day=1). The below code is refactored and with documentation.

def upcoming_birthdays(people_list, days):
    # TODO: write code that finds all upcoming birthdays in the next 90 days and in chronological order
    # 90 is passed in as a parameter from menus.py
    # Template:
    # PERSON turns AGE in X days on MONTH DAY
    # PERSON turns AGE in X days on MONTH DAY

    birthday_list = []

    #paramaters are passed into the sort_birthday_list() that first creates an object for each person and appends
    #to birthday_list. Using sort() on birthday_list to chronologically order each person object by their birthday.
    def sort_birthday_list(person, turning_age, difference, birthday):
        birthday_list.append({
                    'name': person['name'], 
                    'turning_age': turning_age, 
                    'difference': difference,
                    'birthday': birthday
                  })
        birthday_list.sort(key=lambda item:item['birthday'])

    #Looping through the people_list that was passed into upcoming_birthday() function and converting
    #each string in person to a datetime object.
    #converting each datetime object to the current year in "birthday_this_year" and also to next year in 
    #"birthday_next_year". The if elif statement below checks if the difference between "birthday_this_year"
    #and "birthday_next_year is within 90 days or not". If the birth date is within 90 days, each person's parameters 
    #are passed into the sort_birthday_list().
    for person in people_list:

        birthday_dt = date_str_to_obj(person)
        now = datetime.datetime.now()
        birthday_this_year = birthday_dt.replace(year=now.year)
        birthday_next_year = birthday_dt.replace(year=now.year+1)

        difference_this_year = birthday_this_year - now
        difference_next_year = birthday_next_year - now

        turning_age = relativedelta.relativedelta(now, birthday_dt).years +1


        if 0 < ((difference_this_year).days) <= days: 
            sort_birthday_list(person, turning_age, difference_this_year.days, birthday_this_year)
        elif 0 < ((difference_next_year).days) <= days:
            sort_birthday_list(person, turning_age, difference_next_year.days, birthday_next_year)

    #loops through each person in birthday_list that has been chronologially ordered by birthday and within 90 days
    #to print out person's name, the age they are turning, how many days until their birthday, and the month and day of birthday.
    for person in birthday_list:
        print(f"{person['name']} turns {person['turning_age']} in {person['difference']} days on {person['birthday'].strftime("%B %d")}")

#output
# ======== UPCOMING BIRTHDAYS ========
# Nova turns 24 in 27 days on November 28
# Declan turns 30 in 59 days on December 30
# Jane turns 40 in 90 days on January 30
# ==================================== 
Steven Parker
Steven Parker
229,771 Points

Roderick Hung — Normally, "best answer" would be the the one that contained the info that helped resolve your issue! :see_no_evil:
(you can still change that now)

In any case, glad I could help. And happy coding!

Steven Parker
Steven Parker
229,771 Points

After converting the strings to datetime objects, you can perform normal mathematical comparisons on the datetime‍s.
Earlier dates will be considered to be "less than" later dates, making it easy to sort them.

Steven Parker
Steven Parker
229,771 Points

Ordinary comparison operators can be used on the datetime variables, such as:

if birthday1_dt < birthday2_dt:    # this will be true if birthday1 comes first

You can create a custom comparison function to use with sort() or sorted() to put the list in the desired order. Check the official online documentation for more details.

Roderick Hung
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Roderick Hung
Front End Web Development Techdegree Graduate 23,181 Points

Thanks Steven Parker . I used the sort method. Below is the code that will print all upcoming birthdays within 90 days in chronological order. Let me know if you think the code can be refactored further. Thanks.

def upcoming_birthdays(people_list, days):
    # TODO: write code that finds all upcoming birthdays in the next 90 days
    # 90 is passed in as a parameter from menus.py
    # Template:
    # PERSON turns AGE in X days on MONTH DAY
    # PERSON turns AGE in X days on MONTH DAY

    birthday_list = []

    def sort_birthday_list(person, turning_age, difference, birthday_str_num, birthday_str_month_day):
        birthday_list.append({
                    'name': person['name'], 
                    'turning_age': turning_age, 
                    'difference': difference, 
                    'birthday_str': birthday_str_num,
                    'birthday_str_month_day': birthday_str_month_day
                  })
        birthday_list.sort(key=lambda item:item['birthday_str'])

    for person in people_list:

        birthday_dt = date_str_to_obj(person)
        now = datetime.datetime.now()
        birthday_this_year = birthday_dt.replace(year=now.year)
        birthday_next_year = birthday_dt.replace(year=now.year+1)

        difference = birthday_this_year - now

        turning_age = relativedelta.relativedelta(now, birthday_dt).years +1

        if 0 < ((birthday_this_year-now).days) <= days or 0 < ((birthday_next_year-now).days) <= days:
             sort_birthday_list(person, turning_age, difference.days, birthday_dt.strftime("%m %d"), birthday_dt.strftime('%B %d'))

    for person in birthday_list:
        print(f"{person['name']} turns {person['turning_age']} in {person['difference']} days on {person['birthday_str_month_day']}")


output
======== UPCOMING BIRTHDAYS ========
Jane turns 40 in 16 days on January 30
Fred turns 34 in 28 days on February 11
Marcelle turns 20 in 56 days on March 10
Arby turns 34 in 82 days on April 05
====================================