您在这里使用的术语具有误导性。当你说“绝对”时,你的意思是“整体”。当你说“精确”时,你的意思是“在某个期望的精度范围内”。
假设您想要的精度是小数点后两位,因此我们需要将一年测量到 1%。这比一天还长,因此跟踪天数就足够了。如果你需要更精确,那么你可以扩展这个技术,但如果你把它推得太远,“年”就会变得更加棘手,你必须开始问你想要什么mean到“一年”。
尽可能避免问这个问题。这里的许多答案都说“一年有 365.25 天”。但尝试将“365.25 * 24 小时”添加到“现在”,看看是否会得到“明年相同的日期和时间”。虽然“平均而言”看起来是正确的,但对于日历日期来说,实际上 100% 的时间都是错误的。 (这里之所以有效,是因为它在 1% 以内,但 365、366 甚至 363 也是如此。)
我们通过说“1% 足以解决这个问题”来避免这种疯狂。
// 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)
也就是说,在大多数情况下您不应该这样做。从 iOS 8 开始,首选工具是NSDateComponentsFormatter
。您不会得到这种精确的格式(即小数年),但您会得到一个很好的本地化结果,该结果考虑了不同文化中的大多数问题。
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