同一本地时间的 NSDate 与不同时区的另一个 NSDate

NSDate for the same local time as another NSDate in different time zones

我有约会时间:

Friday, July 8, 2016 at 6:30:00 PM India Standard Time

我想要这样的约会时间:

Friday, July 8, 2016 at 6:30:00 PM Central Daylight Time

我不想将使用 NSDateFormatter 的日期从一个时区转换为另一个时区,因为这种转换会给我

Friday, July 8, 2016 at 8:00:00 AM Central Daylight Time

我想要相同的日期时间,但在不同的时区。我该怎么做?

您可以通过将日期拆分为原始时区的组件、在组件上设置目标时区并检索结果日期(不会相同 date/time)来实现此目的

import Foundation

let calendar = NSCalendar.currentCalendar()
let date = NSDate()
let origTz = NSTimeZone(abbreviation: "IST")!
let destTz = NSTimeZone(abbreviation: "CDT")!
let components = calendar.componentsInTimeZone(origTz, fromDate: date)
components.timeZone = destTz
let res = components.date!
let fmt = NSDateFormatter()
fmt.timeZone = destTz
fmt.locale = NSLocale(localeIdentifier: "en_US_POSIX")
fmt.dateStyle = .FullStyle
fmt.timeStyle = .FullStyle
fmt.timeZone = origTz
print(fmt.stringFromDate(date)) // Sunday, July 10, 2016 at 2:14:21 AM India Standard Time
fmt.timeZone = destTz
print(fmt.stringFromDate(res))  // Sunday, July 10, 2016 at 2:14:21 AM Central Daylight Time

dasblinkenlight 的回答绝对正确,但我会更全面地解释一下,并在 ObjC 中显示步骤而不是 Swift。您不希望在不同时区的相同日期;您想要相同的 当地时间(或 挂钟时间):人们看时钟时会看到什么。

不同时区的同一日期具有不同的当地时间:2016 年 7 月 8 日星期五 6:30:00 下午印度标准时间 7 月 8 日星期五, 2016 年中部夏令时间 8:00:00 上午。在加尔各答的人看时钟时会看到 6:30 下午,在奥马哈的人看时钟时会看到 8:00 上午。

首先,仅出于演示目的,我们将使用日期解析器根据您提供的字符串创建原始日期。您只需从通知对象中获取日期。

NSString * originalDateString = @"Friday, July 8, 2016 at 6:30:00 PM India Standard Time";

NSDateFormatter * parser = [NSDateFormatter new];
[parser setDateStyle:NSDateFormatterFullStyle];
[parser setTimeStyle:NSDateFormatterFullStyle];
NSDate * originalDate = [parser dateFromString:originalDateString];

尽管原始字符串包含时区信息,日期不。然而,挂钟时间取决于时区,因此下一步我们需要一个时区对象。你也会从你的通知对象中得到这个。在这里,我们将仅根据其标识符创建一个:NSTimeZone * originalTZ = [NSTimeZone timeZoneWithAbbreviation:@"IST"];

我们需要一个 NSDateComponents 来表示区域中日期的本地时间值。 NSCalendar 负责将日期分解为这些组件:

NSCalendar * cal = [NSCalendar currentCalendar];
NSDateComponents * localComps;
localComps = [cal componentsInTimeZone:originalTZ
                              fromDate:originalDate];

如果您现在检查 localComps,您会看到这些值封装了您期望的挂钟时间:2016-07-08 18:30:00。现在我们已经分解了这些值,我们基本上可以将它们放入另一个区域 而无需转换 。也就是说,如果不将这些值重新解释为绝对时间,如上文所讨论的,这将给出 2016-07-08 08:00:00。只需更改时区(同样,您将从通知而不是其缩写中获取时区):

NSTimeZone * destinationTZ = [NSTimeZone timeZoneWithAbbreviation:@"CDT"];
[localComps setTimeZone:destinationTZ];

现在日历可以从这些组件构造一个新的日期对象。 NSDate * destinationDate = [cal dateFromComponents:localComps];*

此日期是与时区无关的时刻,表示 2016 年 7 月 8 日星期五中部夏令时间 6:30:00 下午。与任何 NSDate 一样,如果您只是将其打印出来,它将采用 GMT 格式。如果您想查看您期望的挂钟时间值,您需要使用设置为目标时区的格式化程序。


*正如 dasblinkenlight 的回答所示,localComps 有一个关联的日历,因此它也能够自己创建日期。我认为这会更清楚一些。