4

dateByAddingComponents が夏時間を処理する方法の例外を理解するのに苦労しています。私は Apple Date and Time Programming Guide を読み、期待される dateByAddingComponents が DST の変更を考慮に入れていることを確認しました。ただし、DST の変更日には、うまくいきません。

コードは次のとおりです。

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 に 6 時間を追加して (私のユース ケースでは午前 12 時)、午前 6 時 (localTimeZone) になることを期待しています。

dateByAddingComponents の使用は DST 変更の前後の両方で機能しますが、11 月 3 日日曜日の DST 変更の日に、午前 5 時になります。

理論: 実際には 11 月 3 日の日曜日に午前 2 時が 2 回あるので、それを考慮する必要があるのでしょうか? その場合、DST の変更の実際の日を説明するロジックを記述し、必要に応じて daylightSavingTimeOffsetForDate を使用してオフセットを追加する必要があります。

私は何が欠けていますか???

編集: わかりました、今日が DST の変更であるかどうかを判断し、1 時間のオフセットを追加/削除することで問題を回避することにしました。ここで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;
    }
}
//////
4

1 に答える 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")
}

...しかし、ちょっと待ってください!@RobNapier からの知恵の言葉は、正しい道を示してくれました... 6 時間を追加しています。開始日が真夜中の場合、彼らは時計を午前 2 時に調整して午前 1 時に戻すため、カレンダーに従って失われた時間を忘れて過ごします。

ですから... イベントが本当に真夜中に始まり、午前 6 時に終わる場合は、Rob の言葉に耳を傾け、期間を使用しないでください。正確な日付コンポーネントを使用してください。その期間は実際には7時間だからです。


おもしろいことに、テストが気になったときにそのことに気づきました。同じ日にのみオフセットを追加する必要があるのはなぜですか? 日を変えると「うまくいく」のはなぜですか?...ライト電球

そして、ところで、私のテストは、すべての日付計算の短所を終わらせ、混乱を終わらせる必要があるライブラリをロールアウトするためのものです..迅速な非常に簡潔な構文。Kitz の一部である Datez として提供されます

于 2015-11-07T21:00:06.613 に答える