iPhone - 获取两个日期之间的天数

发布于 2024-09-06 06:01:15 字数 3953 浏览 2 评论 0原文

我正在为 iPhone 编写 GTD 应用程序。对于到期任务,我想显示“明天到期”或“昨天到期”或“7 月 18 日到期”之类的内容。显然,即使距离任务还有不到 24 小时,我也需要显示“明天”(例如,用户在周六晚上 11 点查看并看到周日上午 8 点有任务)。因此,我编写了一个方法来获取两个日期之间的天数。这是代码...

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

如您所见,该方法返回不正确的结果。它应该返回 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;
}

I'm writing a GTD app for the iPhone. For the due tasks, I want to display something like "Due tomorrow" or "Due yesterday" or "Due July 18th". Obviously, I need to display "Tomorrow" even if the task is less than 24 hours away (e.g. the user checks at 11pm on Saturday and sees there's a task on Sunday at 8am). So, I wrote a method to get the number of days in between two dates. Here's the code...

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

... and here's the output:

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

As you can see, the method returns incorrect results. It should have returned 1 as the number of days between the two days. What am I doing wrong here?

EDIT: I wrote another method. I haven't done extensive unit tests, but so far it seems to work:

+ (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;
}

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

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

发布评论

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

评论(6

别忘他 2024-09-13 06:01:15

来自 components:fromDate:toDate:options: 的文档:

如果没有请求足够小的单位来保存差异的完整精度,则结果是有损的。

由于差异小于一整天,因此它正确返回 0 天的结果。

From the docs for components:fromDate:toDate:options::

The result is lossy if there is not a small enough unit requested to hold the full precision of the difference.

Since the difference is less than a full day, it correctly returns a result of 0 days.

看海 2024-09-13 06:01:15

如果您只关心明天或昨天与某个特定日期,那么您可以节省大量工作,只需测试这些日期是否仅相隔一个日历日。

为此,请比较日期以找出哪个日期较早,哪个日期较晚(如果它们比较相等,则使用该结果),然后测试较早日期后 1 天是否会生成具有相同年、月和日期的日期。每月的某一天作为较晚的日期。


如果您确实想确切地知道从一个日期到另一个日期有多少个日历日:

  1. 向日历发送一条 components:fromDate: 消息以获取该日期的年、月和日- 第一次约会的月份。
  2. 与 #1 相同,但用于第二次约会。
  3. 如果两个日期属于同一年同月,则从另一个日期中减去一个月中的某一天,然后传递给 abs (请参阅 abs(3)) 取绝对值。
  4. 如果不是同一年同月,请测试它们是否在相邻月份(例如,2010 年 12 月至 2011 年 1 月,或 2010 年 6 月至 2010 年 7 月)。如果是,请添加较早日期所在月份的天数(您可以通过向日历发送 rangeOfUnit:inUnit:forDate: 消息,传递 NSDayCalendarUnit 和 < code>NSMonthCalendarUnit)到较晚日期的月份日期,然后将该结果与较早日期的月份日期进行比较。

    例如,在比较 2010 年 12 月 31 日和 2011 年 1 月 1 日时,您首先要确定它们属于相邻月份,然后将 31(2010 年 12 月的天数)加到 1(日期) 2011 年 1 月 1 日),然后从该总和中减去 31(2010 年 12 月 31 日)。由于差值为 1,因此较早的日期比较晚的日期早一天。

    比较 2010-12-30 和 2011-01-02 时,您会确定它们是相邻月份,然后添加 31(2010-12 中的天数) ) 到 2(2011-01-02 的月份日期),然后从该总和中减去 30(2010-12-30 的月份日期)。 33 减 30 等于 3,因此这些日期相隔三个日历日。


不管怎样,我强烈建议至少为这段代码编写单元测试。我发现日期处理代码是最有可能存在细微错误的代码之一,这些错误每年只出现两次。

If all you care about is tomorrow or yesterday vs. a specific date, then you can save yourself a lot of work and just test whether the dates are only one calendar day apart.

To do that, compare the dates to find which is earlier and which is later (and if they compare equal, bail out with that result), then test whether 1 day after the earlier date produces a date with the same year, month, and day-of-month as the later date.


If you really do want to know exactly how many calendar days there are from one date to the other:

  1. Send the calendar a components:fromDate: message to get the year, month, and day-of-the-month of the first date.
  2. Same as #1, but for the second date.
  3. If the two dates are in the same year and month, subtract one day-of-month from the other and pass to abs (see abs(3)) to take the absolute value.
  4. If they are not in the same year and month, test whether they are in adjacent months (e.g., December 2010 to January 2011, or June 2010 to July 2010). If they are, add the number of days in the earlier date's month (which you can obtain by sending the calendar a rangeOfUnit:inUnit:forDate: message, passing NSDayCalendarUnit and NSMonthCalendarUnit, respectively) to the day-of-month of the later date, then compare that result to the earlier date's day-of-month.

    For example, when comparing 2010-12-31 to 2011-01-01, you would first determine that these are in adjacent months, then add 31 (number of days in 2010-12) to 1 (day-of-month of 2011-01-01), then subtract 31 (day-of-month of 2010-12-31) from that sum. Since the difference is 1, the earlier date is one day before the later date.

    When comparing 2010-12-30 to 2011-01-02, you would determine that they are in adjacent months, then add 31 (days in 2010-12) to 2 (day-of-month of 2011-01-02), then subtract 30 (day-of-month of 2010-12-30) from that sum. 33 minus 30 is 3, so these dates are three calendar days apart.


Either way, I strongly suggest writing unit tests at least for this code. I've found that date-handling code is among the most likely to have subtle bugs that only manifest, say, twice a year.

七堇年 2024-09-13 06:01:15

您可以尝试的一件事是使用 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 的比较方法一起使用效果会更好,尽管我自己还没有测试过。

One thing you might try is using rangeOfUnit: to zero out hours, minutes and seconds from the start and end dates.

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];

In this example start will be 2010-06-19 00:00:00 -0400, end will be 2010-06-20 00:00:00 -0400. I'd imagine this would work better with NSCalendar's comparison methods, although I haven't tested it myself.

自在安然 2024-09-13 06:01:15

我正在使用这段代码,它运行得很好:

- (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);
}

I am using this piece of code, it is working very well:

- (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);
}
倥絔 2024-09-13 06:01:15

这是我过去使用过的功能
它在 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;
}

Here is the function I've used in the past
its defined in a category on 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;
}
或十年 2024-09-13 06:01:15
 -(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];

}

 -(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];

}

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