我要为Foundation的日期实例添加一秒钟,但结果要整整一分钟。
var calendar = Calendar(identifier: .iso8601)
calendar.locale = Locale(identifier: "en")
calendar.timeZone = TimeZone(identifier: "GMT")!
let date1 = Date(timeIntervalSinceReferenceDate: -62544967141.9)
let date2 = calendar.date(byAdding: DateComponents(second: 1),
to: date1,
wrappingComponents: true)!
ISO8601DateFormatter().string(from: date1) // => 0019-01-11T22:00:58Z
ISO8601DateFormatter().string(from: date2) // => 0019-01-11T21:59:59Z
有趣的是,下列其中一项使错误消失了:
我的代码中实际上并不需要亚秒精度,因此我创建了此扩展名,可以将其丢弃。
extension Date {
func roundedToSeconds() -> Date {
return Date(timeIntervalSinceReferenceDate: round(timeIntervalSinceReferenceDate))
}
}
我想知道这一点:
答案 0 :(得分:3)
为什么会发生此错误?
我会说这是Core Foundation(CF)中的错误。
Calendar.date(byAdding:to:wrappingComponents:)
调用内部Core Foundation函数_CFCalendarAddComponentsV
,该函数依次使用ICU Calendar C API。 ICU将时间表示为自Unix纪元以来的毫秒浮点数,而CF使用自NeXT参考日期以来的秒数的浮点数。因此,CF必须在调用ICU之前将其表示转换为ICU的表示,然后转换回以将结果返回给您。
这是它从CF时间戳转换为ICU时间戳的方式:
double startingInt;
double startingFrac = modf(*atp, &startingInt);
UDate udate = (startingInt + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
modf
函数将浮点数分为整数和小数部分。让我们插入示例日期:
var startingInt: Double = 0
var startingFrac: Double = modf(date1.timeIntervalSinceReferenceDate, &startingInt)
print(startingInt, startingFrac)
// Output:
-62544967141.0 -0.9000015258789062
接下来,CF调用__CFCalendarAdd
将一秒加到-62544967141。请注意,-62544967141位于一分钟的一分钟间隔-62544967200 .. <-62544967140.0中。因此,当CF向-62544967141加一秒时,它将得到-62544967140,这将在下一轮一分钟的间隔内。由于您指定了包装组件,因此CF不允许更改日期的分钟部分,因此它将包装回原始一分钟间隔-62544967200的开头。
最后,CF将ICU时间转换回CF时间,加上原始时间的小数部分:
*atp = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970 + startingFrac + (nanosecond * 1.0e-9);
因此它返回-62544967200 + -0.9000015258789062 = -62544967200.9,恰好比输入时间早59秒。
我做错什么了吗?
不,该错误在CF中,而不在您的代码中。
我的解决方法有问题吗?
如果您不需要亚秒精度,则解决方法应该没问题。
我可以用最近的日期重现它,但到目前为止只能用负的参考日期,例如日期(timeIntervalSinceReferenceDate:-1008899941.9),即1969-01-11T22:00:58Z。
每分钟间隔的最后一秒任何否定timeIntervalSinceReferenceDate
都会引起问题。该错误有效地使时间0之前的第一轮整分钟的范围从-60.99999999999999到-1.0,但是范围应该从-60.0到-5e324。所有负分钟的每分钟间隔都类似地偏移。