Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I generate convenient date ranges based on a given NSDate?

Tags:

cocoa

nsdate

I'm creating a report generator in Cocoa, and I need to produce convenient date ranges such as "Today", "This Week", "This Month", "This Year", etc.

Is there a good way to do this? Here is my skeleton thus far:

@interface DateRange : NSObject
{
    NSDate startDate;
    NSDate endDate;
}

@property (nonatomic, retain) NSDate * startDate;
@property (nonatomic, retain) NSDate * endDate;

+ (DateRange *)rangeForDayContainingDate:(NSDate *)date;
+ (DateRange *)rangeForWeekContainingDate:(NSDate *)date;
+ (DateRange *)rangeForMonthContainingDate:(NSDate *)date;
+ (DateRange *)rangeForYearContainingDate:(NSDate *)date;

@end

Some example use cases would be as follows:

DateRange * thisWeek = [DateRange rangeForWeekContainingDate:[NSDate date]];
DateRange * thisYear = [DateRange rangeForYearContainingDate:[NSDate date]];

Essentially, I want the returned DateRange object to contain the start and end dates for the week, month or year surrounding the target date. For example (in pseudocode):

NSDate * today = [August 25, 2009];
DateRange * thisWeek = [DateRange rangeForWeekContainingDate:today];
assert(thisWeek.startDate == [August 23, 3009]);
assert(thisWeek.endDate == [August 29, 3009]);

update:

I was able to get this working thanks to the answer provided by Kendall Helmstetter Geln. Here is the complete method for a one-week range:

+ (DateRange *)rangeForWeekContainingDate:(NSDate *)date
{
    DateRange * range = [[self alloc] init];

    // start of the week
    NSDate * firstDay;
    [[self calendar] rangeOfUnit:NSWeekCalendarUnit
                       startDate:&firstDay
                        interval:0
                         forDate:date];
    [range setStartDate:firstDay];

    // end of the week
    NSDateComponents * oneWeek = [[NSDateComponents alloc] init];
    [oneWeek setWeek:1];
    [range setEndDate:[[self calendar] dateByAddingComponents:oneWeek
                                                       toDate:firstDay
                                                      options:0]];
    [oneWeek release];

    return [range autorelease];
}
like image 636
e.James Avatar asked Aug 25 '09 20:08

e.James


People also ask

How do I compare NSDate?

We can also use the earlierDate: and laterDate: methods of the NSDate class: NSDate *earlierDate = [date1 earlierDate:date2];//Returns the earlier of 2 dates. Here earlierDate will equal date2. NSDate *laterDate = [date1 laterDate:date2];//Returns the later of 2 dates.

What is NSDate?

NSDate objects encapsulate a single point in time, independent of any particular calendrical system or time zone. Date objects are immutable, representing an invariant time interval relative to an absolute reference date (00:00:00 UTC on 1 January 2001).

How do you create a date variable in Swift?

Creating a Date and Time in Swift For this you can use DateComponents to specify the components and then Calendar to create the date. The Calendar gives the Date context. Otherwise, how would it know what time zone or calendar to express it in? Other time zone abbreviations can be found here.

How do I print the time in Objective C?

NSDate *date = [NSDate date]; to get the current date and time. Use NSDateFormatter to format it. See this link for a how-to on date and time formatting.


1 Answers

For the sake of completeness, here is my final solution (with thanks to Kendall Helmstetter Geln and jbrennan):

+ (NSCalendar *)calendar
{
    NSCalendar * gregorian = [[NSCalendar alloc]
                              initWithCalendarIdentifier:NSGregorianCalendar];
    return [gregorian autorelease];
}

////////////////////////////////////////////////////////////////

+ (NSDateComponents *)singleComponentOfUnit:(NSCalendarUnit)unit
{
    NSDateComponents * component = [[NSDateComponents alloc] init];

    switch (unit)
    {
        case NSDayCalendarUnit: [component setDay:1]; break;
        case NSWeekCalendarUnit: [component setWeek:1]; break;
        case NSMonthCalendarUnit: [component setMonth:1]; break;
        case NSYearCalendarUnit: [component setYear:1]; break;
    }

    return [component autorelease];
}

////////////////////////////////////////////////////////////////

+ (WM_DateRange *)rangeForUnit:(NSCalendarUnit)unit
               surroundingDate:(NSDate *)date
{
    WM_DateRange * range = [[self alloc] init];

    // start of the period
    NSDate * firstDay;
    [[self calendar] rangeOfUnit:unit
                       startDate:&firstDay
                        interval:0
                         forDate:date];
    [range setStartDate:firstDay];

    // end of the period
    [range setEndDate:[[self calendar]
        dateByAddingComponents:[self singleComponentOfUnit:unit]
                        toDate:firstDay
                       options:0]];

    return [range autorelease];
}

////////////////////////////////////////////////////////////////

+ (WM_DateRange *)rangeForDayContainingDate:(NSDate *)date
{ return [self rangeForUnit:NSDayCalendarUnit surroundingDate:date]; }

+ (WM_DateRange *)rangeForWeekContainingDate:(NSDate *)date
{ return [self rangeForUnit:NSWeekCalendarUnit surroundingDate:date]; }

+ (WM_DateRange *)rangeForMonthContainingDate:(NSDate *)date
{ return [self rangeForUnit:NSMonthCalendarUnit surroundingDate:date]; }

+ (WM_DateRange *)rangeForYearContainingDate:(NSDate *)date
{ return [self rangeForUnit:NSYearCalendarUnit surroundingDate:date]; }

////////////////////////////////////////////////////////////////

- (void)dealloc
{
    [endDate release];
    [startDate release];
    [super dealloc];
}
like image 139
e.James Avatar answered Sep 22 '22 07:09

e.James