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

Objective c NSDate值始终在我的时区中返回,objective-c,nsdate,nsdateformatter,Objective C,Nsdate,Nsdateformatter,TL/DR:我需要一个NSDate特别代表1970-01-01 00:00:00+0000。使用[nsdatedatewithtimeintervalsince1970:0]返回根据我的时区调整的日期。我想不出怎么解决这个问题 我正试图编写一个Objective-CIOS测试,该测试围绕着NSDates。问题是,我尝试创建的任何NSDate对象,无论是直接创建的,还是使用NSDateFormatter或[NSDateComponents datefromfomcomponents]创建的,似乎都

TL/DR:我需要一个
NSDate
特别代表
1970-01-01 00:00:00+0000
。使用
[nsdatedatewithtimeintervalsince1970:0]
返回根据我的时区调整的日期。我想不出怎么解决这个问题

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

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

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


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

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

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

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

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

如果需要,您可以向
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的数量有限,因此它不能唯一地表示时间中的每个瞬间。时间中的许多瞬间映射到相同的
double
值。

这是因为您古怪的调试器选择在本地时区中表示日期
po
向您展示NSDate中真正的内容。@HotLicks如果它不影响我的测试结果,我会接受这个解释。但是NSDate对象本身是经过时区调整的;这不仅仅是它的呈现方式。NSDate对象不应该进行时区调整。它包含的实时时间是您在NSLog或do
po
上看到的时间。我见过NSDate中的时区设置为UTC以外的情况(虽然不是以年为单位)。(显然这里有一个古老的时区字段。)但它不应该影响NSDate如何使用NSDateFormatter打印。(您是否有旧的开发环境?iOS或OSX?)使用
[NSDate dateWithTimeIntervalSince1970:0]
返回根据我的时区调整的日期。“不,它没有。”。它根本不调整NSDate
NSDate
没有时区;它反映了一个时间点。如何显示即时时间是
NSDateFormatter
的一个功能(它确实理解时区的概念)。因此,将格式化程序的
时区设置为
[NSTimeZone timeZoneForSecondsFromGMT:0]
,它将以UTC格式显示。不要担心调试器会显示什么。谢谢您的详细解释。我对Objective-C还是有点陌生,所以这对我来说只是另一件事