【问题标题】:Convert UTC to local time with NSDateFormatter使用 NSDateFormatter 将 UTC 转换为本地时间
【发布时间】:2018-04-02 14:18:48
【问题描述】:

我从我的 iOS 应用程序中的服务器获取以下字符串:

20140621-061250

如何将其转换为当地时间?

如何定义我的日期格式化程序?这是正确的吗?

dateFormatter.dateFormat = @"YYYYMMd-HHmmss"; 

【问题讨论】:

  • 转换成NSTimeInterval,再转换成NSDate,使用NSDateFormatter在本地时区生成字符串。
  • @HotLicks NSTimeInterval 似乎接受双倍。我正在取回我在问题中提到的字符串。这是一个有效的字符串吗?我要求检查服务器是否返回有问题的结果...
  • “转换为当地时间”是什么意思?您是否需要格式相同但调整为本地时间的字符串? NSDate 没有时区的概念。
  • 我将字符串误认为是 UNIX 时间戳,即“纪元”以来的秒数。 simpleBob(显然)正确地认为它只是一个压缩日期,并且可以使用正确的日期格式化字符串轻松解析。

标签: cocoa cocoa-touch time nsdateformatter date-parsing


【解决方案1】:

要将您的字符串放入 NSDate,您可以像这样使用NSDateFormatter

NSString *myDateAsAStringValue = @"20140621-061250"
NSDateFormatter *df = [[NSDateFormatter alloc] init];    
[df setDateFormat:@"yyyyMMdd-HHmmss"];
NSDate *myDate = [df dateFromString: myDateAsAStringValue];

你可能想阅读这篇关于working with Date and Time的帖子

编辑:

要将其解析为 UTC,您必须添加以下行:

[df setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];

另外,当你用 NSLog 打印它时,如果你使用相同的 NSDateFormatter,你会得到输入字符串作为输出(因为你应用了逆解析函数)。

这是完整的代码,用于解析和获取标准格式的输出:

//The input
NSString *myDateAsAStringValue = @"20140621-061250";

//create the formatter for parsing
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
[df setDateFormat:@"yyyyMMdd-HHmmss"];

//parsing the string and converting it to NSDate
NSDate *myDate = [df dateFromString: myDateAsAStringValue];

