【问题标题】:Losing double precision when dealing with timestamps处理时间戳时丢失双精度
【发布时间】:2015-09-22 00:00:16
【问题描述】:

我有一个简单的XCTestCase

func testExample() {
    let date = "2015-09-21T20:38:54.379912Z";// as NSString;
    let date1 = 1442867934.379912;

    XCTAssertEqual(date1, NSDate.sam_dateFromISO8601String(date).timeIntervalSince1970);
}

这个测试通过了,但它不应该通过,原因有两个:

  • 1442867934.379912 在测试中变为 1442867934.379911,尽管它实际上只是一个重新打印的变量
  • sam_ 函数(复制如下)似乎也是这样想的,millisecond 变量变成了millisecond double 0.37991200000000003 0.37991200000000003,后来从NSDate 转换为double,似乎失去了微秒精度(调试器) : po NSDate.sam_dateFromISO8601String(date).timeIntervalSince1970 -> 1442867934.37991

po 1442867934.379912 -> 1442867934.37991

知道为什么吗?微秒精度(小数点后 6 位)对我的应用程序非常重要,我需要使用 iso8601 格式无缝转换为 NSStringNSDate


+ (NSDate *)sam_dateFromISO8601String:(NSString *)iso8601 {
    // Return nil if nil is given
    if (!iso8601 || [iso8601 isEqual:[NSNull null]]) {
        return nil;
    }

    // Parse number
    if ([iso8601 isKindOfClass:[NSNumber class]]) {
        return [NSDate dateWithTimeIntervalSince1970:[(NSNumber *)iso8601 doubleValue]];
    }

    // Parse string
    else if ([iso8601 isKindOfClass:[NSString class]]) {
        const char *str = [iso8601 cStringUsingEncoding:NSUTF8StringEncoding];
        size_t len = strlen(str);
        if (len == 0) {
            return nil;
        }

        struct tm tm;
        char newStr[25] = "";
        BOOL hasTimezone = NO;

        // 2014-03-30T09:13:00Z
        if (len == 20 && str[len - 1] == 'Z') {
            strncpy(newStr, str, len - 1);
        }

        // 2014-03-30T09:13:00-07:00
        else if (len == 25 && str[22] == ':') {
            strncpy(newStr, str, 19);
            hasTimezone = YES;
        }

        // 2014-03-30T09:13:00.000Z
        else if (len == 24 && str[len - 1] == 'Z') {
            strncpy(newStr, str, 19);
        }
        // 2014-03-30T09:13:00.000000Z
        else if (len == 27 && str[len - 1] == 'Z') {
            strncpy(newStr, str, 19);
        }


        // 2014-03-30T09:13:00.000-07:00
        else if (len == 29 && str[26] == ':') {
            strncpy(newStr, str, 19);
            hasTimezone = YES;
        }

        // Poorly formatted timezone
        else {
            strncpy(newStr, str, len > 24 ? 24 : len);
        }

        // Timezone
        size_t l = strlen(newStr);
        if (hasTimezone) {
            strncpy(newStr + l, str + len - 6, 3);
            strncpy(newStr + l + 3, str + len - 2, 2);
        } else {
            strncpy(newStr + l, "+0000", 5);
        }

        // Add null terminator
        newStr[sizeof(newStr) - 1] = 0;

        if (strptime(newStr, "%FT%T%z", &tm) == NULL) {
            return nil;
        }

        double millisecond = 0.0f;

        NSString *subStr = [[iso8601 componentsSeparatedByString:@"."].lastObject substringToIndex:6];

        millisecond = subStr.doubleValue/1000000.f;

        time_t t;
        t = mktime(&tm);

        return [NSDate dateWithTimeIntervalSince1970:t + millisecond];
    }

    NSAssert1(NO, @"Failed to parse date: %@", iso8601);
    return nil;
}

【问题讨论】:

    标签: objective-c swift iso8601


    【解决方案1】:

    1442867934.3799121442867934.379911 很可能等于相同的数字,只是打印不同(一个舍入,另一个截断)...如果微秒时间对您很重要,也许您应该查看https://developer.apple.com/library/mac/qa/qa1398/_index.html

    尤其是因为 [NSDate date] 在时钟变化时会发生巨大变化...而绝对时间 api 不会...

    【讨论】:

    • 我从不让客户端修改或创建任何时间戳。一切都来自服务器并被传回以确定哪些对象需要同步。我不确定那篇文章对我有什么帮助......我想如果我再次这样做,我会将时间戳作为数字发送并作为 NSNumber 存储在客户端......但鉴于我希望使用 NSDate,如何我可以解析 NSString 和 NSDate 之间的微秒吗?它实际上只是简单的输入和输出......只是证明不是很简单。
    【解决方案2】:

    我最终只是更改了我的 API 以传递 Time 对象的 double 表示。即1442867934.379912根本不会与ISO8601混淆。

    NSDate 原生支持此功能。所以我不必解析任何字符串。因此,性能是一个额外的好处。

    我在 Rails/Rabl/Oj 中遇到的几个怪癖:

    • 需要将double 作为string 发送。 iOS 上的 JSONKit 似乎不喜欢超过 15 个有效数字,JSON 规范也不喜欢。

    Rabl(初始化器):

    Oj.default_options = {
      use_to_json: true,
      second_precision: 6,
      float_precision: 16
    }
    

    默认的to_f 没有发送我需要的精度。

    【讨论】:

      猜你喜欢
      • 2012-05-13
      • 1970-01-01
      • 2013-06-17
      • 1970-01-01
      • 2021-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-23
      相关资源
      最近更新 更多