在 Swift 中,如何从 dispatch_time_t 中获取 NSDate?

In Swift, how can I get an NSDate from a dispatch_time_t?

"Walltime" 是 Grand Central Dispatch 使用的一种鲜为人知的时间格式。苹果在这里谈论它:

https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/

虽然有些东西确实很方便,但它是一个粘性检票口。很难让它与其他时间格式兼容,这就是我的问题。

我可以通过将 NSDate 变成 timespec,然后与 dispatch_walltime:

一起使用来制作 walltime
 let now = NSDate().timeIntervalSince1970
 let nowWholeSecsFloor = floor(now)
 let nowNanosOnly = now - nowWholeSecsFloor
 let nowNanosFloor = floor(nowNanosOnly * Double(NSEC_PER_SEC))
 var thisStruct = timespec(tv_sec: Int(nowWholeSecsFloor),
 tv_nsec: Int(nowNanosFloor))
 let wallTime = dispatch_walltime(& thisStruct, 0)

但是天哪,我不知道如何把它变回 NSDate。这是我的尝试:

public func toNSDate(wallTime: dispatch_time_t)->NSDate {
    let wallTimeAsSeconds = Double(wallTime) / Double(NSEC_PER_SEC)
    let date = NSDate(timeIntervalSince1970: wallTimeAsSeconds)
    return date
}

由此产生的 NSDate 不仅偏离了,而且有点滑稽地偏离了,比如五百年之类的。正如 Martin R 指出的那样,问题在于 dispatch_time_t 是一个不透明的值,具有未记录的时间表示形式。

有人知道怎么做吗?

编辑:如果创建 walltime 的过程令人困惑,这基本上就是正在发生的事情:

NSDateDouble定义时间,小数点后的都是纳秒。 dispatch_time,可以创建一个walltime,用UInt64定义时间,所以你必须在DoubleUInt64之间转换才能使用它。要进行该转换,您需要使用 timespec,它将秒和纳秒作为单独的参数,每个参数都必须是 Int.

大量转换正在进行中!

真正的答案是:你不能

"time.h" 头文件中指出:

/*!
 * @typedef dispatch_time_t
 *
 * @abstract
 * A somewhat abstract representation of time; where zero means "now" and
 * DISPATCH_TIME_FOREVER means "infinity" and every value in between is an
 * opaque encoding.
 */
typedef uint64_t dispatch_time_t;

所以 dispatch_time_t 使用了一种未记录的 "abstract" 时间表示,它 甚至可能会在不同版本之间发生变化。

话虽这么说,让我们找点乐子,试着弄清楚是什么 a dispatch_time_t 确实是。所以我们看一下 "time.c",它包含了 dispatch_walltime():

dispatch_time_t
dispatch_walltime(const struct timespec *inval, int64_t delta)
{
    int64_t nsec;
    if (inval) {
        nsec = inval->tv_sec * 1000000000ll + inval->tv_nsec;
    } else {
        nsec = (int64_t)_dispatch_get_nanoseconds();
    }
    nsec += delta;
    if (nsec <= 1) {
        // -1 is special == DISPATCH_TIME_FOREVER == forever
        return delta >= 0 ? DISPATCH_TIME_FOREVER : (dispatch_time_t)-2ll;
    }
    return (dispatch_time_t)-nsec;
}

有趣的部分是最后一行:它取 negative 值 纳秒,并且此值被转换回(无符号)dispatch_time_t。还有一些特殊情况。

因此,要反转转换,我们必须否定 dispatch_time_t 并将其视为纳秒:

public func toNSDate(wallTime: dispatch_time_t)->NSDate {

    // Tricky part HERE:
    let nanoSeconds = -Int64(bitPattern: wallTime)

    // Remaining part as in your question:
    let wallTimeAsSeconds = Double(nanoSeconds) / Double(NSEC_PER_SEC)
    let date = NSDate(timeIntervalSince1970: wallTimeAsSeconds)
    return date
}

确实,这会将挂钟时间正确地转换回原来的时间 NSDate,至少当我在 OS X 应用程序中测试它时是这样。

但是再说一次:不要这样做! 你会依赖一个未记录的表示,它可以在 OS 版本之间改变。还有可能 是上述代码中未考虑的特殊情况。 iOS 运行时中的表示也可能不同,我做到了 不要那样做。 您已收到警告!