为什么NSDateFormatter在巴西时区为19/10/2014返回null?
NSString *dateString = @"19/10/2014"; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"dd/MM/yyyy"]; NSDate *myDate = [dateFormatter dateFromString:dateString];  为什么myDate在这个特定的date为null(19/10/2014)? 
 如果我将dateString更改为@"25/10/2014" ,则dateFormatter正确返回date…我的代码有什么问题? 
*当我的iPhone时区是“巴西利亚,巴西”时,此代码返回null。 例如,当我的时区是“华盛顿特区,EUA”时,代码会返回正确的date。
我们可以通过将时区明确设置为“巴西/东部”来重现您的问题:
 #import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { NSString *dateString = @"19/10/2014"; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"Brazil/East"]; [dateFormatter setDateFormat:@"dd/MM/yyyy"]; NSDate *myDate = [dateFormatter dateFromString:dateString]; NSLog(@"myDate = %@", myDate); } return 0; } 
这是输出:
 2014-06-06 14:22:28.254 commandLine[31169:303] myDate = (null) 
 由于您没有在dateString给出时间,因此系统假定午夜。 但巴西时区不存在午夜 。 
巴西从2014年10月19日的BRT(夏时制时区)变为BRST(非夏令时区) ,直接从“ 2014年10月18日 ”的最后一刻跳到“ 2014年10月19日 01 :00:00” 。
 由于“19/10/2014 00:00:00”不存在, NSDateFormatter返回nil 。 我认为这是NSDateFormatter不良行为,但我们必须处理它。  -[NSDateFormatter dateFromString:]最终调用CFDateFormatterGetAbsoluteTimeFromString ,它使用国际组件Unicode(icu)库中的udat_parseCalendar函数来parsingdate。 
您可以通过使parsing器使用中午而不是午夜作为默认时间来解决该问题。 中午的夏令时没有时区变化。 让我们来写一个帮助函数,它返回给定时区中某个任意date的中午:
 static NSDate *someDateWithNoonWithTimeZone(NSTimeZone *timeZone) { NSDateComponents *components = [[NSDateComponents alloc] init]; components.timeZone = timeZone; components.era = 1; components.year = 2001; components.month = 1; components.day = 1; components.hour = 12; components.minute = 0; components.second = 0; return [[NSCalendar autoupdatingCurrentCalendar] dateFromComponents:components]; } 
 然后我们把date格式化器的defaultDate为这个中午的date: 
 int main(int argc, const char * argv[]) { @autoreleasepool { NSString *dateString = @"19/10/2014"; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"Brazil/East"]; dateFormatter.dateFormat = @"dd/MM/yyyy"; dateFormatter.defaultDate = someDateWithNoonWithTimeZone(dateFormatter.timeZone); NSDate *myDate = [dateFormatter dateFromString:dateString]; NSLog(@"myDate = %@", myDate); } return 0; } 
这里是输出:
 2014-06-06 14:52:31.939 commandLine[31982:303] myDate = 2014-10-19 14:00:00 +0000 
Rob Mayoff对这个问题给出了一个很好的解释和解决办法 。 正如罗布所指出的那样,巴西时区不存在19/10/2014午夜。
另一个可能的解决scheme是告诉date格式化程序是“宽松的”。 在这种情况下,它将返回给定date的第一个有效date:
 NSString *dateString = @"19/10/2014"; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"America/Sao_Paulo"]; dateFormatter.dateFormat = @"dd/MM/yyyy"; dateFormatter.lenient = YES; NSDate *myDate = [dateFormatter dateFromString: dateString]; NSLog(@"myDate = %@", myDate); // myDate = 2014-10-19 03:00:00 +0000 
结果是“2014-10-19 03:00:00 UTC”,即“2014-10-19 01:00:00 BRST”,即夏令时开始当天的第一个有效date。
如果您希望格式化的date是希望所有地区的所有设备都能理解的特定格式,则需要在date格式化程序上设置语言环境。
如果不这样做,date格式化程序将默认为设备区域设置。 这意味着巴西的date格式看起来不是dd / MM / yyyy
您可以强制您的date格式化程序使用特定的区域设置,如下所示:
 [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"]]; 
希望这可以帮助
w ^