ios - NSDate 在与 NSString 之间转换后不相等
问题描述
我正在尝试从 JSON 存储和检索日期。从相同字符串创建的 NSDates 失败isEqualToDate:
。这当然是由于浮点精度问题,但我不知道如何解决它。
给定两个相同的输入字符串dateFromString:
,结果NSDate
对象应该相等:
NSDate *date1 = [NSDate date];
NSString *string1 = [dateFormatter stringFromDate:date1];
NSDate *dateA = [dateFormatter dateFromString:string1];
NSDate *dateB = [dateFormatter dateFromString:string1];
XCTAssertTrue([dateA isEqualToDate:dateB]);
...但是,实际上生成的日期对象很幸运是相等的(而且很少是相等的),因为由于浮点精度问题而引入了随机的缺陷:
(lldb) p [dateA timeIntervalSinceReferenceDate]
(NSTimeInterval) $4 = 560455653.79073596
(lldb) p [dateB timeIntervalSinceReferenceDate]
(NSTimeInterval) $5 = 560455653.79099989
那么,有人遇到过这种情况并解决它吗?我想到的唯一选择是自己写isEquals:
,但这并不理想。
编辑:
具体来说,我正在寻找一种将日期的字符串表示形式转换回日期对象的方法,该对象对于相同的输入字符串将被视为相等。
NSDate
将其内部状态存储在浮点数中的(明显)事实是无关紧要的,因为 Foundation 应该提供一种机制来在给定相同输入时实现输出日期的奇偶校验(即,相同的字符串应该产生相等的对象)。要么是 Foundation 提供了这个,但我很想念它(希望 SO 社区知道一些我不知道的 Foundation),或者 Foundation 有问题(在这种情况下),我正在向 SO 社区寻求我没有的解决方法'还没有考虑。
编辑2:
抱歉,我的问题,正如最初提出的那样,是无稽之谈。为了简化这篇文章的问题,我引入了两个不可能相等的数字之间的无意比较。
原创废话,供后人参考:
我正在尝试从 JSON 存储和检索日期。我从存储的字符串重新创建的对象上
NSDate
存储到 JSON 中失败。这当然是由于浮点精度问题,但我不知道如何解决它。具体来说:isEqualToDate:
NSDate
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; NSDate *date1 = [NSDate date]; NSString *string1 = [dateFormatter stringFromDate:date1]; NSDate *date2 = [dateFormatter dateFromString:string1]; NSString *string2 = [dateFormatter stringFromDate:date2]; // This test passes XCTAssertTrue([string1 isEqualToString:string2]); // This test fails XCTAssertTrue([date1 isEqualToDate:date2]); // This test fails XCTAssertTrue([date1 isEqual:date2]);
查看
date1
和date2
在调试器中我们看到了不同之处:(lldb) p [date1 timeIntervalSinceReferenceDate] 560363055.21521103 (lldb) p [date2 timeIntervalSinceReferenceDate] 560363055.21499991
(注意千位)
NSDate
的isEqual:
(和变体)几乎可以肯定是在比较实例的时间偏移量,因此失败了。我尝试为存储的字符串(即
.SSSSSS
)添加更多精度,但这似乎没有影响。那么,有人遇到过这种情况并解决它吗?我想到的唯一选择是自己写
isEquals:
,但这并不理想。
解决方案
tl; dr - 您正在尝试将纳秒与毫秒进行比较。这些结果将不一样。
当你创建一个NSDate
with时,[NSDate date];
你会得到一个包含小数秒到微秒甚至纳秒精度的值。
当您将日期转换为具有格式的字符串时,yyyy-MM-dd'T'HH:mm:ss.SSS
您正在创建一个精确到 3 位小数(毫秒)的字符串。然后,当您将该字符串转换回一个NSDate
时,您会得到一个浮点数,它尽可能地接近那些毫秒。
因此,您的原始日期精度为微秒或纳秒,而第二个日期仅为毫秒。当然,由于精度不同,这两个日期也会有所不同。这与浮点数无关。即使您有完美的浮点数,您也将 100.123456789 与 100.123 进行比较。它们不是同一个数字。
您说您尝试使用SSSSSS
而不是,SSS
但NSDateFormatter
不尊重超过三个小数位的任何内容,因此超出的任何内容SSS
都是浪费精力。
有了这个解释,你有什么解决方案来比较你的两个日期?
一种是仅比较两个日期到小数点后三位。这是一个有用的小NSDate
类别方法,它可以做到这一点:
@interface NSDate (extra)
- (BOOL)isEqualToDateMilliseconds:(NSDate *)otherDate;
@end
@implementation NSDate (extra)
- (BOOL)isEqualToDateMilliseconds:(NSDate *)otherDate {
TimeInterval secs1 = [self timeIntervalSinceReferenceDate];
TimeInterval secs2 = [self timeIntervalSinceReferenceDate];
return abs(secs1 - secs2) < 0.001;
}
@end
如果您有自己的代码,请使用更好的类别名称。
现在您可以替换:
XCTAssertTrue([date1 isEqualToDate:date2]);
和:
XCTAssertTrue([date1 isEqualToDateMilliseconds:date2]);
你会得到正确的结果。
推荐阅读
- php - Woocommerce如何获取最近订单的产品图片
- mongodb - Like Count 查询 Mongoose
- python - 从列表或字典中编写任意大小的布尔表达式
- c++ - 为什么 std :: string 对象的最大大小是 4611686018427387897?
- url - curl命令将war文件推送到github操作中的url
- visual-studio-code - VSCode 配置默认集成终端
- excel - 匹配函数 - 数组查找值
- r - 绘图未显示神经网络
- javascript - 在添加到可能不存在的对象属性时如何使用 += ?
- android - 从右到左输入数字就像解决数学问题 Android Studio