夏令时的时区问题(神秘失踪两小时)

时间:2011-05-23 23:30:12

标签: objective-c cocoa-touch nsdate

我有一段代码在过去确实有效(至少我认为这样做是因为否则问题会在之前得到认可)。从几周开始,可能是自DST以来,它开始产生不正确的结果。

此代码段应生成一个NSDate对象,引用给定日期周的第一天00:00h。

NSCalendar *gregorianCalender = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDate *firstDayDate;

unsigned yearAndWeek = NSTimeZoneCalendarUnit | NSYearCalendarUnit | NSWeekCalendarUnit;

// retrieve the components from the current date
NSDateComponents *compsCurrentDate = [gregorianCalender components:yearAndWeek fromDate:date];

[compsCurrentDate setWeekday:2]; // Monday
[compsCurrentDate setHour:0];
[compsCurrentDate setMinute:0];
[compsCurrentDate setSecond:0];

// make a date from the modfied components
firstDayDate = [gregorianCalender dateFromComponents:compsCurrentDate]; 

NSLog(@"MONDAY: %@", firstDayDate);

今天是5月24日,所以最后一行应该打印类似

MONDAY: 2011-05-23 00:00:00 +0000

但它确实打印了以下内容

MONDAY: 2011-05-22 22:00:00 +0000

这显然是错误的。 5月22日是最后一个星期天。现在为了让事情变得奇怪,当我将setHour:0更改为

时,结果再次正确
[compsCurrentDate setHour:2];

我的猜测是,时区出了点问题。但是在苹果文档,谷歌和stackoverflow上花了半天后,我无法弄清楚问题是什么。也许我使用了错误的关键字,或者我是唯一一个遇到这个问题的人。

我们非常感谢每一条信息!请让我知道你在想什么!谢谢!

1 个答案:

答案 0 :(得分:3)

由于没有人能够回答我继续我的研究并在NSData的文档中发现了一个简单但致命的细节:description方法在尝试打印NSDate时调用默认情况下,NSString使用“默认”NSLocale来确定格式和时区。所以关于我的代码的一切都是正确的,我试图修复的真正问题是在其他地方,我已经在星期一做了这个...我应该RTFM更强烈;)

以下代码行打印时区的正确时间:

NSLog(@"MONDAY: %@", [firstDayDate descriptionWithLocale:[NSLocale systemLocale]]);

然而,在我的研究中,我发现一些有趣的片段基本上做同样的事情(虽然输出也是“错误的”,这就是为什么我不简单地取代我的)。考虑到所有这些因素,我的尝试工作有点崎岖不平。我找到的最简单和最强大的内容来自Apple文档本身,也可以在各种网站上找到:

/* VERSION FROM APPLE DOKU */
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

// Get the weekday component of the current date
NSDateComponents *weekdayComponents = [gregorian components:NSWeekdayCalendarUnit fromDate:date];

/*
 Create a date components to represent the number of days to subtract from the current date.
 The weekday value for Monday in the Gregorian calendar is 2, so subtract 2 from the number of days to subtract from the date in question.  (If today's Sunday, subtract 0 days.)
 */
NSDateComponents *componentsToSubtract = [[NSDateComponents alloc] init];
[componentsToSubtract setDay: 0 - ([weekdayComponents weekday] - 2)];

NSDate *beginningOfWeek = [gregorian dateByAddingComponents:componentsToSubtract toDate:date options:0];

/*
 Optional step:
 beginningOfWeek now has the same hour, minute, and second as the original date (today).
 To normalize to midnight, extract the year, month, and day components and create a new date from those components.
 */
NSDateComponents *components =
[gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
             fromDate: beginningOfWeek];
beginningOfWeek = [gregorian dateFromComponents:components];

NSLog(@"MONDAY: %@", [beginningOfWeek descriptionWithLocale:[NSLocale systemLocale]]);

最后,要完成此操作:如何计算当前周的最后日的代码段。

// reuse the "first day" to calculate the last day
NSDate *endOfLastWeek = [whateveryourobject andmethodiscalled]; 

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

/*
 endOfLastWeek now is monday in the current week so create date components to add one week to the current date
 */
NSDateComponents *componentsToAdd = [[NSDateComponents alloc] init];
[componentsToAdd setWeek:1];

NSDate *endOfWeek = [gregorian dateByAddingComponents:componentsToAdd toDate:endOfLastWeek options:0];

NSLog(@"SUNDAY: %@", [endOfWeek descriptionWithLocale:[NSLocale systemLocale]]);