Количество дней между двумя NSDates

Как определить количество дней между двумя значениями NSDate (учитывая также время)?

Значения NSDate находятся в любой форме [NSDate date].

В частности, когда пользователь входит в неактивное состояние в моем приложении iPhone, я сохраняю следующее значение:

exitDate = [NSDate date];

И когда они открывают приложение, я получаю текущее время:

NSDate *now = [NSDate date];

Теперь я хотел бы реализовать следующее:

-(int)numberOfDaysBetweenStartDate:exitDate andEndDate:now

Ответ 1

Здесь реализация, которую я использовал для определения количества календарных дней между двумя датами:

+ (NSInteger)daysBetweenDate:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime
{
    NSDate *fromDate;
    NSDate *toDate;

    NSCalendar *calendar = [NSCalendar currentCalendar];

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

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

    return [difference day];
}

EDIT:

Фантастическое решение выше, здесь версия Swift ниже как расширение на NSDate:

extension NSDate {
  func numberOfDaysUntilDateTime(toDateTime: NSDate, inTimeZone timeZone: NSTimeZone? = nil) -> Int {
    let calendar = NSCalendar.currentCalendar()
    if let timeZone = timeZone {
      calendar.timeZone = timeZone
    }

    var fromDate: NSDate?, toDate: NSDate?

    calendar.rangeOfUnit(.Day, startDate: &fromDate, interval: nil, forDate: self)
    calendar.rangeOfUnit(.Day, startDate: &toDate, interval: nil, forDate: toDateTime)

    let difference = calendar.components(.Day, fromDate: fromDate!, toDate: toDate!, options: [])
    return difference.day
  }
}

Немного разворачивается сила, которую вы можете удалить в зависимости от вашего варианта использования.

Вышеупомянутое решение также работает для часовых поясов, отличных от текущего часового пояса, идеально подходит для приложения, которое показывает информацию о местах по всему миру.

Ответ 2

Вот лучшее решение, которое я нашел. Кажется, используется одобренный Apple метод для определения любого количества единиц между NSDates.

- (int)daysBetween:(NSDate *)dt1 and:(NSDate *)dt2 {
    NSUInteger unitFlags = NSDayCalendarUnit;
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; 
    NSDateComponents *components = [calendar components:unitFlags fromDate:dt1 toDate:dt2 options:0];
    return [components day]+1;
}

например. если вам нужны месяцы, тогда вы можете включить "NSMonthCalendarUnit" как unitFlag.

Чтобы отблагодарить оригинального блоггера, я нашел эту информацию здесь (хотя была небольшая ошибка, которую я исправил выше): http://cocoamatic.blogspot.com/2010/09/nsdate-number-of-days-between-two-dates.html?showComment=1306198273659#c6501446329564880344

Ответ 3

Обновление Swift 3.0

extension Date {

    func differenceInDaysWithDate(date: Date) -> Int {
        let calendar = Calendar.current

        let date1 = calendar.startOfDay(for: self)
        let date2 = calendar.startOfDay(for: date)

        let components = calendar.dateComponents([.day], from: date1, to: date2)
        return components.day ?? 0
    }
}

Обновление Swift 2.0

extension NSDate {

    func differenceInDaysWithDate(date: NSDate) -> Int {
        let calendar: NSCalendar = NSCalendar.currentCalendar()

        let date1 = calendar.startOfDayForDate(self)
        let date2 = calendar.startOfDayForDate(date)

        let components = calendar.components(.Day, fromDate: date1, toDate: date2, options: [])
        return components.day
    }

}

Исходное решение

Другое решение в Swift.

Если ваша цель - получить точное число дней между двумя датами, вы можете обойти эту проблему следующим образом:

// Assuming that firstDate and secondDate are defined
// ...

var calendar: NSCalendar = NSCalendar.currentCalendar()

// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDayForDate(firstDate)
let date2 = calendar.startOfDayForDate(secondDate)

let flags = NSCalendarUnit.DayCalendarUnit
let components = calendar.components(flags, fromDate: date1, toDate: date2, options: nil)

components.day  // This will return the number of day(s) between dates

Ответ 4

Я использую это как метод категории для класса NSDate

// returns number of days (absolute value) from another date (as number of midnights beween these dates)
- (int)daysFromDate:(NSDate *)pDate {
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
    NSInteger startDay=[calendar ordinalityOfUnit:NSCalendarUnitDay
                                           inUnit:NSCalendarUnitEra
                                          forDate:[NSDate date]];
    NSInteger endDay=[calendar ordinalityOfUnit:NSCalendarUnitDay
                                         inUnit:NSCalendarUnitEra
                                        forDate:pDate];
    return abs(endDay-startDay);
}

