Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSDateFormatter Puzzle! Is this is new Mountain Lion Bug?

I believe I might have found a bug in NSDateFormatter, and I am not sure if this is localized to having a Brazil time zone specified in System Preferences.

A full project is here: http://www.idanfe.com/dl/nsDateFormatterPuzzle.zip

The relevant excerpts of the code is shown below:

NSArray *unformattedDatesArray = [[NSArray alloc] initWithObjects:@"2011-09-18",
     @"2011-10-16",@"2011-11-13",@"2011-12-11",@"2012-01-08",@"2012-02-05", nil];

NSString *resultedString = @"";
int ii = 0;
NSUInteger items = [unformattedDatesArray count];
for (ii=0; ii<items; ii++) {
    NSLog(@"original item # %d = %@",ii,[unformattedDatesArray objectAtIndex:ii]);
    resultedString = [resultedString stringByAppendingString:@"original item # "];
    resultedString = [resultedString stringByAppendingString:[NSString stringWithFormat: @"%d = ",ii]];
    resultedString = [resultedString stringByAppendingString:[unformattedDatesArray objectAtIndex:ii]];
    resultedString = [resultedString stringByAppendingString:[NSString stringWithFormat:@"\n"]];
}
NSDateFormatter *originalFormat = [[NSDateFormatter alloc] init];
[originalFormat setDateFormat:@"yyyy-MM-dd"]; // hh:mm:ss
NSDateFormatter *newFormat = [[NSDateFormatter alloc] init];
[newFormat setDateFormat:@"dd/MM/yyyy"]; // hh:mm:ss
NSString *formattedDate;
for (ii=0; ii<items; ii++) {
    formattedDate = [newFormat stringFromDate:[originalFormat
                                               dateFromString:[unformattedDatesArray objectAtIndex:ii]]];
    NSLog(@"formatted item # %d = %@",ii,formattedDate);
    resultedString = [resultedString stringByAppendingString:@"formatted item # "];
    resultedString = [resultedString stringByAppendingString:[NSString stringWithFormat: @"%d = ",ii]];
    resultedString = [resultedString stringByAppendingString:(formattedDate == nil ? @"ERROR" : formattedDate)];
    resultedString = [resultedString stringByAppendingString:[NSString stringWithFormat:@"\n"]];
}
[resultingStringTextView insertText:resultedString];

Basically, I'm starting with an array of 5 NSStrings which represent dates. I'm creating 2 NSDateFormatters: originalFormat is used to convert the original string representation of the date into an actual NSDate; newFormat is used to create a new desired string representation of the NSDate generated with the originalFormat date formatter.

This is my resulting log:

2012-08-26 nsDateFormatterPuzzle[3261:303] original item # 0 = 2011-09-18
2012-08-26 nsDateFormatterPuzzle[3261:303] original item # 1 = 2011-10-16
2012-08-26 nsDateFormatterPuzzle[3261:303] original item # 2 = 2011-11-13
2012-08-26 nsDateFormatterPuzzle[3261:303] original item # 3 = 2011-12-11
2012-08-26 nsDateFormatterPuzzle[3261:303] original item # 4 = 2012-01-08
2012-08-26 nsDateFormatterPuzzle[3261:303] original item # 5 = 2012-02-05
2012-08-26 nsDateFormatterPuzzle[3261:303] formatted item # 0 = 18/09/2011
2012-08-26 nsDateFormatterPuzzle[3261:303] formatted item # 1 = (null)
2012-08-26 nsDateFormatterPuzzle[3261:303] formatted item # 2 = 13/11/2011
2012-08-26 nsDateFormatterPuzzle[3261:303] formatted item # 3 = 11/12/2011
2012-08-26 nsDateFormatterPuzzle[3261:303] formatted item # 4 = 08/01/2012
2012-08-26 nsDateFormatterPuzzle[3261:303] formatted item # 5 = 05/02/2012

Note that if you go to System Preferences --> Date and Time --> Time Zone

and change your Time Zone to Rio de Janeiro, you will have the same results.

================================ TRYING to Workaround ===================================

I am trying to get a workaround this bug meanwhile Apple does not fix it.

I have two problems, one is to render a PDF file from this date, and I think I have solved this by:

 if (formated_issue_date == NULL) {
            formated_issue_date = [BHDateFormatter bhDateFormat:data_emissao];

}

Using this handler:

