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

此生再无相见时 提交于 2019-12-22 18:35:13

问题


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

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?


回答1:


You didn't specify a time zone (by setting the timeZone property of either components or calendar). So the system used your local time zone to convert components to a date.

The playground system used your local time zone to convert newDate to the string “Jan 1, 1 at 7:00 AM”. (The playground system has special-case code for displaying Date objects. The special-case code uses your local time zone.)

The print function used the UTC time zone to convert newDate to the string “0001-01-01 11:56:02 +0000”. (The print function uses the CustomStringConvertible protocol. Date's CustomStringConvertible implementation uses the UTC time zone.)

I deduce that your local time zone is US/Eastern time, also known as 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

So why the weird minutes and seconds in UTC? Because at noon on November 18, 1883, the US and Canada railway companies began using a new, standard time system, which is the time system we still use today. Observe:

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

Prior to noon, the difference from UTC time is 4:56:02.

Before the advent of standard railway time, we typically defined local time based on local apparent noon (the moment when the sun is highest in the sky and shadows point exactly north or south or disappear entirely if the sun is directly overhead).

If we look at the definition of America/New_York in the tz Time Zone Database, we find this:

# From Paul Eggert (2014-09-06):
# Monthly Notices of the Royal Astronomical Society 44, 4 (1884-02-08), 208
# says that New York City Hall time was 3 minutes 58.4 seconds fast of
# Eastern time (i.e., -4:56:01.6) just before the 1883 switch.  Round to the
# nearest second.

and a bit further down:

Zone America/New_York   -4:56:02 -  LMT 1883 Nov 18 12:03:58

This explains the difference we see above prior to noon on November 18, 1883.



来源:https://stackoverflow.com/questions/47686895/swift-date-from-components-is-incorrect-when-printed-date-has-2-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!