5

iPhone 用の GTD アプリを作成しています。期限のタスクについては、「明日期限」、「昨日期限」、「7 月 18 日期限」などを表示したいと考えています。明らかに、タスクが 24 時間以内であっても「明日」と表示する必要があります (たとえば、ユーザーが土曜日の午後 11 時にチェックすると、日曜日の午前 8 時にタスクがあることがわかります)。そこで、2 つの日付の間の日数を取得するメソッドを作成しました。これがコードです...

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd-HH-mm"];

NSDate *nowDate = [dateFormatter dateFromString:@"2010-01-01-15-00"];
NSDate *dueDate = [dateFormatter dateFromString:@"2010-01-02-14-00"];

NSLog(@"NSDate *nowDate = %@", nowDate);
NSLog(@"NSDate *dueDate = %@", dueDate);

NSCalendar *calendar = [NSCalendar currentCalendar];

NSDateComponents *differenceComponents = [calendar components:(NSDayCalendarUnit)
                                                     fromDate:nowDate
                                                       toDate:dueDate
                                                      options:0];

NSLog(@"Days between dates: %d", [differenceComponents day]);

...そしてここに出力があります:

NSDate *nowDate = 2010-01-01 15:00:00 -0700
NSDate *dueDate = 2010-01-02 14:00:00 -0700
Days between dates: 0

ご覧のとおり、このメソッドは間違った結果を返します。2 日間の日数として 1 が返されるはずです。ここで何が間違っていますか?

編集:別の方法を書きました。私は大規模な単体テストを行っていませんが、これまでのところうまくいくようです:

+ (NSInteger)daysFromDate:(NSDate *)fromDate inTimeZone:(NSTimeZone *)fromTimeZone untilDate:(NSDate *)toDate inTimeZone:(NSTimeZone *)toTimeZone {

    NSCalendar *calendar = [NSCalendar currentCalendar];
    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;

    [calendar setTimeZone:fromTimeZone];
    NSDateComponents *fromDateComponents = [calendar components:unitFlags fromDate:fromDate];

    [calendar setTimeZone:toTimeZone];
    NSDateComponents *toDateComponents = [calendar components:unitFlags fromDate:toDate];

    [calendar setTimeZone:[NSTimeZone defaultTimeZone]];
    NSDate *adjustedFromDate = [calendar dateFromComponents:fromDateComponents];
    NSDate *adjustedToDate = [calendar dateFromComponents:toDateComponents];

    NSTimeInterval timeIntervalBetweenDates = [adjustedToDate timeIntervalSinceDate:adjustedFromDate];
    NSInteger daysBetweenDates = (NSInteger)(timeIntervalBetweenDates / (60.0 * 60.0 * 24.0));

    NSDateComponents *midnightBeforeFromDateComponents = [[NSDateComponents alloc] init];
    [midnightBeforeFromDateComponents setYear:[fromDateComponents year]];
    [midnightBeforeFromDateComponents setMonth:[fromDateComponents month]];
    [midnightBeforeFromDateComponents setDay:[fromDateComponents day]];

    NSDate *midnightBeforeFromDate = [calendar dateFromComponents:midnightBeforeFromDateComponents];
    [midnightBeforeFromDateComponents release];

    NSDate *midnightAfterFromDate = [[NSDate alloc] initWithTimeInterval:(60.0 * 60.0 * 24.0)
                                                               sinceDate:midnightBeforeFromDate];

    NSTimeInterval timeIntervalBetweenToDateAndMidnightBeforeFromDate = [adjustedToDate timeIntervalSinceDate:midnightBeforeFromDate];
    NSTimeInterval timeIntervalBetweenToDateAndMidnightAfterFromDate = [adjustedToDate timeIntervalSinceDate:midnightAfterFromDate];

    if (timeIntervalBetweenToDateAndMidnightBeforeFromDate < 0.0) {

        // toDate is before the midnight before fromDate

        timeIntervalBetweenToDateAndMidnightBeforeFromDate -= daysBetweenDates * 60.0 * 60.0 * 24.0;

        if (timeIntervalBetweenToDateAndMidnightBeforeFromDate < 0.0)
            daysBetweenDates -= 1;
    }
    else if (timeIntervalBetweenToDateAndMidnightAfterFromDate >= 0.0) {

        // toDate is after the midnight after fromDate

        timeIntervalBetweenToDateAndMidnightAfterFromDate -= daysBetweenDates * 60.0 * 60.0 * 24.0;

        if (timeIntervalBetweenToDateAndMidnightAfterFromDate >= 0.0)
            daysBetweenDates += 1;
    }

    [midnightAfterFromDate release];

    return daysBetweenDates;
}
4

7 に答える 7

3

のドキュメントからcomponents:fromDate:toDate:options:

差の完全な精度を保持するために要求された十分に小さい単位がない場合、結果は損失を伴います。

差は 1 日未満であるため、0 日という結果が正しく返されます。

于 2010-06-19T11:55:01.207 に答える
2

この関連する質問に対する非常に良い答えを使用して、簡単に解決できます