Ответ 5

Мне понадобилось количество дней между двумя датами, включая день начала. например дни между 14-2-2012 и 16-2-2012 годами приведут к результату 3.

+ (NSInteger)daysBetween:(NSDate *)dt1 and:(NSDate *)dt2 {
        NSUInteger unitFlags = NSDayCalendarUnit;
        NSCalendar* calendar = [NSCalendar currentCalendar];
        NSDateComponents *components = [calendar components:unitFlags fromDate:dt1 toDate:dt2 options:0];
        NSInteger daysBetween = abs([components day]);
    return daysBetween+1;
}

Обратите внимание, что не имеет значения, в каком порядке вы указываете даты. Он всегда будет возвращать положительное число.

Ответ 6

NSDate *lastDate = [NSDate date];
NSDate *todaysDate = [NSDate date];
NSTimeInterval lastDiff = [lastDate timeIntervalSinceNow];
NSTimeInterval todaysDiff = [todaysDate timeIntervalSinceNow];
NSTimeInterval dateDiff = lastDiff - todaysDiff;

dateDiff будет тогда числом секунд между двумя датами. Просто делите на количество секунд в день.

Ответ 7

@Брайан

Брайан отвечает, в то время как хорошо, только вычисляет разницу в днях с точки зрения 24-часовых кусков, но не различий календарного дня. Например, 23:59 24 декабря находится всего в 1 минуте от Рождества, для многих приложений, которые считаются в один прекрасный день. Функция Brian daysBetween вернет 0.

Заимствуя оригинальную реализацию Брайана и начало/конец дня, я использую следующее в своей программе: (NSDate начало дня и конца дня)

