DateMonger Challenge

Here is my solution to this chapter’s challenge. I think I have used the category correctly, unless I’m missing something about the initialisation. The invalid date handling seems clunky, needs refinement, but trying to be helpful rather than just refuse badly formatted inputs!

main.m

#import <Foundation/Foundation.h>
#import "NSDate+BNRDateConvenience.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        
//Pass in some numbers for dates
        int day = 12;
        int month = 13;
        int year = 19;
        
//Create new date by passing numbers to bnr_dateFromDay:Month:andYear method in new category
        NSDate *newDate =[NSDate bnr_dateFromDay:day Month:month andYear:year];

//Set up Date formatter and print out details of new date object
        NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
        [formatter setDateStyle:NSDateFormatterFullStyle];
        [formatter setTimeStyle:NSDateFormatterFullStyle];
        
        NSLog(@"%@", [formatter stringFromDate:newDate]);
    }
    return 0;
}

NSDate+BNRDateConvenience.h

#import <Foundation/Foundation.h>

@interface NSDate (BNRDateConvenience)

+(NSDate *) bnr_dateFromDay:(int ) d Month:(int) m andYear:(int) y;


@end

NSDate+BNRDateConvenience.m


#import "NSDate+BNRDateConvenience.h"

@implementation NSDate (BNRDateConvenience)

+(NSDate *)bnr_dateFromDay:(int)d Month:(int)m andYear:(int)y
{
    
//Initialize up a calendar variable
    NSCalendar *cal=[NSCalendar currentCalendar];
    
//Initialize a date component variable
    NSDateComponents *comps = [[NSDateComponents alloc]init];
    
//Initialize a provisional date object
    NSDate *provisionalDate = [[NSDate alloc]init];
    
//Check for invalid dates, guess corrections!
    if (y<20) {
        y+=2000;
    }else if (y<100){
        y+=1900;
    }
    if (m<1) {
        m=1;
        NSLog(@"Are you sure you entered a correct date?");
    }
    if (m>12) {
        m-=12;
        NSLog(@"Are you sure you entered a correct date?");
    }
//Add month and year to provisional date
    [comps setMonth:m];
    [comps setYear:y];
    provisionalDate=[ cal dateFromComponents:comps];
    
//Get range of possible days in month of provisional date
    NSRange dayRange =[cal rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:provisionalDate];
    
//Check to see if given day is within range, return nil and message if not, else return updated date
    if (d>dayRange.length) {
         NSLog(@"Invalid date.");
        return nil;
    }
    if (d<1) {
          NSLog(@"Invalid date.");
        return nil;
    }else{
        [comps setDay:d];
        provisionalDate=[ cal dateFromComponents:comps];
        return provisionalDate;
    }
    
}

@end

Having a slight problem that I think might have to do with timezones. My date keeps coming back two hours early.
I tried reading up in the reference and have so far ended up with this:

main.m:

[code]#import <Foundation/Foundation.h>
#import “NSDate+BNRDateConvenience.h”

int main(int argc, const char * argv[])
{

@autoreleasepool {
    
    NSDate *theDateIPassedIn =[ [[NSDate alloc] init]midnightOfDateWithYear: 2013 withMonth: 5 withDay: 19];
    
    
    NSLog(@"The passed in date was %@", theDateIPassedIn);
}
return 0;

}
[/code]

NSDate+BNRDateConvenience.h:

[code]#import <Foundation/Foundation.h>

@interface NSDate (BNRDateConvenience)

  • (NSDate *)midnightOfDateWithYear: (NSInteger)y
    withMonth: (NSInteger)m
    withDay: (NSInteger)d;

@end
[/code]

and NSDate+BNRDateConvenience.m:

[code]#import “NSDate+BNRDateConvenience.h”

@implementation NSDate (BNRDateConvenience)

  • (NSDate *)midnightOfDateWithYear:(NSInteger)y
    withMonth:(NSInteger)m
    withDay:(NSInteger)d
    {

    NSDateComponents *componentsOfDate = [[NSDateComponents alloc] init];
    [componentsOfDate setYear:y];
    [componentsOfDate setMonth:m];
    [componentsOfDate setDay:d];
    [componentsOfDate setTimeZone:[NSTimeZone localTimeZone]];

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

    NSDate *passedInDate = [calender dateFromComponents:componentsOfDate];

    return passedInDate;
    }

@end
[/code]

(Have also tried to set the hour, minute and second of my NSDateComponents to zero)
Any tips or hints?

I had the same problem using the same kind of solution you had. I fixed my code like this.

NSDate+BNRDateConvenience.h

