首先要理解的关键是它NSDate
并不代表您可能正在考虑的任何“日期”。它代表一个单一的时间瞬间(不考虑相对论效应或其他诸如此类的东西;正是您直觉上认为的“时间瞬间”)。
例如,这意味着 anNSDate
没有时区。它没有位置。它本身对“天”、“小时”或“月”一无所知。它只是自特定时刻以来经过的秒数。
为什么我要不断地谈论这个?因为NSDate
几乎从来都不是您想要的调度应用程序。当您说“2017 年 3 月 15 日下午 2:00”时,您的意思是基于某些日历的标称时间。因为日历可能会随着时间而改变(实际上 DST 规则确实会以不可预知的方式随着时间而改变),所以您无法准确知道任何未来的标称时间代表什么瞬间。您可以猜测,但您无法知道,因为在此之前将名称映射到瞬间的规则可能会发生变化。当然,即使知道规则,标称时间也将代表不同的时刻,具体取决于它必须应用于哪个时区。甚至可能根本不存在标称时间(因为 DST 可能会跳过它),或者它映射到多个瞬间(因为 DST 可能会向后跳过)。
如果您正在安排日程,您希望使用NSCalendar
and NSDateComponents
。这些工具让您可以对标称时间进行编码、应用时区,并以其他方式处理日历系统的混乱。您应该只NSDate
在必要时将它们转换为,意识到如果它是未来日期,则转换是不确定的。
NSDateComponents
还允许您跟踪您对日期的实际了解以及您不了解的内容,因为其中大部分是可选的。例如,可以创建NSDateComponents
一个nil
有时区但没有时间的。这使您可以表达诸如“万圣节是整个 10 月 31 日,无论您在哪个时区”之类的内容。但也要表达“本次会议在美国东部标准时间下午 2:00,即美国中部标准时间下午 1:00”。将这些东西NSDate
过早地转换为总是会导致万圣节从 10 月 30 日晚上 10 点开始,以及其他类似的废话。
关于您的编辑,我认为您的所有困惑都在于“2016-12-15T22:16:11+05:00”的含义。这是一个绝对的时间点。不是当地时间。这不是UTC。这是一个特定的时间点,每个人都会同意那个时间点。世界上任何人都会同意这是Unix Epoch之后的 1481822171 秒。月球或火星上的人也会同意。这是一个绝对的时间点。正确的原因是“+05:00”。这会将标称时间(“2016 年 12 月 15 日晚上 10:16:11”)偏移为绝对时间(纪元后 1481822171 秒)。
当你把它变成一个 NSDate 时,唯一存储的信息是 1481822171。没有别的了。它不是“在一个时区”。这不是“本地的”。它是 1481822171。当您打印日期的描述时,为了方便起见,它会为我打印“2016 年 12 月 15 日,下午 12:16”。但这只是为了方便而以当地时间打印。这并不意味着 NSDate 是“本地时间”。如果你想打印这个,你应该总是使用 NSDateFormatter 并应用你想要应用的时区。永远不要依赖description
。
我知道我一遍又一遍地说同样的话,但这似乎是人们真正“得到”的最困难的事情。绝对时间没有时区。时区纯粹是为了名义时间而存在的,因为它使人类快乐。标称时间与人类对日历的看法有关。他们与时间的运作方式只有有限的关系(不关心你在地球上的哪个位置,或者你所处的政治体系以及他们喜欢什么日历)。
如果您想知道这是否在一些名义时间之间,您需要找出这些时间实际上是什么时候。所以你会做这样的事情:
// 8pm and 11pm on the 15th of December (in the +05:00 timezone).
var startComp = DateComponents()
startComp.year = 2016
startComp.month = 12
startComp.day = 15
startComp.hour = 20
startComp.timeZone = TimeZone(secondsFromGMT: 5*60*60)! // +05:00
var endComp = startComp
endComp.hour = 23
let calendar = Calendar(identifier: .gregorian)
let startDate = calendar.date(from: startComp)!
let endDate = calendar.date(from: endComp)!
let targetDate = ISO8601DateFormatter().date(from: "2016-12-15T22:16:11+05:00")!
startDate < targetDate && targetDate < endDate // true