Swift - Date from components is incorrect when printed. Date has 2 values?

Aar*_*ker 3 nscalendar nsdatecomponents swift

I'm trying to store a time of day in a Date:

let calendar = NSCalendar.init(identifier: .gregorian)
let components = NSDateComponents()
components.hour = 7
components.minute = 0

var newDate = calendar?.date(from: components as DateComponents)!

print(newDate!)
Run Code Online (Sandbox Code Playgroud)

However, this yields some bizarre results when I try to print or otherwise use the value. Here's a Swift Playground of the results:

奇怪的日期结果

How can newDate be both 7:00AM and 11:56AM at the same time?

rob*_*off 5

您没有指定时区(通过设置或的timeZone属性)。因此系统使用您的本地时区转换为日期。componentscalendarcomponents

游乐场系统使用您当地的时区转换newDate为字符串“Jan 1, 1 at 7:00 AM”。(游乐场系统具有用于显示Date对象的特例代码。特例代码使用您的本地时区。)

该print函数使用 UTC 时区转换newDate为字符串“0001-01-01 11:56:02 +0000”。(该print函数使用CustomStringConvertible协议。Date的CustomStringConvertible实现使用 UTC 时区。)

我推断您当地的时区是美国/东部时间,也称为America/New_York:

import Foundation

var calendar = Calendar(identifier: .gregorian)
let components = NSDateComponents()
components.hour = 7
components.minute = 0

for timeZoneIdentifier in TimeZone.knownTimeZoneIdentifiers {
    calendar.timeZone = TimeZone(identifier: timeZoneIdentifier)!
    let date = calendar.date(from: components as DateComponents)!
    let dateString = "\(date)"
    if dateString == "0001-01-01 11:56:02 +0000" {
        print("\(timeZoneIdentifier) \(date)")
    }
}
// Only one line of output: America/New_York 0001-01-01 11:56:02 +0000
Run Code Online (Sandbox Code Playgroud)

那么为什么UTC中奇怪的分钟和秒呢? 因为在 1883 年 11 月 18 日中午,美国和加拿大的铁路公司开始使用一种新的、标准的时间系统,这就是我们今天仍在使用的时间系统。观察:

import Foundation

let formatter = ISO8601DateFormatter()

var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(identifier: "America/New_York")!
let components = NSDateComponents()

components.year = 1883
components.month = 11
components.day = 18

components.hour = 11
print(formatter.string(from: calendar.date(from: components as DateComponents)!))
// prints 1883-11-18T15:56:02Z

components.hour = 12
print(formatter.string(from: calendar.date(from: components as DateComponents)!))
// prints 1883-11-18T17:00:00Z
Run Code Online (Sandbox Code Playgroud)

中午之前,与 UTC 时间的时差为 4:56:02。

在标准铁路时间出现之前,我们通常根据当地的视正午(太阳在天空中最高且阴影正好指向北方或南方或如果太阳直接在头顶上完全消失的时刻)来定义当地时间。

如果我们查看tz Time Zone Database中 America/New_York 的定义,我们会发现:

import Foundation

var calendar = Calendar(identifier: .gregorian)
let components = NSDateComponents()
components.hour = 7
components.minute = 0

for timeZoneIdentifier in TimeZone.knownTimeZoneIdentifiers {
    calendar.timeZone = TimeZone(identifier: timeZoneIdentifier)!
    let date = calendar.date(from: components as DateComponents)!
    let dateString = "\(date)"
    if dateString == "0001-01-01 11:56:02 +0000" {
        print("\(timeZoneIdentifier) \(date)")
    }
}
// Only one line of output: America/New_York 0001-01-01 11:56:02 +0000
Run Code Online (Sandbox Code Playgroud)

再往下一点:

import Foundation

let formatter = ISO8601DateFormatter()

var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(identifier: "America/New_York")!
let components = NSDateComponents()

components.year = 1883
components.month = 11
components.day = 18

components.hour = 11
print(formatter.string(from: calendar.date(from: components as DateComponents)!))
// prints 1883-11-18T15:56:02Z

components.hour = 12
print(formatter.string(from: calendar.date(from: components as DateComponents)!))
// prints 1883-11-18T17:00:00Z
Run Code Online (Sandbox Code Playgroud)

这解释了我们在上面看到的 1883 年 11 月 18 日中午之前的差异。