2013-11-04 19 views
3

我很難理解dateByAddingComponents如何處理夏令時的例外情況。我已閱讀了Apple日期和時間編程指南,並預計dateByAddingComponents將DST更改考慮在內。但是,在DST更改日期,它不適合我。NSCalendar dateByAddingComponents如何處理夏時制錯誤

下面的代碼:

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; 
[gregorian setTimeZone:[NSTimeZone localTimeZone]]; 
NSDateComponents *midnight = [gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:self.currentDate]; 
midnight.hour = 0; 
midnight.minute = 0; 
midnight.second = 0; 


NSDate *startDate = [gregorian dateFromComponents:midnight]; 
NSDateComponents *offSetComponents = [[NSDateComponents alloc] init]; 
[offSetComponents setDay:1]; 
NSDate *endDate = [gregorian dateByAddingComponents:offSetComponents toDate:startDate options:0]; 

//Calculate start time from config (hours/min from seconds) 
int startTimeInMinutes = self.club.clubConfiguration.startTime.integerValue; 
int startTimeHours = startTimeInMinutes/60; 
int startTimeMins = startTimeInMinutes % 60; 
NSLog(@"---- startTimeHours %i", startTimeHours); 
NSLog(@"---- startTImeMins %i", startTimeMins); 

NSDateComponents *offSetComponents2 = [[NSDateComponents alloc] init]; 
[offSetComponents2 setHour:startTimeHours]; 
[offSetComponents2 setMinute:startTimeMins]; 
NSDate *firstTeeTime = [gregorian dateByAddingComponents:offSetComponents2 toDate:startDate options:0]; 

說明: 我從我用它來計算firstTeeTime服務器獲取一個startTimeInMinutes。例如,我希望將startDate(12am在我的用例中)添加6小時,並獲得6am(localTimeZone)。

使用dateByAddingComponents可以在DST更改之前和之後使用,但是在DST更改日的11月3日星期日,我得到了5am。

理論:由於11月3日星期日實際上有2個2點,我可能要解釋這個問題嗎?如果是這種情況,我不得不編寫一些邏輯來說明DST更改的實際日期,並在適當的情況下使用daylightSavingTimeOffsetForDate添加偏移量。

我失蹤了什麼?

編輯:好吧,我決定通過確定今天是否是DST更改並添加/刪除小時偏移量來解決問題。感覺有點像我在這裏錯過關於NSDate的東西,但是,它的工作原理。希望這可以幫助別人整天早上撓頭。

解決代碼:

////// Work around for DST 
NSTimeZone *currentZone = [gregorian timeZone]; 
NSDate *dstTransitionDate = [currentZone nextDaylightSavingTimeTransitionAfterDate:startDate]; 
NSTimeInterval dstOffset = [currentZone daylightSavingTimeOffsetForDate:endDate]; 
NSDateComponents *startDateComponents = [gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:startDate]; 
NSDateComponents *dstTransitionDateComponents = [gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:dstTransitionDate]; 
int offset = 0; 

if ([startDateComponents year] == [dstTransitionDateComponents year] && 
     [startDateComponents month] == [dstTransitionDateComponents month] && 
     [startDateComponents day] == [dstTransitionDateComponents day]) 
{ 
    if (dstOffset > 0){ 
     offset = -1; 
    } else { 
     offset = 1; 
    } 
} 
////// 

回答

1

我完全同情你,因爲我已經陷入同樣的​​陷阱。首先,我在結果和文檔方面摸不着頭腦,然後試圖推出定製的「添加日期組件」邏輯來嘗試傻瓜的差事。

然後,遇到你的答案,其完美配合,我的測試終於通:

// assert! 
XCTAssertEqual(dateFormatter.stringFromDate(dstSwitch.date), "11/1/15, 12:00 AM") 

// Offseting the date should just work 
do { 
    let dstOffset = dstSwitch + 3.hours 
    XCTAssertEqual(dateFormatter.stringFromDate(dstOffset.date), "11/1/15, 3:00 AM") 
} 

...但是堅持吧! Words of wisdom from @RobNapier讓我在正確的道路上...我們正在添加6個小時,這意味着一個人在活動中只會花費6個小時的生活。如果開始日期是午夜,他們會在凌晨2點將他們的手錶調整回凌晨1點,並根據日曆將遺失的時間浪費在遺忘時間中。

所以......如果事件真正在午夜開始,到凌晨6點結束,請注意Rob的話,不要使用持續時間。使用確切的日期組件。因爲那個時間實際上是7個小時。


爲了好玩,當我的測試困擾着我時,我意識到了這一點。爲什麼我只需要在同一天添加膠印?爲什麼改變日子「正常」? ... Liiiight bulb

而且,順便說一句,我的測試是針對我推出的庫,應該結束所有日期計算的短暫結束並結束混淆。使用真正簡潔的swift語法。 Will be available as Datez, part of Kitz