+(NSString*)bhDateFormat:(NSString *)inputedDate{

    NSString *formattedDate = @"";

    NSRange range82 = NSMakeRange (8, 2);
    NSRange range52 = NSMakeRange (5, 2);
    NSRange range04 = NSMakeRange (0, 4);

    NSString *firstBlock = [inputedDate substringWithRange:range82];
    NSString *secondBlock = [inputedDate substringWithRange:range52];
    NSString *thirdBlock = [inputedDate substringWithRange:range04];

    formattedDate = [formattedDate stringByAppendingString:firstBlock];
    formattedDate = [formattedDate stringByAppendingString:@"/"];
    formattedDate = [formattedDate stringByAppendingString:secondBlock];
    formattedDate = [formattedDate stringByAppendingString:@"/"];
    formattedDate = [formattedDate stringByAppendingString:thirdBlock];

    return formattedDate;
}

The second problem is that the invoice date is loaded into a a table view that contains a date formatter, and changing this would cause the sorting of the table to be lost.

Using the same principle as my handler, and insisting on passing the 16/10/2011 date causes errors on the table view, like: -[__NSCFString timeIntervalSinceReferenceDate]: unrecognized selector sent to instance 0x40265f3a0

This is the best code I found so far:

if (formatterDate == NULL) {
                        NSLog (@"outprintString '%@' = NULL", dateString);
                        NSString *newDate = [BHDateFormatter bhDateFormat:dateString];
                        if ([newDate isEqualToString:@"16/10/2011"]) {
                            formatterDate = [inputFormatter dateFromString:@"2011-10-17"];
                            NSLog(@"formatterDate = %@", formatterDate);
                            NSAlert *alert = [[[NSAlert alloc]init] autorelease];
                            [alert setMessageText:[NSString stringWithFormat:NSLocalizedString(@"Mountain Lion Bug: I have found a date \"16/10/2011\" in your invoice # %@ issued by %@, I must rename the date in your table view to \"17/10/2011\", the date on the rendered invoice file will be display correctly. I apologize for this, but it is an operating system level bug that has to be fixed by Apple.","A comment here"),invoiceNumber, emitente]];
                            [alert runModal];
                        }
                    }

Could you please comment?

Thanks!

like image 406
fundidor Avatar asked Aug 28 '12 21:08

fundidor


2 Answers

I believe that you're right -- that is a bug, and you should report it. Interestingly, if you set the timezone to Nuuk-Greenland, which is in the same timezone as Rio, it works properly. Also, "2011-10-15" and "2011-10-17" work ok with the Rio time zone, but not "2011-10-16" which is really strange.

This is the simplified the code that I used to test this:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

    NSArray *unformattedDatesArray = [[NSArray alloc] initWithObjects:@"2011-09-18",@"2011-10-16",@"2011-11-13",@"2011-12-11",@"2012-01-08",@"2012-02-05", nil];

    NSDateFormatter *originalFormat = [[NSDateFormatter alloc] init];
    [originalFormat setDateFormat:@"yyyy-MM-dd"]; // hh:mm:ss
    NSDateFormatter *newFormat = [[NSDateFormatter alloc] init];
    [newFormat setDateFormat:@"dd/MM/yyyy"]; // hh:mm:ss

    for (NSString * aString in unformattedDatesArray) {
        NSString *formattedDate = [newFormat stringFromDate:[originalFormat dateFromString:aString]];
        NSLog(@"Original date string is: %@  formatted date as string is: %@",aString, formattedDate);
    }
}

Added Information:

To find the source of this problem, I looped through all the dates in 2011 with the following code:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

    NSDateFormatter *originalFormat = [[NSDateFormatter alloc] init];
    [originalFormat setDateFormat:@"yyyy-MM-dd"]; // hh:mm:ss
    NSDateFormatter *newFormat = [[NSDateFormatter alloc] init];
    [newFormat setDateFormat:@"dd/MM/yyyy"]; // hh:mm:ss

    NSDate *start = [originalFormat dateFromString:@"2011-01-01"];
    NSDate *newDate;
    for (int i=0; i<365; i++) {
        newDate = [start dateByAddingTimeInterval:86400 * i];
        NSString *formattedDate = [newFormat stringFromDate:newDate];
        NSLog(@"formatted date as string is: %@",formattedDate);
    }
}

When you look at the date strings, you find that there are 2 february 19th's and no October 16th, which is why you get null for that date (although if you log the date objects themselves, you get all the dates correctly).

like image 163
rdelmar Avatar answered Sep 22 '22 10:09

rdelmar


It's not a bug. October 16 is the day that Brazil went onto southern-hemisphere daylight saving time that year, and your proposed time (assumed to be midnight, since you don't give an explicit time) is during the missing hour. It is a non-existent time.

like image 20
matt Avatar answered Sep 19 '22 10:09

matt