问题
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