【问题标题】:NSDate value is always returned in my time zoneNSDate 值总是在我的时区返回
【发布时间】:2015-03-03 22:50:50
【问题描述】:

TL/DR:我需要一个 NSDate 专门代表 1970-01-01 00:00:00 +0000。使用 [NSDate dateWithTimeIntervalSince1970:0] 返回为我的时区调整的日期。我不知道如何解决这个问题。

我正在尝试编写一个围绕NSDates 的Objective-C iOS 测试。问题是,我尝试创建的任何NSDate 对象,无论是直接的,还是使用NSDateFormatter[NSDateComponents dateFromComponents],似乎总是会针对我的Mac 当前时区进行调整。也就是说,测试将使用 NSDate 的时区调整值,它也会以这种方式出现在 Xcode 调试器中。但是,如果我在 NSDateComponentsNSDateFormatter 中将时区明确设置为 GMT,当我使用 po 检查控制台中的值时,它将像我设置的那样返回 GMT 的日期/时间。

例如,[NSDate dateWithTimeIntervalSince1970:0] 返回一个日期为 1969-12-31 19:00:00 -0500 的 NSDate。 [NSDate date] 返回当前日期和时间,但根据我的时区:2015-03-03 05:49:32 -0500。我一生都想不通为什么我不能简单地将1970-01-01 00:00:00 +0000 作为 NSDate。

我有什么遗漏吗?谁能帮我解决这个问题?

谢谢!

编辑:这是我尝试使用NSDateComponents

NSCalendar *calendar = [[NSCalendar alloc]
                            initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *components = [[NSDateComponents alloc] init];
    [components setYear:1970];
    [components setMonth:1];
    [components setDay:1];
    [components setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
    NSDate *componentDate = [calendar dateFromComponents:components];

componentDate 在控制台中正确显示(当我键入 po componentDate 时),但在调试窗口中显示为时区调整。此外,这个时区调整值是我的测试中使用的值,因此它对我编写可靠测试的能力有一定的影响。

【问题讨论】:

  • 那是因为你的调试器选择在本地时区表示日期。 po 向您展示 NSDate 中的真正内容。
  • @HotLicks 如果它不影响我的测试结果,我会接受这个解释。但是 NSDate 对象本身是时区调整的;这不仅仅是它的呈现方式。
  • NSDate 对象不应进行时区调整。它包含的实际时间是您在 NSLog 或在其上执行 po 时所看到的。
  • 我见过(虽然不是几年)NSDate 中的时区设置为 UTC 以外的情况。 (显然那里有一个古老的时区字段。)但它不应该影响 NSDate 如何使用 NSDateFormatter 打印。 (你有旧的开发环境吗?iOS 还是 OSX?)
  • "使用 [NSDate dateWithTimeIntervalSince1970:0] 会返回为我的时区调整的日期。"不,它没有。它根本不调整NSDateNSDate 没有时区;它反映了一个时间点。如何显示该瞬间是NSDateFormatter 的函数(确实了解时区的概念)。因此,将格式化程序的 timeZone 设置为 [NSTimeZone timeZoneForSecondsFromGMT:0],它将以 UTC 格式显示。不用担心调试器显示的内容。

标签: objective-c nsdate nsdateformatter


【解决方案1】:

TLDR:[NSDate dateWithTimeIntervalSince1970:0] 表示您要求的日期。它是将NSDate 转换为涉及您的时区的字符串。


作为人类,我们有很多方式来表示时间的瞬间。例如,“2001 年 1 月 1 日 00:00:00 GMT”表示特定的时间点。同一时刻的另一个名称是“December 31, 2000 18:00:00 America/Chicago”。同一时刻的第三个名称是“January 1, 2001 03:00:00 Europe/Moscow”。所有这些名称(字符串)以及更多名称都代表同一时刻。作为人类,我们认识到所有这些标签都代表同一时刻。

NSDate 以完全不同的方式表示时间瞬间:NSDate 存储相对于一个预定参考瞬间的偏移量,以秒为单位。 NSDate 不存储任何时区信息。它只存储一个双精度数。对于每一瞬间,只有一个 NSDate 代表它。1

注意人类表示和NSDate 表示之间的区别:一个瞬间有许多人类表示,但只有一个NSDate 表示。

当您在NSLog 中使用%@ 或在调试器中使用po 打印日期时,它会向该日期发送description 消息。 description 方法的工作是为NSDate 所代表的独特瞬间生成一些人类表示。但是有很多可能的人类表示,description 方法只能使用一种。该方法的实现者决定使用基于您当地时区的表示。

如果您不喜欢使用本地时区,请不要使用description(直接或间接)。创建一个NSDateFormatter,设置它的timeZone,根据需要设置它的其他属性,并使用它来格式化您的日期。您已经注意到这是有效的。这是解决您问题的正确方法。

如果需要,您可以向 NSDate 添加一个类别,定义一个返回 GMT 日期表示的 gmtDescription 方法:

@interface NSDate (Argus9)
- (NSString *)gmtDescription;
@end

@implementation NSDate (Argus9)

- (NSString *)gmtDescription {
    static NSDateFormatter *df;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        df = [[NSDateFormatter alloc] init];
        df.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
        df.dateFormat = @"yyyy-MM-dd HH:mm:ss";
    });
    return [df stringFromDate:self];
}

@end

脚注 1. 这并不完全正确,因为double 有两个零:+0 和 -0。两者都代表同一时间瞬间(参考瞬间本身)。此外,由于NSDate 将其偏移存储为double,并且双精度数是有限的,因此它不能唯一地表示每个时刻。许多瞬间映射到相同的double 值。

【讨论】:

  • 感谢您如此详尽的解释。我对Objective-C还是有点陌生​​,所以这只是我试图绕过的另一件事,但你的描述很有帮助。事实证明,我尝试测试的方法确实使用了NSDateFormatter,但没有设置时区,所以我猜它在测试期间使用了我的 Mac 系统时区。从现在开始我要注意的事情!无论如何,再次感谢,我很感激!
猜你喜欢
  • 1970-01-01
  • 2023-04-01
  • 2021-04-19
  • 2017-03-21
  • 2015-12-19
  • 1970-01-01
  • 1970-01-01
  • 2019-09-15
  • 1970-01-01
相关资源
最近更新 更多