于 2010-10-22T14:55:17.320 に答える
2

あなたが試みるかもしれないことの1つはrangeOfUnit:、開始日と終了日から時間、分、秒をゼロにすることです。

NSCalendar *calendar = [NSCalendar currentCalendar];
NSCalendarUnit range = NSDayCalendarUnit;
NSDateComponents *comps = [[NSDateComponents alloc] init];
NSDate *start = [NSDate date];
NSDate *end;

[comps setDay:1];
[calendar rangeOfUnit:range startDate:&start interval:nil forDate:start];

end = [calendar dateByAddingComponents:comps toDate:start options:0];

この例では、開始は2010-06-19 00:00:00 -0400、終了は になります2010-06-20 00:00:00 -0400。自分でテストしたことはありませんが、これは NSCalendar の比較方法でうまく機能すると思います。

于 2010-06-19T15:30:17.953 に答える
2

明日または昨日と特定の日付だけを気にする場合は、多くの作業を省いて、日付が 1 暦日しか離れていないかどうかをテストするだけで済みます。

これを行うには、日付を比較して、どちらが早いか遅いかを調べます (そして、それらが等しい場合は、その結果で救済します)。次に、早い日付の 1 日後の日付が同じ年、月、および日付になるかどうかをテストします。より後の日付としての日付。


ある日付から別の日付までの暦日数を正確に知りたい場合は、次のようにします。

  1. カレンダーにcomponents:fromDate:メッセージを送信して、最初の日付の年、月、日を取得します。
  2. #1と同じですが、2番目の日付です。
  3. 2 つの日付が同じ年と月にある場合は、一方の日を他方から減算し、 ( abs(3)absを参照) に渡して絶対値を取得します。
  4. それらが同じ年と月にない場合は、それらが隣接する月にあるかどうかをテストします (例: 2010 年 12 月から 2011 年 1 月、または 2010 年 6 月から 2010 年 7 月)。そうである場合は、前の日付の月の日数 (カレンダーにメッセージを送信し、それぞれと rangeOfUnit:inUnit:forDate:を渡すことで取得できます) を後の日付の月の日に加算し、その結果を前の日付と比較します。日付の日。NSDayCalendarUnitNSMonthCalendarUnit

    たとえば、2010 年 12 月 31 日と 2011 年 1 月 1 日を比較する場合、最初にこれらが隣接する月にあると判断し、次に 31 (2010 年 12 月の日数) を 1 (2011 年の月の日) に追加します。 -01-01)、その合計から 31 (2010 年 12 月 31 日) を引きます。差は 1 であるため、早い日付は遅い日付の 1 日前になります。

    2010 年 12 月30日と 2011 年 1 月 2 日を比較する場合、それらが隣接する月にあると判断し、31 (2010 年 12 月の日) を 2 (2011 年 1 月 2 日) に加算します。次に、その合計から 30 (2010 年 12 月 30 日) を引きます。33 ひく 30 は 3 なので、これらの日付は 3 暦日離れています。


いずれにせよ、少なくともこのコードの単体テストを作成することを強くお勧めします。日付処理コードは、たとえば年に 2 回しか現れない微妙なバグが発生する可能性が最も高いコードの 1 つであることがわかりました。

于 2010-06-19T12:26:25.047 に答える
1

私はこのコードを使用していますが、非常にうまく機能しています:

- (NSInteger)daysToDate:(NSDate*)date
{
    if(date == nil) {
        return NSNotFound;
    }
    NSUInteger otherDay = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay   inUnit:NSCalendarUnitEra forDate:date];
    NSUInteger currentDay = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:self];
    return (otherDay-currentDay);
}
于 2015-01-14T10:28:06.563 に答える
0

これは、NSDateのカテゴリで定義された過去に使用した関数です

- (int) daysToDate:(NSDate*) endDate
{
    //dates needed to be reset to represent only yyyy-mm-dd to get correct number of days between two days.
    NSDateFormatter *temp = [[NSDateFormatter alloc] init];
    [temp setDateFormat:@"yyyy-MM-dd"];
    NSDate *stDt = [temp dateFromString:[temp stringFromDate:self]];
    NSDate *endDt =  [temp dateFromString:[temp stringFromDate:endDate]];
    [temp release]; 
    unsigned int unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;
    NSCalendar *gregorian = [[NSCalendar alloc]
                             initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *comps = [gregorian components:unitFlags fromDate:stDt  toDate:endDt  options:0];
    int days = [comps day];
    [gregorian release];
    return days;
}
于 2010-06-29T15:49:24.277 に答える
0
 -(NSInteger)daysBetweenTwoDates:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime

{

NSDate *fromDate;
NSDate *toDate;

NSCalendar *calendar = [NSCalendar currentCalendar];

[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate
             interval:NULL forDate:fromDateTime];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate
             interval:NULL forDate:toDateTime];

NSDateComponents *difference = [calendar components:NSDayCalendarUnit
                                           fromDate:fromDate toDate:toDate options:0];

return [difference day];

}

于 2012-12-19T04:26:27.883 に答える