似乎NSDateFormatter有一个“特点”让你意想不到:如果你做一个简单的“固定”格式操作,例如:
NSDateFormatter
NSDateFormatter* fmt = [[NSDateFormatter alloc] init]; [fmt setDateFormat:@"yyyyMMddHHmmss"]; NSString* dateStr = [fmt stringFromDate:someDate]; [fmt release];
然后它在美国和大多数地区都可以正常工作,直到…有人将手机设置为 24 小时区域,将设置中的 12/24 小时切换设置为 12。然后上面开始将“AM”或“PM”添加到结果字符串的结尾。
(并参见https://developer.apple.com/library/content/qa/qa1480/_index.html)
显然,Apple 已经宣布这是“糟糕的”——Broken As Design,他们不会修复它。
规避显然是为特定地区(通常是美国)设置日期格式化程序的语言环境,但这有点混乱:
NSLocale *loc = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; [df setLocale: loc]; [loc release];
在 onsies-twosies 中还不错,但我正在处理大约十个不同的应用程序,我看到的第一个应用程序有 43 个这种情况的实例。
那么对于宏/重写类/任何巧妙的想法可以最大限度地减少改变一切的努力,而不会使代码变得晦涩难懂?(我的第一直觉是用一个可以在 init 方法中设置语言环境的版本覆盖 NSDateFormatter。需要更改两行——alloc/init 行和添加的导入。)
这是我迄今为止提出的 - 似乎适用于所有场景:
@implementation BNSDateFormatter -(id)init { static NSLocale* en_US_POSIX = nil; NSDateFormatter* me = [super init]; if (en_US_POSIX == nil) { en_US_POSIX = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; } [me setLocale:en_US_POSIX]; return me; } @end
我将在星期二中午之前将赏金奖励给我看到的最佳(合法)建议/批评。[见下文——截止日期已延长。]
关于 OMZ 的建议,这就是我的发现——
这是类别版本–h文件:
#import <Foundation/Foundation.h> @interface NSDateFormatter (Locale) - (id)initWithSafeLocale; @end
m类文件:
#import "NSDateFormatter+Locale.h" @implementation NSDateFormatter (Locale) - (id)initWithSafeLocale { static NSLocale* en_US_POSIX = nil; self = [super init]; if (en_US_POSIX == nil) { en_US_POSIX = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; } NSLog(@"Category's locale: %@ %@", en_US_POSIX.description, [en_US_POSIX localeIdentifier]); [self setLocale:en_US_POSIX]; return self; } @end
编码:
NSDateFormatter* fmt; NSString* dateString; NSDate* date1; NSDate* date2; NSDate* date3; NSDate* date4; fmt = [[NSDateFormatter alloc] initWithSafeLocale]; [fmt setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; dateString = [fmt stringFromDate:[NSDate date]]; NSLog(@"dateString = %@", dateString); date1 = [fmt dateFromString:@"2001-05-05 12:34:56"]; NSLog(@"date1 = %@", date1.description); date2 = [fmt dateFromString:@"2001-05-05 22:34:56"]; NSLog(@"date2 = %@", date2.description); date3 = [fmt dateFromString:@"2001-05-05 12:34:56PM"]; NSLog(@"date3 = %@", date3.description); date4 = [fmt dateFromString:@"2001-05-05 12:34:56 PM"]; NSLog(@"date4 = %@", date4.description); [fmt release]; fmt = [[BNSDateFormatter alloc] init]; [fmt setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; dateString = [fmt stringFromDate:[NSDate date]]; NSLog(@"dateString = %@", dateString); date1 = [fmt dateFromString:@"2001-05-05 12:34:56"]; NSLog(@"date1 = %@", date1.description); date2 = [fmt dateFromString:@"2001-05-05 22:34:56"]; NSLog(@"date2 = %@", date2.description); date3 = [fmt dateFromString:@"2001-05-05 12:34:56PM"]; NSLog(@"date3 = %@", date3.description); date4 = [fmt dateFromString:@"2001-05-05 12:34:56 PM"]; NSLog(@"date4 = %@", date4.description); [fmt release];
结果:
2011-07-11 17:44:43.243 DemoApp[160:307] Category's locale: <__NSCFLocale: 0x11a820> en_US_POSIX 2011-07-11 17:44:43.257 DemoApp[160:307] dateString = 2011-07-11 05:44:43 PM 2011-07-11 17:44:43.264 DemoApp[160:307] date1 = (null) 2011-07-11 17:44:43.272 DemoApp[160:307] date2 = (null) 2011-07-11 17:44:43.280 DemoApp[160:307] date3 = (null) 2011-07-11 17:44:43.298 DemoApp[160:307] date4 = 2001-05-05 05:34:56 PM +0000 2011-07-11 17:44:43.311 DemoApp[160:307] Extended class's locale: <__NSCFLocale: 0x11a820> en_US_POSIX 2011-07-11 17:44:43.336 DemoApp[160:307] dateString = 2011-07-11 17:44:43 2011-07-11 17:44:43.352 DemoApp[160:307] date1 = 2001-05-05 05:34:56 PM +0000 2011-07-11 17:44:43.369 DemoApp[160:307] date2 = 2001-05-06 03:34:56 AM +0000 2011-07-11 17:44:43.380 DemoApp[160:307] date3 = (null) 2011-07-11 17:44:43.392 DemoApp[160:307] date4 = (null)
手机 [制作 iPod Touch] 设置为英国,12/24 开关设置为 12。两个结果有明显差异,我判断类别版本是错误的。请注意,类别版本中的日志正在执行(并且代码中的停止被命中),因此这不仅仅是代码没有被使用的情况。
由于我还没有收到任何适用的回复,我会将赏金截止日期再延长一两天。
赏金会在 21 小时内结束——它会发给最努力提供帮助的人,即使答案对我来说并不是真正有用。
稍微修改了类别实现:
#import "NSDateFormatter+Locale.h" @implementation NSDateFormatter (Locale) - (id)initWithSafeLocale { static NSLocale* en_US_POSIX2 = nil; self = [super init]; if (en_US_POSIX2 == nil) { en_US_POSIX2 = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; } NSLog(@"Category's locale: %@ %@", en_US_POSIX2.description, [en_US_POSIX2 localeIdentifier]); [self setLocale:en_US_POSIX2]; NSLog(@"Category's object: %@ and object's locale: %@ %@", self.description, self.locale.description, [self.locale localeIdentifier]); return self; } @end
基本上只是更改了静态语言环境变量的名称(以防与子类中声明的静态变量发生冲突)并添加了额外的 NSLog。但是看看 NSLog 打印的内容:
2011-07-15 16:35:24.322 DemoApp[214:307] Category's locale: <__NSCFLocale: 0x160550> en_US_POSIX 2011-07-15 16:35:24.338 DemoApp[214:307] Category's object: <NSDateFormatter: 0x160d90> and object's locale: <__NSCFLocale: 0x12be70> en_GB 2011-07-15 16:35:24.345 DemoApp[214:307] dateString = 2011-07-15 04:35:24 PM 2011-07-15 16:35:24.370 DemoApp[214:307] date1 = (null) 2011-07-15 16:35:24.378 DemoApp[214:307] date2 = (null) 2011-07-15 16:35:24.390 DemoApp[214:307] date3 = (null) 2011-07-15 16:35:24.404 DemoApp[214:307] date4 = 2001-05-05 05:34:56 PM +0000
如您所见, setLocale 根本没有。格式化程序的语言环境仍然是 en_GB。似乎某个类别中的 init 方法有些“奇怪”。
请参阅下面接受的答案。
initWithSafeLocale在“超级”类别中init被编码为self = [super init];. 这会初始化对象本身的SUPERCLASS,NSDateFormatter但不会初始化对象本身。init``NSDateFormatter
initWithSafeLocale
init
self = [super init];
init``NSDateFormatter
显然,当这个初始化被跳过时,setLocale“反弹”,大概是因为对象中缺少一些数据结构。更改inittoself = [self init];会导致NSDateFormatter初始化发生,并setLocale再次感到高兴。
setLocale
self = [self init];
这是该类别的 .m 的“最终”来源:
#import "NSDateFormatter+Locale.h" @implementation NSDateFormatter (Locale) - (id)initWithSafeLocale { static NSLocale* en_US_POSIX = nil; self = [self init]; if (en_US_POSIX == nil) { en_US_POSIX = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; } [self setLocale:en_US_POSIX]; return self; } @end
有时你有一个“啊哈!!” 时刻,有时它更像是一个“Duh!!” 这是后者。initWithSafeLocale在“超级”类别中init被编码为self = [super init];. 这会初始化对象本身的SUPERCLASS,NSDateFormatter但不会初始化对象本身。init``NSDateFormatter