如何获得NSDate
的2个值之间的精确差异(十进制)。
EG。 2016年1月15日至 2017年7月15日 = 1。5年。
我可以使用类似的东西:
NSCalendar.currentCalendar().components(NSCalendarUnit.CalendarUnitYear, fromDate: date1, toDate: date1, options: nil).year
但这给了我绝对值。即,对于上面的例子,它会给我 1年。是否有可能将精确值正确至少几位小数?
答案 0 :(得分:5)
您在此使用的条款具有误导性。当你说"绝对"你的意思是"积分。"当你说"确切"你的意思是"在一定的精度范围内。"
让我们说你想要的精度是2位小数,所以我们需要测量一年到1%。这比一天大,所以追踪天数就足够了。如果你需要更高的精度,那么你可以扩展这种技术,但是如果你把它推得太远,"年"变得更加棘手,你必须在一年之后开始询问你的意思。"
尽可能避免问这个问题。这里的许多答案都说像#34;一年有365.25天。"但是尝试添加" 365.25 * 24小时"到现在""看看你明年是否得到了相同的日期和时间。"虽然看起来可能是正确的,但平均而言,#34;对于日历日期,它实际上是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
答案 1 :(得分:3)
由于您提到您的目标是可以向用户显示的内容,作为两个日期之间时间的有意义指示,您可能会发现使用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年”之类的结果,但it doesn't seem to work right now。 (即使您将allowedUnits
限制为仅.Year
,您仍也不会获得分数年份。我要向Apple提交错误...) 。您可以调整allowedUnits
以获得您喜欢的任何粒度,并使用includesApproximationPhrase
让类将“About ...”的本地化版本添加到结果字符串中(如果它不准确)。如果您在最终格式中有一定的灵活性,这将是一个非常好的解决方案。
答案 2 :(得分:2)
这个问题没有一个完美的答案。不同年份的长度略有不同。你必须做出一些假设。
如果您假设每年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年,并且会忽略更长/更短的月份。这是正确的做法吗?取决于你的目标和你的假设。)
答案 3 :(得分:1)
根据NASA,平均每年有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
显然,这取决于你的假设。如果您想计算fromDate
和toDate
之间的闰日,则会更复杂。
一些示例输出:
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