首页 iOS 中时区的坑
文章
取消

iOS 中时区的坑

iOS 开发中以前用开发一直使用 DateFormatter / NSDateFormatter 来格式化时间, 获取时间字符串, 因此倒也没有遇到过时区的问题.

最近需要做一个类似于社区应用动态发布时间类似于:

发布于: 10 秒前 发布于: 1 小时前 …

这样的时间展示方式就需要来计算从发布到当前时间间隔来进行判断如何展示给用户.

因此使用如下代码来计算发布到当前时间过去了多少秒:

1
2
3
4
5
6
7
8
public func timeText(_ created: Date) -> String {
    // created 为动态创建的时间的 Date 对象
    let sec = Date().timeIntervalSince1970 - created.timeIntervalSince1970
    if sec < 60 {
        return String(Int(sec)) + "秒前"
    }
    // 省略其他判断分支
}

但是这样的写法可能会出现:

发布于: -275099 秒前 …

出现这样的结果的问题就出现在 Date() 这个初始上面. Date() 初始化的时间为 GTM +0000 时区的时间.

纠正: 原本以为 Date() 会根据当前系统设置的时区来初始化. 结果使用真机测试之后发现总是会以 GTM +0000 时区来初始化. 因此 使用 Date().timeIntervalSince1970 来获取当前的时间就会出现 8 小时 / 28800 秒 的时间差.

一般开发时候, 我们会使用 DateFormatter / NSDateFormatter 来对 NSDate / Date 对象进行时间字符串的格式化, 如果没有特殊需求的时候, 都会按照当前时区来进行处理, 倒也不会出现什么问题.

但是如果需要获取当前时区准确的 时间戳 来进行计算的时候, 就需要把 时区 考虑进去:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public func timeText(_ created: Date) -> String {
    // 获取当前时区
    let timeZone = TimeZone.current
    // 获取当前时区与 0 时区的时间差
    let offset = timeZone.secondsFromGMT()
    // 计算当前时区的时间戳
    let timestamp = Date().timeIntervalSince1970 + TimeInterval(offset)
    // 发布时间到当前时间间隔
    let sec = timestamp - created.timeIntervalSince1970
    if sec < 60 {
        return String(Int(sec)) + "秒前"
    }
    // 省略其他判断分支       
}

至此问题就解决了.

本文由作者按照 CC BY 4.0 进行授权