fundidor
fundidor

Reputation: 207

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!

Upvotes: 6

Views: 496

Answers (2)

matt
matt

Reputation: 536028

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.

Upvotes: 1

rdelmar
rdelmar

Reputation: 104092

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).

Upvotes: 5

Related Questions