Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

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

I create an instance of DateComponents using the following code:

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)

I then print the dateComponents object and get the following (expected) output:

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

Immediately following this, I print the date created using the following code:

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

To my great dismay and thorough unhappiness, the following is outputted:

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

The date(from: dateComponents) function appears to have added just over a minute to the dateComponents before creating a date from them.

Thanks for any help in advance.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

NSDate has some strange and undocumented behaviors for ancient dates. The change seems to have happened around 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!)
    }
}

My calendar is Gregorian and timezone is EDT (UTC -0500). This is the output:

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

So for the years prior to 1895, Apple somehow added 17 minutes and 32 second to my time. You got a different offset, which is likely due your locale settings.

I couldn't find anything historical event about the Gregorian calendar in 1895. This question mentions that Britain started to switch over to GMT and the Greenwich Observatory started adjusting date/time standards across the British Isles in the 1890s so that may have accounted for this offset. Perhaps someone can delve into the source code for Date / NSDate and figure it out?


If you want to use DateComponent to store a repeating schedule, use nextDate(after:matching:matchingPolicy:) to find the next occurance of your schedule:

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)!

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...