DST 的时区问题(神秘的缺失两个小时)

发布于 2024-11-09 21:32:26 字数 1265 浏览 0 评论 0原文

我有一段代码在过去确实有效(至少我认为它确实有效,因为否则问题就会更早被识别出来)。几周后,可能是从夏令时开始,它开始产生不正确的结果。

此代码片段应该生成一个 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 上花了半天时间后,我无法弄清楚问题出在哪里。也许我使用了错误的关键字,或者我是唯一有这个问题的人。

每一条信息都受到高度赞赏!请告诉我您的想法!谢谢你!

I have a piece of code that did work in the past (at least I think it did because otherwise the problem would have been recognized earlier). Since a few weeks, might be since DST, it began to produce incorrect results.

This snippet is supposed to generate a NSDate object referring to the first day in the given dates week at 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);

Today is the 24th of May so the last line should print something like

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

but it really prints the following

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

which is obviously wrong. The 22nd of May was last sunday. Now to make things weird, the result is correct again when I change the setHour:0 to

[compsCurrentDate setHour:2];

My guess is, that there's something wrong about the timezones. But after spending half a day on the Apple documentation, google and stackoverflow I could'nt figure out what the problem is. Maybe I am using the wrong keywords or I am the only one with that problem.

Every piece of information is highly appreciated! Please let me know what you think! Thank you!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

三寸金莲 2024-11-16 21:32:26

由于没有人能够回答,我继续我的研究,并在 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]]);

Since nobody was able to answer I continued my research and discovered a simple but fatal detail in the documentation of NSData: the description method which is called when trying to print the NSDate as a NSString by default uses a "default" NSLocale to determine the format and timezone. So everything about my code was correct, the real problem I tried to fix was somewhere else and I already did this on monday .. I should RTFM more intense ;)

The following line of code prints the correct time for my timezone:

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

Nevertheless, during my research I discovered some interesting snippets doing basically the same thing (although the output was "wrong" as well, that's why I didn't simply replace mine). Taking all of them into consideration my attempt is working but a little bumpy. The most easy and robust I found comes from the Apple documentation itself and can also be found on various websites:

/* 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]]);

Finally, to complete this: a snippet how to calculate the last day of the current week.

// 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]]);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文