syedfa
syedfa

Reputation: 2819

Given an NSDate, find the last day of fourth prior month

I am trying to calculate an NSDate object based on the current date. If the current date is April 1st, 2015, I need to generate the date, December 31, 2014. If the current date is April 30th, 2015, I STILL need to generate the date, December 31, 2014. If however, it is May 1st, 2015, I need to generate January 31st, 2015. In other words, whatever month I am in, I need the date of the end of the month, from four months ago, regardless of where I am in the current month.

The code I have thus far is:

    NSCalendar *theCalendar = [NSCalendar currentCalendar];
    NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
    [dayComponent setDay:-90];
    NSDate *nextDate = [theCalendar dateByAddingComponents:dayComponent toDate:[NSDate date] options:0];

    NSLog(@"The date I am getting is: %@", nextDate);

The above code gives me the date value of exactly 90 days prior to the current date, but I need the date to always be the end of the month that is 4 months earlier.

Upvotes: 0

Views: 185

Answers (2)

rob mayoff
rob mayoff

Reputation: 385860

As you've already discovered, you need a starting date and a calendar:

NSDate *startingDate = [NSDate date];
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];

You'll need the components of the current date but only down to the current month, because you don't care about the specific day within the month:

NSDateComponents *components = [calendar
    components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth
    fromDate:startingDate];

You say you want the last day of the fourth prior month. Since months have different numbers of days, the last day varies depending on the month. But all months have first days, and those first days are always numbered 1. So it's easiest to compute “the last day of the fourth prior month” by first going back three months:

components.month -= 3;

Then, go one day prior to that month:

components.day = -1;

Finally, you need to get clear in your head that an NSDate represents an instant in time, but a day (like “April 1st, 2015”) is an interval of time, starting and ending at specific instants. If you're going to represent a whole day using an NSDate, you're going to be storing one instant within that interval. You don't want to store the first or last instant (which will both be midnights); that causes problems for some days in some time zones. Instead, use noon as your instant:

components.hour = 12;

Now you're ready to ask the calendar for a new NSDate:

NSDate *lastDayOfFourthPriorMonth = [calendar dateFromComponents:components];

Upvotes: 2

idStar
idStar

Reputation: 10804

You want to get familiar with NSCalendar and NSDateComponents. If you read those two API documents, you will be able to assemble the answer.

Conceptually, you want to use NSCalendar components:fromDate: to get the components (day, month, year) of the current date.

You will then walk that month value back 4 months. Now if that wraps past January, you know you need to determine how much, so that you adjust and stay within months [1..12]. Further, you'll need to decrement the year at that point too.

Knowing the month, you can find the last day of that month through several means; the crudest of which is to maintain your own enum...but there's probably a better way using NSCalendar that will also account for February in leap years.

At the end, you can build the resultant date from the components you've assembled, using the NSCalendar method dateFromComponents:.

See @rob mayoff's excellent answer which is a more concrete realization of this theory and IMO, the correct answer.

Upvotes: 1

Related Questions