使用 DateFormatter 产生的结果会偏离一天

Using DateFormatter produces a result which is off by a day

使用 DateFormatter 产生的结果偏离一天(实际上是 12 小时)。使用以下代码始终如一地生成显示为前一天的日期。一段时间以来,我一直在许多应用程序中使用它,但终于抽出时间来深入研究它。

extension Date
{
    func display() -> String
    {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "MMMM dd, yyyy"
        print(dateFormatter.locale)
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        let txt = dateFormatter.string(from: self)
        print(txt)
        return txt
    }
}

与此相关的其他问题建议更改语言环境,因此为此添加了代码,但我检查了语言环境和实际日期。如果我在日期上加 8 小时,我会得到正确的显示结果,但加的时间少于 8 小时则没有任何效果。正在从联系人应用程序的生日字段中检索一些日期,这会产生具有一天中的时间 00:00:00 UTC 的日期。看来我需要将日期转换为当地时间?设备上的时区设置为本地时区(太平洋)。这看起来还不错,但是从日期选择器中检索到的日期不是 UTC 时间,而是当地时间。自从使用日历 class 并尝试提取 .timezone 组件后,我一直无法弄清楚如何判断日期所在的时区 "NSCalendarUnitTimeZone cannot be gotten by this method"。关于如何创建适用于所有情况的通用日期格式化程序有什么建议吗?

一些观察:

  1. 如果您的 Date 对象处于 UTC 时区,则也将格式化程序的 timeZone 设置为 TimeZone(secondsFromGMT: 0)

  2. 如果您要在 UI 中显示 Date 对象的字符串表示形式,您 不会 想要使用localeen_US_POSIX。你想在设备的默认位置显示它(即,根本不要更改格式化程序的 locale)。您仅在处理内部使用的 ISO 8601 和 RFC 3339 日期字符串时使用 en_US_POSIX,例如,用于与 Web 服务交换日期字符串)。

  3. 最后,我不会指定 dateFormat 字符串,因为并非所有用户都希望日期采用 MMMM dd, yyyy 格式。例如,英国用户希望它采用 d MMMM yyyy 格式。在 UI 中显示日期时,请改为指定 dateStyle。或者,如果这些样式中的 none 有效,请继续并指定 dateFormat,但使用 setLocalizedDateFormatFromTemplate(_:) 而不是固定字符串进行设置。

因此,为了您的目的,您可以这样做:

extension Date {
    var dateString: String {
        let formatter = DateFormatter()
        formatter.dateStyle = .long
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        return formatter.string(from: self)
    }
}

或者,如果您经常调用它,您可能希望重用格式化程序:

extension Date {

    private static let formatterForDateString: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .long
        formatter.timeZone = TimeZone(secondsFromGMT: 0)

        return formatter
    }()

    var dateString: String {
        return Date.formatterForDateString.string(from: self)
    }

}

使用timeZone属性,获取准确的日期,如下图:

    formatter.timeZone = TimeZone(secondsFromGMT: 0)

它将解决您的问题!