- (NSDate *)beginningOfDay:(NSDate *)date
{
    NSCalendar *cal = [NSCalendar currentCalendar];
    NSDateComponents *components = [cal components:( NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
    [components setHour:0];
    [components setMinute:0];
    [components setSecond:0];
    return [cal dateFromComponents:components];
}

- (NSDate *)endOfDay:(NSDate *)date
{
    NSCalendar *cal = [NSCalendar currentCalendar];
    NSDateComponents *components = [cal components:( NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
    [components setHour:23];
    [components setMinute:59];
    [components setSecond:59];
    return [cal dateFromComponents:components];
}

- (int)daysBetween:(NSDate *)date1 and:(NSDate *)date2 {
    NSDate *beginningOfDate1 = [self beginningOfDay:date1];
    NSDate *endOfDate1 = [self endOfDay:date1];
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *beginningDayDiff = [calendar components:NSDayCalendarUnit fromDate:beginningOfDate1 toDate:date2 options:0];
    NSDateComponents *endDayDiff = [calendar components:NSDayCalendarUnit fromDate:endOfDate1 toDate:date2 options:0];
    if (beginningDayDiff.day > 0)
        return beginningDayDiff.day;
    else if (endDayDiff.day < 0)
        return endDayDiff.day;
    else {
        return 0;
    }
}

Ответ 8

Другой подход:

NSDateFormatter* dayFmt = [[NSDateFormatter alloc] init];
[dayFmt setTimeZone:<whatever time zone you want>];
[dayFmt setDateFormat:@"g"];
NSInteger firstDay = [[dayFmt stringFromDate:firstDate] integerValue];    
NSInteger secondDay = [[dayFmt stringFromDate:secondDate] integerValue];
NSInteger difference = secondDay - firstDay;

Преимущество над схемой timeIntervalSince... заключается в том, что часовой пояс можно принять во внимание, и нет никакой двусмысленности с интервалами на несколько секунд меньше или длиннее одного дня.

И немного более компактный и менее запутанный, чем подходы NSDateComponents.

Ответ 9

Просто добавьте ответ для тех, кто посещает эту страницу, пытаясь сделать это в Swift. Подход практически такой же.

private class func getDaysBetweenDates(startDate:NSDate, endDate:NSDate) -> NSInteger {

    var gregorian: NSCalendar = NSCalendar.currentCalendar();
    let flags = NSCalendarUnit.DayCalendarUnit
    let components = gregorian.components(flags, fromDate: startDate, toDate: endDate, options: nil)

    return components.day
}

Этот ответ был найден здесь, в разделе обсуждения следующего метода:

components(_:fromDate:toDate:options:)

Ответ 10

Вот реализация функции Брайана в Swift:

class func daysBetweenThisDate(fromDateTime:NSDate, andThisDate toDateTime:NSDate)->Int?{

    var fromDate:NSDate? = nil
    var toDate:NSDate? = nil

    let calendar = NSCalendar.currentCalendar()

    calendar.rangeOfUnit(NSCalendarUnit.DayCalendarUnit, startDate: &fromDate, interval: nil, forDate: fromDateTime)

    calendar.rangeOfUnit(NSCalendarUnit.DayCalendarUnit, startDate: &toDate, interval: nil, forDate: toDateTime)

    if let from = fromDate {

        if let to = toDate {

            let difference = calendar.components(NSCalendarUnit.DayCalendarUnit, fromDate: from, toDate: to, options: NSCalendarOptions.allZeros)

            return difference.day
        }
    }

    return nil
}

Ответ 11

Вы имеете в виду календарные или 24-часовые периоды? т.е. во вторник в 9 вечера за день до среды в 6 утра или менее одного дня?

Если вы имеете в виду первое, это немного сложно, и вам придётся прибегать к манипуляциям через NSCalendar и NSDateComponent, которые я не помню с головы.

Если вы имеете в виду последний, просто получите временные интервалы дат с даты ссылки, вычтите один из другого и разделите на 24 часа (24 * 60 * 60), чтобы получить приблизительный интервал, секунды прыжка не включены.

Ответ 12

Почему не просто:

int days = [date1 timeIntervalSinceDate:date2]/24/60/60;

Ответ 13

Получил один, не уверен, что это именно то, что вы хотите, но это может помочь некоторым из вас (помогло мне!!)

Моя цель состояла в том, чтобы знать, если между двумя датами (разница менее 24 часов) у меня был день "overday" + 1:

Я сделал следующее (немного архаичное, я признаю)

NSDate *startDate = ...
NSDate *endDate = ...

NSDate уже отформатирован другим NSDateFormatter (этот только для этой цели:)

NSDateFormatter *dayFormater = [[NSDateFormatter alloc]init];
[dayFormater setDateFormat:@"dd"];

int startDateDay = [[dayFormater stringFromDate:startDate]intValue];

int endDateDay = [[dayFormater stringFromDate:dateOn]intValue];

if (endDateDay > startDateDay) {
    NSLog(@"day+1");
} else {
    NSLog(@"same day");
}

возможно, что-то вроде этого уже существует, но не нашел его

Тим

Ответ 14

Решение, которое я нашел, было:

+(NSInteger)getDaysDifferenceBetween:(NSDate *)dateA and:(NSDate *)dateB {

  if ([dateA isEqualToDate:dateB]) 
    return 0;

  NSCalendar * gregorian = 
        [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];



  NSDate * dateToRound = [dateA earlierDate:dateB];
  int flags = (NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit);
  NSDateComponents * dateComponents = 
         [gregorian components:flags fromDate:dateToRound];


  NSDate * roundedDate = [gregorian dateFromComponents:dateComponents];

  NSDate * otherDate = (dateToRound == dateA) ? dateB : dateA ;

  NSInteger diff = abs([roundedDate timeIntervalSinceDate:otherDate]);

  NSInteger daysDifference = floor(diff/(24 * 60 * 60));

  return daysDifference;
}

Здесь я эффективно округлю первое свидание, чтобы начать с начала дня, а затем вычислить разницу, как предлагает Джонатан выше...

Ответ 15

Я опубликовал класс/библиотеку с открытым исходным кодом, чтобы сделать это.

Посмотрите RelativeDateDescriptor, который можно использовать для получения разницы во времени следующим образом:

RelativeDateDescriptor *descriptor = [[RelativeDateDescriptor alloc] initWithPriorDateDescriptionFormat:@"%@ ago" postDateDescriptionFormat:@"in %@"];

// date1: 1st January 2000, 00:00:00
// date2: 6th January 2000, 00:00:00
[descriptor describeDate:date2 relativeTo:date1]; // Returns '5 days ago'
[descriptor describeDate:date1 relativeTo:date2]; // Returns 'in 5 days'

Ответ 16

Зачем использовать следующий метод NSDate:

- (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate

Это вернет число секунд между двумя датами, и вы можете разделить на 86 400, чтобы получить количество дней!