获取单个NSDateComponent的2个日期之间的确切差异

如何获得NSDate 2个值之间的精确差异(十进制)。

例如。 2016年1月15日2017 7月15日 = 15年

我可以使用类似: NSCalendar.currentCalendar().components(NSCalendarUnit.CalendarUnitYear, fromDate: date1, toDate: date1, options: nil).year
但这给了我绝对的价值 。 即,对于上面的例子,它会给我1年 。 是否有可能将精确值正确至少几位小数?

您在此处使用的术语具有误导性。 当你说“绝对”时,你的意思是“积分”。 当你说“确切”时,你的意思是“在一定的精确度内。”

假设你想要的精度是2位小数,所以我们需要测量一年到1%。 这比一天大,所以跟踪天数就足够了。 如果你需要更高的精确度,那么你可以扩展这种技术,但是如果你把它推得太远,“年”会变得更加棘手,你必须开始问“一年”你的意思

尽可能避免问这个问题。 这里的许多答案都说“一年365.25天”。 但是尝试将“365.25 * 24小时”添加到“现在”,看看你是否得到“明年的相同日期和时间”。 虽然“平均而言”似乎是正确的,但实际上,日历日期的百分比实际上是错误的。 (它在这里工作,因为它在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 

由于您提到您的目标是可以向用户显示的内容,作为两个日期之间时间的有意义指示,您可能会发现使用NSDateComponentsFormatter更容易。 例如:

 let dateStr1 = "Jan 15 2016" let dateStr2 = "Jul 15 2017" let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = "MMM dd yyyy" if let date1 = dateFormatter.dateFromString(dateStr1), let date2 = dateFormatter.dateFromString(dateStr2) { let dateComponentsFormatter = NSDateComponentsFormatter() dateComponentsFormatter.allowedUnits = [.Year, .Month] dateComponentsFormatter.unitsStyle = .Full let difference = dateComponentsFormatter.stringFromDate(date1, toDate: date2) } 

这会给你一个字符串,写着“1年,6个月”。 这并不是您指定的目标,但它清楚地表明了用户并避免了很多复杂性。 在NSDateComponentsFormatter上有一个名为allowsFractionalUnits的属性,它应该会导致像“1。5年”这样的结果,但它现在似乎不起作用 。 (即使你只.Year限制。年,你仍然没有得到一个小数年。我要向Apple提交一个错误…)。 您可以调整allowedUnits以获得您喜欢的任何粒度,并使用includesApproximationPhrase让类在结果字符串中添加本地化版本的“About …”(如果它不精确)。 如果您在最终格式中有一定的灵活性,这将是一个非常好的解决方案。

这个问题没有一个完美的答案。 不同年份的长度略有不同。 你必须做出一些假设。

如果你假设每年365.2425天,每天有24小时,那么计算是微不足道的:

 let secondsPerYear: NSTimeInterval = NSTimeInterval(365.2425 * 24 * 60 * 60) let secondsBetweenDates = date2.timeIntervalSinceReferenceDate - date1.timeIntervalSinceReferenceDate; let yearsBetweenDates = secondsBetweenDates / secondPerYear 

但是有很多边缘案例和奇怪的事情需要处理。 因为闰年,有些年有365天,有些有366.然后有闰秒。

如果您在@ CodeDifferent的答案中摆脱了几个月,那么您将获得一个允许在日期之间进行闰天的答案。

但是,正如Code Different指出的那样,他写的答案实际上给出了更准确的答案,即使它们不是。 (相差3个月总会产生。25年,并且会忽略更长/更短的月份。这是正确的做法吗?取决于你的目标和你的假设。)

根据美国宇航局的数据 ,平均每年有365.2422天。 在这里,我每年最多365.25天:

 let components = NSCalendar.currentCalendar().components([.Year, .Month, .Day], fromDate: fromDate, toDate: toDate, options: []) var totalYears = Double(components.year) totalYears += Double(components.month) / 12.0 totalYears += Double(components.day) / 365.25 

显然,这取决于你的假设。 如果你想计算fromDatetoDate之间的闰天,它会更复杂。

一些样本输出:

 From date To date Total Years ------------ ------------ ------------ Jan 15, 2016 Jul 15, 2017 1.5 Jan 15, 2016 Apr 14, 2016 0.25 Jan 15, 2016 Aug 15, 2017 1.5833 Jan 15, 2016 Jan 14, 2018 1.9988