为什么 NSDate 的 dateWithTimeIntervalSince1970 修改它的输入?
Why NSDate's dateWithTimeIntervalSince1970 modifies its input?
我是运行以下代码:
for (int i = 0; i < 100; ++i) {
NSDate *date = [NSDate date];
NSTimeInterval interval = date.timeIntervalSince1970;
NSDate *newDate = [NSDate dateWithTimeIntervalSince1970:interval];
if (![date isEqualToDate:newDate]) {
NSLog(@"Not equal!");
}
}
令人惊讶的是,在许多迭代中,日期彼此不相等。怎么可能?
反汇编dateWithTimeIntervalSince1970:
显示调用-initWithIntervalSinceReferenceDate:
:
CoreFoundation`+[NSDate dateWithTimeIntervalSince1970:]:
0x10fd39430 <+0>: pushq %rbp
0x10fd39431 <+1>: movq %rsp, %rbp
0x10fd39434 <+4>: pushq %rbx
0x10fd39435 <+5>: pushq %rax
0x10fd39436 <+6>: movsd %xmm0, -0x10(%rbp)
0x10fd3943b <+11>: movq 0x2d8146(%rip), %rsi ; "alloc"
0x10fd39442 <+18>: movq 0x29edc7(%rip), %rbx ; (void *)0x000000010f35e940: objc_msgSend
0x10fd39449 <+25>: callq *%rbx
0x10fd3944b <+27>: movsd -0x10(%rbp), %xmm0 ; xmm0 = mem[0],zero
0x10fd39450 <+32>: addsd 0x1f9d58(%rip), %xmm0 ; _CFLog_os_trace_type_map + 16
0x10fd39458 <+40>: movq 0x2d9041(%rip), %rsi ; "initWithTimeIntervalSinceReferenceDate:"
0x10fd3945f <+47>: movq %rax, %rdi
0x10fd39462 <+50>: callq *%rbx
0x10fd39464 <+52>: movq 0x2d81b5(%rip), %rsi ; "autorelease"
0x10fd3946b <+59>: movq %rax, %rdi
0x10fd3946e <+62>: movq %rbx, %rax
0x10fd39471 <+65>: addq [=10=]x8, %rsp
0x10fd39475 <+69>: popq %rbx
0x10fd39476 <+70>: popq %rbp
0x10fd39477 <+71>: jmpq *%rax
0x10fd39479 <+73>: nopl (%rax)
另外,初始化器的输入是secs - 978307200
(或者,secs - NSTimeIntervalSince1970
),这是1970年和参考日期之间的时差。此计算在从双精度数减去整数(双精度数)时可能会由于舍入误差而改变输入值的分数。例如,这是未通过测试的日期:
date: Sun Mar 25 17:54:39 2018 (543682479.8504179716),
newDate: Sun Mar 25 17:54:39 2018 (543682479.8504180908)
由于log2(543682479.8504179716 + NSTimeIntervalSince1970) ~ 30.5
和log2(543682479.8504179716) ~ 29.01
,double值的指数需要调整,尾数需要归一化,可能会影响小数值
解决方案是改用+dateWithTimeIntervalSinceReferenceDate:
工厂方法,直接初始化一个NSDate
,不需要额外的计算。
引用 1970 年的方法用于 BSD-style 自 1970 年以来的日期间隔。定义了一个分辨率,IIRC 毫秒。因此,NSTimeValue
值有意四舍五入到该规格。由于此舍入,您不能期望转换和重新转换的值是相同的值。
只使用这些方法,当你得到一个BSD的时候。这记录在案:
This method is useful for creating NSDate objects from time_t values returned by BSD system functions.
https://developer.apple.com/documentation/foundation/nsdate/1591576-datewithtimeintervalsince1970
但是,正如 StatusReport 所说,日期是浮点数。测试是否相等总是很危险的。 IE。当您使用 Core Data 存储日期时,它是 "rounded" 到 SQL 列的分辨率。
我是运行以下代码:
for (int i = 0; i < 100; ++i) {
NSDate *date = [NSDate date];
NSTimeInterval interval = date.timeIntervalSince1970;
NSDate *newDate = [NSDate dateWithTimeIntervalSince1970:interval];
if (![date isEqualToDate:newDate]) {
NSLog(@"Not equal!");
}
}
令人惊讶的是,在许多迭代中,日期彼此不相等。怎么可能?
反汇编dateWithTimeIntervalSince1970:
显示调用-initWithIntervalSinceReferenceDate:
:
CoreFoundation`+[NSDate dateWithTimeIntervalSince1970:]:
0x10fd39430 <+0>: pushq %rbp
0x10fd39431 <+1>: movq %rsp, %rbp
0x10fd39434 <+4>: pushq %rbx
0x10fd39435 <+5>: pushq %rax
0x10fd39436 <+6>: movsd %xmm0, -0x10(%rbp)
0x10fd3943b <+11>: movq 0x2d8146(%rip), %rsi ; "alloc"
0x10fd39442 <+18>: movq 0x29edc7(%rip), %rbx ; (void *)0x000000010f35e940: objc_msgSend
0x10fd39449 <+25>: callq *%rbx
0x10fd3944b <+27>: movsd -0x10(%rbp), %xmm0 ; xmm0 = mem[0],zero
0x10fd39450 <+32>: addsd 0x1f9d58(%rip), %xmm0 ; _CFLog_os_trace_type_map + 16
0x10fd39458 <+40>: movq 0x2d9041(%rip), %rsi ; "initWithTimeIntervalSinceReferenceDate:"
0x10fd3945f <+47>: movq %rax, %rdi
0x10fd39462 <+50>: callq *%rbx
0x10fd39464 <+52>: movq 0x2d81b5(%rip), %rsi ; "autorelease"
0x10fd3946b <+59>: movq %rax, %rdi
0x10fd3946e <+62>: movq %rbx, %rax
0x10fd39471 <+65>: addq [=10=]x8, %rsp
0x10fd39475 <+69>: popq %rbx
0x10fd39476 <+70>: popq %rbp
0x10fd39477 <+71>: jmpq *%rax
0x10fd39479 <+73>: nopl (%rax)
另外,初始化器的输入是secs - 978307200
(或者,secs - NSTimeIntervalSince1970
),这是1970年和参考日期之间的时差。此计算在从双精度数减去整数(双精度数)时可能会由于舍入误差而改变输入值的分数。例如,这是未通过测试的日期:
date: Sun Mar 25 17:54:39 2018 (543682479.8504179716),
newDate: Sun Mar 25 17:54:39 2018 (543682479.8504180908)
由于log2(543682479.8504179716 + NSTimeIntervalSince1970) ~ 30.5
和log2(543682479.8504179716) ~ 29.01
,double值的指数需要调整,尾数需要归一化,可能会影响小数值
解决方案是改用+dateWithTimeIntervalSinceReferenceDate:
工厂方法,直接初始化一个NSDate
,不需要额外的计算。
引用 1970 年的方法用于 BSD-style 自 1970 年以来的日期间隔。定义了一个分辨率,IIRC 毫秒。因此,NSTimeValue
值有意四舍五入到该规格。由于此舍入,您不能期望转换和重新转换的值是相同的值。
只使用这些方法,当你得到一个BSD的时候。这记录在案:
This method is useful for creating NSDate objects from time_t values returned by BSD system functions.
https://developer.apple.com/documentation/foundation/nsdate/1591576-datewithtimeintervalsince1970
但是,正如 StatusReport 所说,日期是浮点数。测试是否相等总是很危险的。 IE。当您使用 Core Data 存储日期时,它是 "rounded" 到 SQL 列的分辨率。