[code]#import <Foundation/Foundation.h>

@interface NSDate (BNRDateConvenience)

  • (NSDate *)bnr_makeMidnightDateWithYear:(NSUInteger)year Month:(NSUInteger)month Day:(NSUInteger)day;

@end
[/code]
NSDate+BNRDateConvenience.m

[code]#import “NSDate+BNRDateConvenience.h”

@implementation NSDate (BNRDateConvenience)

  • (NSDate *)bnr_makeMidnightDateWithYear:(NSUInteger)year Month:(NSUInteger)month Day:(NSUInteger)day
    {
    NSDateComponents *comps = [[NSDateComponents alloc] init];
    [comps setHour:0];
    [comps setMinute:0];
    [comps setSecond:0];
    [comps setDay:day];
    [comps setMonth:month];
    [comps setYear:year];

    NSCalendar *gregorian = [[NSCalendar alloc]
    initWithCalendarIdentifier:NSGregorianCalendar];
    [gregorian setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@“CET”]];
    return [gregorian dateFromComponents:comps];
    }
    @end
    [/code]
    main.m

[code]#import <Foundation/Foundation.h>
#import “NSDate+BNRDateConvenience.h”

int main(int argc, const char * argv[])
{

@autoreleasepool {
    
    NSDate *custumDate = [NSDate bnr_makeMidnightDateWithYear:1975 Month:7 Day:6];
    
    NSLog(@"The date is %@", [custumDate descriptionWithCalendarFormat:nil
                                                              timeZone:[NSTimeZone localTimeZone]
                                                                locale:nil]);
    
}
return 0;

}
[/code]

[quote=“torjush”]Having a slight problem that I think might have to do with timezones. My date keeps coming back two hours early.
I tried reading up in the reference and have so far ended up with this:

main.m:

[code]#import <Foundation/Foundation.h>
#import “NSDate+BNRDateConvenience.h”

int main(int argc, const char * argv[])
{

@autoreleasepool {
    
    NSDate *theDateIPassedIn =[ [[NSDate alloc] init]midnightOfDateWithYear: 2013 withMonth: 5 withDay: 19];
    
    
    NSLog(@"The passed in date was %@", theDateIPassedIn);
}
return 0;

}
[/code]

NSDate+BNRDateConvenience.h:

[code]#import <Foundation/Foundation.h>

@interface NSDate (BNRDateConvenience)

  • (NSDate *)midnightOfDateWithYear: (NSInteger)y
    withMonth: (NSInteger)m
    withDay: (NSInteger)d;

@end
[/code]

and NSDate+BNRDateConvenience.m:

[code]#import “NSDate+BNRDateConvenience.h”

@implementation NSDate (BNRDateConvenience)

  • (NSDate *)midnightOfDateWithYear:(NSInteger)y
    withMonth:(NSInteger)m
    withDay:(NSInteger)d
    {

    NSDateComponents *componentsOfDate = [[NSDateComponents alloc] init];
    [componentsOfDate setYear:y];
    [componentsOfDate setMonth:m];
    [componentsOfDate setDay:d];
    [componentsOfDate setTimeZone:[NSTimeZone localTimeZone]];

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

    NSDate *passedInDate = [calender dateFromComponents:componentsOfDate];

    return passedInDate;
    }

@end
[/code]

(Have also tried to set the hour, minute and second of my NSDateComponents to zero)
Any tips or hints?[/quote]

hi torjush,
I implemented the method just like you and got the same problem.
Then I used this code to print my date in the main.m file:

NSDate *date = [NSDate bnr_initToMidnightWithYear:2015 month:8 day:21]; NSLog(@"The date is %@", [date descriptionWithCalendarFormat:nil timeZone:[NSTimeZone systemTimeZone] locale:nil]);
Build and run it and I got

I think this may have fixed the problem.

[quote=“wzienrgo”]
hi torjush,
I implemented the method just like you and got the same problem.
Then I used this code to print my date in the main.m file:

NSDate *date = [NSDate bnr_initToMidnightWithYear:2015 month:8 day:21]; NSLog(@"The date is %@", [date descriptionWithCalendarFormat:nil timeZone:[NSTimeZone systemTimeZone] locale:nil]);
Build and run it and I got

I think this may have fixed the problem.[/quote]

The above provides the correction for the local time zone, but “descriptionWithCalendarFormat” is deprecated as of OS 10.10.

Instead, use “descriptionWithLocale:NSLocaleCalendar” like this:

NSLog(@"The date is %@", [date descriptionWithLocale:NSLocaleCalendar]);

You should get the same output via code that is not deprecated under OS 10.10 (Yosemite).