为什么 Calendar.date(from: DateComponents) 添加时间?

Why is Calendar.date(from: DateComponents) adding time?

我使用以下代码创建 DateComponents 的实例:

let dateComponents = DateComponents(
            calendar: .current,
            timeZone: Calendar.current.timeZone,
            era: nil,
            year: nil,
            month: nil,
            day: nil,
            hour: 9,
            minute: 0,
            second: 0,
            nanosecond: 0,
            weekday: 2,
            weekdayOrdinal: nil,
            quarter: nil,
            weekOfMonth: nil,
            weekOfYear: nil,
            yearForWeekOfYear: nil)

然后我打印 dateComponents 对象并获得以下(预期)输出:

calendar: gregorian (current) timeZone: Europe/London (current) hour: 9 minute: 0 second: 0 nanosecond: 0 weekday: 2 isLeapMonth: false

紧接着,我使用以下代码打印创建日期:

print(Calendar.current.date(from: dateComponents)!)

令我非常沮丧和彻底不高兴的是,输出如下:

0001-01-01 09:01:15 +0000

date(from: dateComponents) 函数似乎在 dateComponents 中添加了超过一分钟,然后才从它们创建日期。

提前感谢您的帮助。

NSDate 对古代日期有一些奇怪且未记录的行为。变化似乎发生在1895年左右:

for year in 1890..<1900 {
    // January 1 of each year @ 9AM
    let dateComponents = DateComponents(
        calendar: .current,
        timeZone: Calendar.current.timeZone,
        year: year,
        month: 1,
        day: 1,
        hour: 9)

    if dateComponents.isValidDate {
        print(dateComponents.date!)
    }
}

我的日历是公历,时区是美国东部时间 (UTC -0500)。这是输出:

1890-01-01 14:17:32 +0000
1891-01-01 14:17:32 +0000
1892-01-01 14:17:32 +0000
1893-01-01 14:17:32 +0000
1894-01-01 14:17:32 +0000 // not correct
1895-01-01 14:00:00 +0000 // correct
1896-01-01 14:00:00 +0000
1897-01-01 14:00:00 +0000
1898-01-01 14:00:00 +0000
1899-01-01 14:00:00 +0000

所以在 1895 年之前的几年里,Apple 以某种方式将我的时间增加了 17 分 32 秒。您得到了不同的偏移量,这可能是由于您的语言环境设置所致。

我找不到任何关于 1895 年公历的历史事件。This question 提到英国开始改用 GMT,格林威治天文台开始调整不列颠群岛的 date/time 标准在 1890 年代,这可能是造成这种抵消的原因。也许有人可以深入研究 Date / NSDate 的源代码并找出答案?


如果您想使用 DateComponent 来存储重复的时间表,请使用 nextDate(after:matching:matchingPolicy:) 来查找您的时间表的下一次出现:

let dateComponents = DateComponents(calendar: .current, timeZone: .current, hour: 9, weekday: 2)

// 9AM of the next Monday
let nextOccurance = Calendar.current.nextDate(after: Date(), matching: dateComponents, matchingPolicy: .nextTime)!