Get the exact difference between 2 dates for a single NSDateComponent

前端 未结 4 1647
暗喜
暗喜 2021-01-20 10:49

How can I get the exact difference (in decimal) between 2 values of NSDate.

Eg. Jan 15 2016 to Jul 15 2017 = <

4条回答
  •  醉梦人生
    2021-01-20 11:14

    The terms you've used here are misleading. When you say "absolute" you mean "integral." And when you say "exact" you mean "within some desired precision."

    Let's say the precision you wanted was 2 decimal places, so we'd need to measure a year to 1%. That's larger than a day, so tracking days is sufficient. If you needed more precision, then you could expand this technique, but if you push it too far, "year" gets more tricky, and you have to start asking what you mean by "a year."

    Avoid asking this question when you can. Many answers here say things like "there are 365.25 days in a year." But try adding "365.25 * 24 hours" to "right now" and see if you get "the same date and time next year." While it may seem correct "on average," it is actually wrong 100% of the time for calendar dates. (It works out here because it's within 1%, but so would 365, 366, or even 363.)

    We avoid this madness by saying "1% is close enough for this problem."

    // What calendar do you *really* mean here? The user's current calendar, 
    // or the Gregorian calendar? The below code should work for any calendar,
    // because every calendar's year is made up of some number of days, but it's
    // worth considering if you really mean (and are testing) arbitrary calendars.
    // If you mean "Gregorian," then use NSCalendar(identifier: NSCalendarIdentifierGregorian)!
    let calendar = NSCalendar.currentCalendar()
    
    // Determine how many integral days are between the dates
    let diff = calendar.components(.Day, fromDate: date1, toDate: date2, options: [])
    
    // Determine how many days are in a year. If you really meant "Gregorian" above, and
    // so used calendarWithIdentifer rather than currentCalendar, you can estimate 365 here.
    // Being within one day is inside the noise floor of 1%.
    // Yes, this is harder than you'd think. This is based on MartinR's code: http://stackoverflow.com/a/16812482/97337
    var startOfYear: NSDate? = nil
    var lengthOfYear = NSTimeInterval(0)
    calendar.rangeOfUnit(.Year, startDate: &startOfYear, interval: &lengthOfYear, forDate: date1)
    let endOfYear = startOfYear!.dateByAddingTimeInterval(lengthOfYear)
    let daysInYear = calendar.components(.Day, fromDate: startOfYear!, toDate: endOfYear, options: []).day
    
    // Divide
    let fracDiff = Double(diff.day) / Double(daysInYear)
    

    That said, in most cases you shouldn't be doing this. Since iOS 8, the preferred tool is NSDateComponentsFormatter. You won't get this precise format (i.e. fractional years), but you'll get a nicely localized result that takes most issues into account across different cultures.

    let formatter = NSDateComponentsFormatter()
    formatter.unitsStyle = .Full
    formatter.includesApproximationPhrase = true
    formatter.allowedUnits = [.Year, .Month]
    formatter.allowsFractionalUnits = true
    
    formatter.stringFromDate(date1, toDate: date2)
    // About 1 year, 6 months
    

提交回复
热议问题