-1

I have a Date variable with a person's date birthday. I would like to know how many days remains before this person next birthday. It should be calculated from today date to current year birthday date.

How can this be done with Swift? Also it will be great to consider February 29 in leap years.

ucelme
  • 97
  • 1
  • 13
  • 2
    Does this answer your question? [Calculating the difference between two dates in Swift](https://stackoverflow.com/questions/50950092/calculating-the-difference-between-two-dates-in-swift) – Harry J Dec 15 '19 at 22:57
  • Not exactly. I need days calculated in one year, not the different years. – ucelme Dec 15 '19 at 23:06
  • 2
    Why doesn't Calendar.current.dateComponents([.day], from: fromDate, to: toDate).day not work? You just take the Date from today and the birthday and get the difference in days. Which is what you want – tclass Dec 15 '19 at 23:22
  • This code calculate days from the birthday year, but I need the days to be calculated from today date to current year birthday. – ucelme Dec 15 '19 at 23:30
  • 3
    Too broad. "Here is a large problem to be solved. Please solve it for me including all the code." That's not a proper thing to ask on SO. – matt Dec 15 '19 at 23:54
  • 1
    It's kind of a difficult question to answer without more context but Martin R solved it and I linked to their answer in my answer. – WikipediaBrown Dec 16 '19 at 00:08

2 Answers2

5

It depends on what you are looking to use the days value for but here is a small function that will return a Double describing the amount of days until a given Date. Martin R gave a really good answer here and my answer is mainly based on theirs with a little bit of documentation added.

/// This function takes a `Date` parameter and returns an `Int` describing how many days away the `Date` is into the future (accounting for leap years by making 2/29 dates land on 3/1 in non-leap years).
/// - Parameter date: The `Date` object to be evaluated.
func daysUntil(birthday: Date) -> Int {
    let cal = Calendar.current
    let today = cal.startOfDay(for: Date())
    let date = cal.startOfDay(for: birthday)
    let components = cal.dateComponents([.day, .month], from: date)
    let nextDate = cal.nextDate(after: today, matching: components, matchingPolicy: .nextTimePreservingSmallerComponents)
    return cal.dateComponents([.day], from: today, to: nextDate ?? today).day ?? 0
}
WikipediaBrown
  • 689
  • 1
  • 8
  • 27
5

To the guys who tried to close this: This is about birthday which has totally different rules from days.

Birthdays are complicated. Your birthday was the date of the moment when you were born, in the timezone where you were born. Considering that Samoa = UTC+14 and Baker Island = UTC-12, it is possible that people born at the same moment in time have birthdays that are two days apart.

So to store somebody's birthday, not the moment of birth, you either store year/month/day, or if you want to store it as a point in time, you store the beginning of that day in UTC, with the understanding that this is to specify a day, and must not be converted to local time.

Now when does your birthday repeat? If the person is born on D/M/Y and D/M is not February 29th, then the next birthday is either D/M/current year or D/M/next year. It is D/M/current year if that date is in the future, otherwise D/M/next year.

If the person is born on February 29th, then you have to determine when officially the next birthday is if the year is not a leap year - this will be February 28th or March 1st, depending on which rules apply.

We also need to clarify what "number of days" means. If my birthday is on April 1st, and now it is March 31st, one second to midnight, my birthday will be one second from now. However, I will assume that the result is supposed to be "one day from now".

So here is the algorithm:

Step 1: Find day/month/year when the person was born.

Step 2: Determine the current time, and convert it to local day/month/year. Determine the current time only once to avoid problems if this calculation is done nanoseconds before midnight.

Step 3: Determine the year when the birthday repeats: If day/month of birthday is greater than current day/month, then the year when the birthday repeats is the current year, otherwise the next year. This is also correct if the birthday was on Feb. 29th.

Step 4: Determine the day/month when the birthday repeats: This is the same as the day/month of the birthday, unless the birthday was on Feb. 29th and the year when the birthday repeats is not a leap year. In that case, the birthday repeats on Feb 28th or March 1st, depending on which rules you decide to apply.

Step 5: Convert the current day/month/year + 12 hours to UTC. Convert the date when the birthday repeats + 12 hours to UTC. Calculate the difference in seconds (which the OS should do for you). Divide by 86,400, then round to the nearest integer. The "+12 hours" and "round to nearest integer" make sure that you have no problems with daylight savings time, leap seconds etc.

Writing this code in Swift or any other language should be no problem.

gnasher729
  • 51,477
  • 5
  • 75
  • 98