SwiftDate and date calculation

392 views Asked by At

I'm trying to calculate the number of weeks and days that a pet is alive. I'm using SwiftDate for that. So I let the user select a birthdate and compare it to the current date.

I don't want to compare the time... so both the birthdate and current date should have the same time.

My code is:

let greg = Calendar(identifier: .gregorian)
var date1 = birthday // from datepicker
var date2 = Date()
var components1 = greg.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date1)
var components2 = greg.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date2)
components1.hour = 15
components1.minute = 0
components1.second = 0
components2.hour = 15
components2.minute = 0
components2.second = 0
date1 = greg.date(from: components1)!
date2 = greg.date(from: components2)!

let daysAlive = (date2 - date1).in(.day)
let weeksAlive = Int(weeksAlive! / 7)
let restDays = daysAlive! - (weeksAlive * 7)
let aLive = "Age: \(weeksAlive) wk, \(restDays) d (\(daysAlive!) d)"

Output is "Age: 28 wk, 3 d (199 d)"

The code works... but how can I refactor it? And is there an easier way to set the 'same time' for two given dates?

3

There are 3 answers

4
vadian On BEST ANSWER

To ignore the time just omit the components for .hour, .minute and .second

let components1 = greg.dateComponents([.year, .month, .day], from: date1)
let components2 = greg.dateComponents([.year, .month, .day], from: date2)

date1 = greg.date(from: components1)!
date2 = greg.date(from: components2)!

This is a simpler solution without any third party library. It calculates the difference once for weekOfMonth and day and once for day with the Calendar method dateComponents(:from:to:. Extra math is not needed.

let greg = Calendar(identifier: .gregorian)
var date1 = greg.date(from: DateComponents(year: 2017, month: 6, day: 25))!
var date2 = Date()
var components1 = greg.dateComponents([.year, .month, .day], from: date1)
var components2 = greg.dateComponents([.year, .month, .day], from: date2)

let weeksAndDays = greg.dateComponents([.weekOfMonth, .day], from: components1, to: components2)
let justDays = greg.dateComponents([.day], from: components1, to: components2)

let aLive = "Age: \(weeksAndDays.weekOfMonth!) wk, \(weeksAndDays.day!) d (\(justDays.day!) d)"
// "Age: 28 wk, 3 d (199 d)"
0
creeperspeak On

If you don't care about the time difference, why not just leave the time out completely? It would give you the same output, and would just save you from putting in default time values and then ignoring them for the rest of your function:

let greg = Calendar(identifier: .gregorian)
var date1 = birthday // from datepicker
var date2 = Date()
var components1 = greg.dateComponents([.year, .month, .day], from: date1)
var components2 = greg.dateComponents([.year, .month, .day], from: date2)
date1 = greg.date(from: components1)!
date2 = greg.date(from: components2)!

let daysAlive = (date2 - date1).in(.day)
let weeksAlive = Int(weeksAlive! / 7)
let restDays = daysAlive! - (weeksAlive * 7)
let aLive = "Age: \(weeksAlive) wk, \(restDays) d (\(daysAlive!) d)"
1
Rob On

As others have pointed out, you can just grab .year, .month, .day from your calendar. You might also see if DateComponentsFormatter can achieve what you want:

let formatter: DateComponentsFormatter = {
    let _formatter = DateComponentsFormatter()
    _formatter.allowedUnits = [.day, .weekOfMonth]
    _formatter.unitsStyle = .full
    return _formatter
}()

@IBAction func didValueChange(_ picker: UIDatePicker) {
    let calendar = Calendar.current

    let birthDate = calendar.date(from: calendar.dateComponents([.year, .month, .day], from: picker.date))!
    let now = calendar.date(from: calendar.dateComponents([.year, .month, .day], from: Date()))!
    label.text = formatter.string(from: birthDate, to: now)
}

While the DateComponentsFormatter is a little constrained regarding the results, it is localized, resulting in the following in US locale:

28 weeks, 3 days

But if your app is configured to support other locales, for example German, it results in:

28 Wochen und 3 Tage