-1

Context

I am trying to figure out if today is somebody's birthday. With the birthdate as the parameter.

1 Approach

Checking if the days since the birthdate are divisible by 365 without a rest. Obviously this would fail because of years with 366 days.

2 Approach

Comparing the day & month attributes of today & the birthday. This approach won't work for people born on the 29th of February. Basically, the problem is again the leap year.

Does anybody know an elegant & simple way to determine if the current date is a birthday?

Community
  • 1
  • 1
Goga
  • 349
  • 1
  • 16
  • Is it an assignment or something you genuinely want to do ? If it's the latter, you should use [data structures designed for time](https://stackoverflow.com/questions/39717964/how-to-get-todays-date-less-18-years) – Arthur Attout Dec 30 '19 at 21:20
  • I need it for a personal project. So yeah, pure passion :) – Goga Dec 30 '19 at 21:22

2 Answers2

1

If your birthday is Feb 29, what logic do intend to use to answer the question "is today your birthday on a non leap year?". That's a non obvious thing to answer. The best I can come up is to consider Feb 28 your birthday, unless this year is a leap year, in which case Feb 29 is your birthday.

With that you can simply ask for how many days are in February this year and then do some simple logic to pretend the birthday is actually the 28th in a non leap year.

// Psuedo code
var bday = someUser.bday
if (bday.month == 2 && bday.date == 29 && daysInFebruraryThisYear == 28) {
  bday.date = 28
}

// Is it today?
return bday.month == today.month && bday.date == today.date
Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
  • If my birthday is the 29th, wouldn't I celebrate on the 1st of March on non leap years? – Goga Dec 30 '19 at 21:46
  • The real answer is, apparently, it depends. ["In non-leap years, some leaplings celebrate their birthday on either February 28 or March 1, while others only observe birthdays on the authentic intercalary date, February 29."](https://en.wikipedia.org/wiki/February_29#Born_on_February_29) – Alex Wayne Dec 30 '19 at 21:49
  • 1
    Oh dear lord. I will just settle with 1st of March & hope I won't get bad App Store reviews – Goga Dec 30 '19 at 21:52
  • Luckily it's only 1 in 1461 people, or 0.068%. `1/(365*4+1)`. So you can't , mathematically, upset that many users :) – Alex Wayne Dec 30 '19 at 21:57
  • A nice little easter egg would be to ask the user when they celebrate. Of course only when they input the 29th in the first place. That would be cute haha. Thank you for your help! Cheers! – Goga Dec 30 '19 at 22:01
1

Another solution is to convert the month/day date to the current calendar and compare from there:

let actualBirthDate = Date(year: 2000, month: 2, day: 29)
// Created 'currentDate' as a non-leap year for testing, 
// but you would want to use 'Date()' here for your app
let currentDate = Date(year: 2001, month: 3, day: 1) 

var birthDateComponents = Calendar.current.dateComponents([.year, .month, .day], from: actualBirthDate)
let currentDateComponents = Calendar.current.dateComponents([.year, .month, .day], from: currentDate)

// Plot the birthdate on this year's calendar.
// Feb.29 will convert to Mar.1 on a non-leap year.
birthDateComponents.year = currentDateComponents.year

if let birthday = Calendar.current.date(from: birthDateComponents) {

    var birthdayComponents = Calendar.current.dateComponents([.year, .month, .day], from: birthday)

    // Now if day and month match, we know it's your birthday
    if birthdayComponents.month == currentDateComponents.month && birthdayComponents.day == currentDateComponents.day {
        print("Happy birthday!")
    }
}

Modify currentDate to try out different years (or just set that to Date() when you're done). February 29 will convert to March 1 on non-leap years, but remain the same on leap years.

If you're wondering about that Date initializer I used, I just have a Date extension that looks like this:

extension Date {

    init(year: Int? = nil, month: Int? = nil, day: Int? = nil, hour: Int? = nil, minute: Int? = nil, second: Int? = nil, millisecond: Int? = nil, timeZone: TimeZone? = nil) {

        let now = Date()
        var dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second, .nanosecond], from: now)
        dateComponents.year = year
        dateComponents.month = month
        dateComponents.day = day
        dateComponents.hour = hour
        dateComponents.minute = minute
        dateComponents.second = second
        dateComponents.nanosecond = 1000000 * (millisecond ?? 0)

        if let timeZone = timeZone {
            dateComponents.timeZone = timeZone
        }

        guard let date = Calendar.current.date(from: dateComponents) else {
            fatalError("Failed to construct date")
        }

        self.init(timeInterval: 0, since: date)
    }
}
BevTheDev
  • 563
  • 2
  • 9