【问题标题】:Swift - Date from components is incorrect when printed. Date has 2 values?Swift - 打印时来自组件的日期不正确。日期有 2 个值?
【发布时间】:2018-05-21 01:52:52
【问题描述】:

我正在尝试在日期中存储一天中的时间:

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

但是,当我尝试打印或以其他方式使用该值时,这会产生一些奇怪的结果。这是结果的 Swift Playground:

newDate 怎么可能同时是上午 7:00 和上午 11:56?

【问题讨论】:

  • 仅供参考 - 使用 Date 以小时和分钟为单位存储时间是个坏主意。您真正需要如何处理“一天中的时间”?
  • 另请注意,playground 以当地时间显示 Date 对象,当您 print a Date 时,日期值以 UTC 时间显示。因此,除非您生活在 UTC 时间,否则您将始终看到两个不同的时间。但这种情况更不寻常,因为差异通常是整个小时或半小时,具体取决于您的时区。这种情况很可能是因为年份是 1 而不是更现代的年份。
  • 值得一提的是,你应该使用DateComponent而不是NSDateComponent和Calendar而不是NSCalendar,因为Swift3
  • 重申 rmaddy 的观点,“我试图在日期中存储一天中的时间”意味着您错误地使用了 Date。不要那样做。如果要存储“时钟上的时间”,则存储 2-3 个整数(小时、分钟、秒)。如果您想存储“自最近午夜以来的秒数”,请存储它(请记住,这与小时/分钟/秒不同相同,因为 DST 可能会发生变化)。
  • 公元 1 年还没有公历 ???? 1582 年之前的所有内容都未指定。

标签: swift nscalendar nsdatecomponents


【解决方案1】:

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

操场系统使用您的本地时区将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

那么为什么会有奇怪的 UTC 分钟和秒数? Because at noon on November 18, 1883, the US and Canada railway companies began using a new, standard time system,这是我们今天仍在使用的时间系统。观察:

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

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

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

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

# 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.

再往下一点:

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

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

【讨论】:

  • 我明白这一点,但是.. 那我怎样才能打印正确的时间呢?
  • 这取决于你所说的“正确的时间”是什么意思。您可能应该发布自己的问题并提供完整的详细信息。
  • 所以我在这之后意识到...... print 不会改变实际值,但它只会显示不同时区的值。当答案说打印“转换”时,我感到很困惑。 print 实际上并没有更改该值,而仅以 UTC 显示。现在我明白了。
猜你喜欢
  • 1970-01-01
  • 2020-06-26
  • 2020-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-18
  • 2014-09-29
相关资源
最近更新 更多