为什么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 ^