NSDate值始终在我的时区中返回

时间:2015-03-03 22:50:51

标签: objective-c nsdate nsdateformatter

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时),但在调试窗口中显示为时区调整。此外,这个时区调整后的值是我在测试中使用的值,因此它对我编写可靠测试的能力有一定的影响。

1 个答案:

答案 0 :(得分:2)

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


作为人类,我们有很多方法可以及时代表。例如,“2001年1月1日00:00:00 GMT”表示特定时刻。同一时刻的另一个名字是“2000年12月31日18:00:00美国/芝加哥”。同一时刻的第三个名字是“2001年1月1日03:00:00欧洲/莫斯科”。所有这些名称(字符串)以及更多名称代表了同一时刻。作为人类,我们认识到所有这些标签代表了同一时刻。

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

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

使用%@中的NSLog打印日期,或在调试程序中使用po时,会将description消息发送到日期。 description方法的工作是为NSDate表示的唯一时刻生成一些人类表示。但是有许多可能的人类表示,而description方法只能使用一个。该方法的实现者决定使用基于您当地时区的表示。

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

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

@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值。