Stan92
Stan92

Reputation: 453

Determine the number of months between two dates using Cocoa

How do I calculate the number of months between two dates using Cocoa?

Thanks, Stan

Upvotes: 9

Views: 8232

Answers (6)

ClockWise
ClockWise

Reputation: 1519

Here's the objective-c version of @eliocs answer updated for iOS11:

- (NSInteger)monthsSince:(NSDate *)from to:(NSDate *)to {
    NSDateComponents *fromComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitMonth | NSCalendarUnitYear fromDate:from];
    NSDateComponents *toComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitMonth | NSCalendarUnitYear fromDate:to];

    return ((toComponents.year - fromComponents.year) * 12) + (toComponents.month - fromComponents.month);
}

Upvotes: 0

eliocs
eliocs

Reputation: 18827

I had to calculate the months changes between two dates. This means that from the 31st of January to the 1st of February 1 month has passed were NSCalendar.components will return 0.

import UIKit

func monthsSince(from: NSDate, to: NSDate) -> Int {
    let fromComponents = NSCalendar.currentCalendar().components([.Month, .Year], fromDate: from)
    let toComponents = NSCalendar.currentCalendar().components([.Month, .Year], fromDate: to)

    return ((toComponents.year - fromComponents.year) * 12) + (toComponents.month - fromComponents.month)
}

let tests = [
    (from: (day: 1, month: 1, year: 2016), to: (day: 31, month: 1, year: 2016), result: 0),
    (from: (day: 22, month: 1, year: 2016), to: (day: 5, month: 2, year: 2016), result: 1),
    (from: (day: 22, month: 12, year: 2015), to: (day: 1, month: 1, year: 2016), result: 1),
    (from: (day: 1, month: 1, year: 2016), to: (day: 1, month: 2, year: 2016), result: 1),
    (from: (day: 1, month: 1, year: 2016), to: (day: 1, month: 3, year: 2016), result: 2)
]

for test in tests {
    let from = NSCalendar.currentCalendar().dateWithEra(1, year: test.from.year, month: test.from.month, day: test.from.day, hour: 0, minute: 0, second: 0, nanosecond: 0)!
    let to = NSCalendar.currentCalendar().dateWithEra(1, year: test.to.year, month: test.to.month, day: test.to.day, hour: 0, minute: 0, second: 0, nanosecond: 0)!

    if monthsSince(from, to: to) == test.result {
        print("Test \(test), Passed!")
    } else {
        print("Test \(test), Failed!")
    }
}

Upvotes: 2

JonD
JonD

Reputation:

NSInteger month = [[[NSCalendar currentCalendar] components: NSCalendarUnitMonth
                                                   fromDate: yourFirstDate
                                                     toDate: yourSecondDate
                                                    options: 0] month];

Upvotes: 38

Tobias M
Tobias M

Reputation: 1338

To get an answer that includes the fraction of a month the following can be used:

- (NSNumber *)numberOfMonthsBetweenFirstDate:(NSDate *)firstDate secondDate:(NSDate *)secondDate {

if ([firstDate compare:secondDate] == NSOrderedDescending) {
    return nil;
}

NSCalendar *calendar = [NSCalendar currentCalendar];

NSDateComponents *firstDateComponents = [calendar components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit
                                                    fromDate:firstDate];

NSInteger firstDay = [firstDateComponents day];

NSRange rangeOfFirstMonth = [calendar rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:firstDate];
NSUInteger numberOfDaysInFirstMonth = rangeOfFirstMonth.length;
CGFloat firstMonthFraction = (CGFloat)(numberOfDaysInFirstMonth - firstDay) / (CGFloat)numberOfDaysInFirstMonth;

// last month component
NSDateComponents *lastDateComponents = [calendar components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit
                                                              fromDate:secondDate];
NSInteger lastDay = [lastDateComponents day];
NSRange rangeOfLastMonth = [calendar rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:secondDate];
NSUInteger numberOfDaysInLastMonth = rangeOfLastMonth.length;
CGFloat lastMonthFraction = (CGFloat)(lastDay) / (CGFloat)numberOfDaysInLastMonth;

// Check if the two dates are within the same month
if (firstDateComponents.month == lastDateComponents.month
        && firstDateComponents.year == lastDateComponents.year) {
    NSDateComponents *dayComponents = [calendar components:NSDayCalendarUnit
                                                           fromDate:firstDate
                                                             toDate:secondDate
                                                            options:0];

    NSRange rangeOfMonth = [calendar rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:firstDate];
    NSUInteger numberOfDaysInMonth = rangeOfMonth.length;
    return [NSNumber numberWithFloat:(CGFloat)dayComponents.day / (CGFloat)numberOfDaysInMonth];
}

//  Start date of the first complete month
NSDateComponents *firstDateFirstDayOfNextMonth = firstDateComponents;
firstDateFirstDayOfNextMonth.month +=1;
firstDateFirstDayOfNextMonth.day = 1;

// First day of the last month
NSDateComponents *secondDateFirstDayOfMonth = lastDateComponents;
secondDateFirstDayOfMonth.day = 1;

NSInteger numberOfMonths = secondDateFirstDayOfMonth.month - firstDateFirstDayOfNextMonth.month
        + (secondDateFirstDayOfMonth.year - firstDateFirstDayOfNextMonth.year) * 12;

return [NSNumber numberWithFloat:(firstMonthFraction + numberOfMonths + lastMonthFraction)]; 
}

Upvotes: 5

Gamma-Point
Gamma-Point

Reputation: 1514

components:fromDate:toDate:options doesn't work correctly for following example:

begin date: Jan 01, 2012 end date: March 31, 2012.

num of months using above method = 2

The correct answer should be 3 months.

I am using the long way of calculations as follows: 1. Find number of days in the beginning month. Add it to the fraction part of the month. If the first date is the beginning of the month, count it as full month. 2. find the number of days in the end month. Add it to the fraction part of the monthIf the date is the last of the month count it as a full month. 3. Find the whole/full months between the 2 dates and add to the integer part of the month. 4. Add the integer & fraction parts of the months to get a correct value.

Upvotes: 1

ennuikiller
ennuikiller

Reputation: 46965

Look at NSCalendars components:fromDate:toDate:options method. It allows you to subtract dates and then extract the months property value

Upvotes: 14

Related Questions