转换为NSString和从NSString转换后NSDate不等于

lev*_*ker 4 objective-c ios

我正在尝试存储和检索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]);
Run Code Online (Sandbox Code Playgroud)

...但是,实际上生成的日期对象很幸运是相同的(很少是),因为由于浮点精度问题引入了随机残差:

(lldb) p [dateA timeIntervalSinceReferenceDate]
(NSTimeInterval) $4 = 560455653.79073596
(lldb) p [dateB timeIntervalSinceReferenceDate]
(NSTimeInterval) $5 = 560455653.79099989
Run Code Online (Sandbox Code Playgroud)

那么,任何人都会遇到这种情况并解决它吗?我想到的唯一选择就是自己编写,isEquals:但这并不理想.


编辑:

具体来说,我正在寻找的是一种将日期的字符串表示转换回日期对象的方法,对于相同的输入字符串,该日期对象将被视为相等.

NSDate将其内部状态存储在浮点数中的(明显)事实是无关紧要的,因为基金会应该提供一种机制来在给定相同输入时实现输出日期的奇偶校验(即相同的字符串应该产生相等的对象).基金会提供这个并且我错过了它(并且希望SO社区知道关于基金会我不知道的事情),或者基金会是错误的(在这种情况下)并且我正在寻找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]);
Run Code Online (Sandbox Code Playgroud)

查看date1date2调试器中我们看到了差异:

(lldb) p [date1 timeIntervalSinceReferenceDate]
560363055.21521103

(lldb) p [date2 timeIntervalSinceReferenceDate]
560363055.21499991
Run Code Online (Sandbox Code Playgroud)

(注意千分之一的地方)

NSDateisEqual:(和变体)几乎可以肯定地比较实例的时间偏移,从而失败.

我已经尝试为存储的字符串添加更多精度(即 .SSSSSS),但这似乎没有影响.

那么,任何人都会遇到这种情况并解决它吗?我想到的唯一选择就是自己编写,isEquals:但这并不理想.

rma*_*ddy 5

tl; dr - 你试图比较纳秒到毫秒.那些结果将不一样.

当你创建一个NSDatewith时,[NSDate date];你得到一个包含小数秒的值,精确到微秒,甚至可能是纳秒.

将日期转换为具有格式的字符串时,yyyy-MM-dd'T'HH:mm:ss.SSS您将创建一个精确到3位小数(毫秒)的字符串.然后,当您将该字符串转换回a时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
Run Code Online (Sandbox Code Playgroud)

如果您自己的代码,请使用更好的类别名称.

现在你可以替换:

XCTAssertTrue([date1 isEqualToDate:date2]);
Run Code Online (Sandbox Code Playgroud)

有:

XCTAssertTrue([date1 isEqualToDateMilliseconds:date2]);
Run Code Online (Sandbox Code Playgroud)

你会得到正确的结果.

  • 您的方法不能按预期工作.两个浮点值可以小于1/1000,但仍然在不同方向上圆.使用`fabsf(a - b)<0.001`. (2认同)
  • 我指的是你发布的链接.这是官方规范(规范).`NSDateFormatter`并不完全遵守规范. (2认同)