//create the formatter for the output
NSDateFormatter *out_df = [[NSDateFormatter alloc] init];
[out_df setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssz"];

//output the date
NSLog(@"the date is %@",[out_df stringFromDate:myDate]);

【讨论】:

  • 您需要在格式化程序上设置时区,否则它将使用您当地的时区。我在 UTC -6,这给了我一个 NSDate,它代表自 1970 年 1 月以来的 1403352770 秒,即 2014 年 6 月 21 日 12:12:50 UTC。当格式化程序或格式字符串中未指定时区时,它会隐式减去 UTC 偏移量。设置时区会给出正确的时间戳 1403331170,(2014 年 6 月 21 日,06:12:50 UTC)。
  • @simpleBob 不起作用。添加行后 NSString *local = [df stringFromDate:myDate];并使用 NSLog 打印,字符串 local 返回了 20140621-061250 的逐字副本。
  • @cateof 可能,您使用相同的格式化程序将字符串转换为日期,而不是从日期转换回字符串。这就是为什么您的输出看起来像输入的原因。 (见我的编辑)
【解决方案2】:

Swift 中使用 NSDate 扩展的一种可能解决方案(也许它可以帮助这个问题的未来观众):

import UIKit

// For your personal information: NSDate() initializer 
// always returns a date in UTC, no matter the time zone specified.
extension NSDate {
    // Convert UTC (or GMT) to local time
    func toLocalTime() -> NSDate {
        let timezone: NSTimeZone = NSTimeZone.localTimeZone()
        let seconds: NSTimeInterval = NSTimeInterval(timezone.secondsFromGMTForDate(self))
        return NSDate(timeInterval: seconds, sinceDate: self)
    }

    // Convert local time to UTC (or GMT)
    func toGlobalTime() -> NSDate {
        let timezone: NSTimeZone = NSTimeZone.localTimeZone()
        let seconds: NSTimeInterval = -NSTimeInterval(timezone.secondsFromGMTForDate(self))
        return NSDate(timeInterval: seconds, sinceDate: self)
    }
}

【讨论】:

  • 这是错误的。日期没有时区。这只是一个时间点。通过这样做,您正在更改日期,而您不应该这样做。如果您需要知道不同时区的时间组件(如小时),您应该使用日历方法 func dateComponents(in timeZone: TimeZone, from date: Date) -> DateComponents 或使用 DateFormatter 并更改时区以显示 UTC 时间
【解决方案3】:

这个问题并没有具体说明转换的含义,但是无论最终目标如何,您应该做的第一件事是使用正确配置的NSDateFormatter 正确解析服务器响应。这需要指定正确的格式字符串,并且必须在格式化程序上明确设置时区,否则它将从本地时间推断出来,这在大多数情况下是不正确的。

指定格式字符串

让我们看看提供的输入字符串:

20140621-061250

这使用四位数字表示年份,两位数字(用零填充)表示月份,以及两位数字(假设这些数字也将用零填充)表示一天。其后是-,然后是两个数字表示小时,2 位数字表示分钟,2 位数字表示秒。

参考Unicode date format standards,我们可以通过以下方式导出格式字符串。代表日历年的四位数字将替换为格式字符串中的yyyy。月份使用MM,当天使用dd。接下来是文字-。对于时间,我假设它将采用 24 小时格式,否则此响应会模棱两可,因此我们使用 HH。然后分钟是mm 和秒ss。连接格式说明符会产生以下格式字符串,我们将在下一步中使用它:

yyyyMMdd-HHmmss

在我们的程序中,这看起来像:

NSString *dateFormat = @"yyyyMMdd-HHmmss";

配置输入日期格式化程序

上面的时间格式没有指定时区,但是因为您已经获得了服务器响应的规范,它表示 UTC 时间,我们可以将其编码到我们的应用程序中。所以,我们实例化一个NSDateFormatter,设置正确的时区,设置日期格式:

NSTimeZone *inputTimeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
NSDateFormatter *inputDateFormatter = [[NSDateFormatter alloc] init];
[inputDateFormatter setTimeZone:inputTimeZone];
[inputDateFormatter setDateFormat:dateFormat];

将输入字符串转换为NSDate

出于演示目的,我们对您从服务器响应中收到的字符串进行硬编码;您可以将 inputString 的定义替换为您从服务器获得的定义:

NSString *inputString = @"20140621-061250";
NSDate *date = [inputDateFormatter dateFromString:inputString];

此时,我们有必要的对象来进行任何进一步的转换或计算——NSDate,它表示服务器通信的时间。请记住,NSDate 只是一个时间戳 - 它与时区无关,它仅在转换为日期的字符串表示或通过NSDateComponents 表示日历日期时才起作用。

后续步骤

这个问题没有明确说明需要什么类型的转换,所以我们将看到一个格式化日期的示例,以与服务器响应相同的格式显示(虽然,我想不出可能的用途老实说,这段特殊代码的情况)。步骤非常相似——我们指定一个格式字符串、一个时区、配置一个日期格式化程序,然后从日期生成一个字符串(以指定的格式):

NSTimeZone *outputTimeZone = [NSTimeZone localTimeZone];
NSDateFormatter *outputDateFormatter = [[NSDateFormatter alloc] init];
[outputDateFormatter setTimeZone:outputTimeZone];
[outputDateFormatter setDateFormat:dateFormat];
NSString *outputString = [outputDateFormatter stringFromDate:date];

由于我在 UTC-06:00,打印 outputString 会得到以下信息:

20140621-001250

如果您要向用户显示此日期,您可能会想要使用 setDateStyle:setTimeStyle: 而不是格式字符串,或者使用 NSCalendar 来获取 NSDateComponents 实例算术或计算日期。向用户显示详细日期字符串的示例:

NSTimeZone *outputTimeZone = [NSTimeZone localTimeZone];
NSDateFormatter *outputDateFormatter = [[NSDateFormatter alloc] init];
[outputDateFormatter setTimeZone:outputTimeZone];
[outputDateFormatter setDateStyle:NSDateFormatterFullStyle];
[outputDateFormatter setTimeStyle:NSDateFormatterFullStyle];
NSString *outputString = [outputDateFormatter stringFromDate:date];

在此处打印outputString 为我们提供以下信息:

2014 年 6 月 21 日星期六上午 12:12:50 山区夏令时

请注意,正确设置时区将处理夏令时的转换。使用上面的格式化程序样式代码将输入字符串更改为“20141121-061250”会给我们显示以下日期(请注意,山区标准时间是 UTC-7):

山区标准时间 2014 年 11 月 20 日星期四晚上 11:12:50

总结

每当您以字符串形式获取表示日历日期和时间的日期输入时,第一步是使用为输入格式、时区以及可能的区域设置和日历配置的NSDateFormatter 进行转换,具体取决于输入的来源和您的要求。这将产生一个NSDate,它是一个时刻的明确表示。在创建 NSDate 之后,可以根据您的应用程序需求对其进行格式化、设置样式或将其转换为日期组件。

【讨论】:

  • 这是一个很好的分析。很好的答案。
  • @cateof 谢谢!如果对问题进行了编辑以指定您在解析服务器日期后要执行的操作,如果您需要更多指导,我可能会扩展“下一步”。
  • 我将字符串作为 2014 年 12 月 5 日上午 9:11:51 传递,我的模拟器本地时间的输出是 2013 年 12 月 22 日下午 2:41:51。这怎么可能。服务器时间为 UTC。
  • 非常好的答案。我会用的。
猜你喜欢
  • 1970-01-01
  • 2023-03-31
  • 2018-07-27
  • 2011-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多