Hot*_*cks 164 iphone locale objective-c nsdateformatter ios
似乎NSDateFormatter有一个"功能"意外地咬你:如果你做一个简单的"固定"格式操作,如:
NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:@"yyyyMMddHHmmss"];
NSString* dateStr = [fmt stringFromDate:someDate];
[fmt release];
Run Code Online (Sandbox Code Playgroud)
然后它在美国和大多数区域设置工作正常UNTIL ...将手机设置为24小时区域的人将设置中的12/24小时开关设置为12.然后上面开始将"AM"或"PM"添加到结果字符串的结尾.
(参见,例如,NSDateFormatter,我做错了什么或者这是一个错误?)
(请参阅https://developer.apple.com/library/content/qa/qa1480/_index.html)
显然苹果公司宣称这是"不好" - 破碎如设计,他们不会解决它.
规避显然是为特定区域(通常是美国)设置日期格式化程序的区域设置,但这有点乱:
NSLocale *loc = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
[df setLocale: loc];
[loc release];
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
我将奖励给我在星期二中午看到的最佳(合法)建议/批评奖励.[见下文 - 延长期限.]
重新讨论OMZ的建议,这就是我所发现的 -
这是类别版本 - h文件:
#import <Foundation/Foundation.h>
@interface NSDateFormatter (Locale)
- (id)initWithSafeLocale;
@end
Run Code Online (Sandbox Code Playgroud)
类别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
Run Code Online (Sandbox Code Playgroud)
代码:
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];
Run Code Online (Sandbox Code Playgroud)
结果:
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)
Run Code Online (Sandbox Code Playgroud)
手机[使iPod Touch]设置为英国,12/24开关设置为12.这两个结果有明显区别,我判断类别版本是错误的.请注意,类别版本IS中的日志正在执行(并且代码中的停止点被命中),因此不仅仅是代码的某种情况不会被使用.
由于我还没有得到任何适用的答复,我会将奖金截止日期延长一两天.
Bounty在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
Run Code Online (Sandbox Code Playgroud)
基本上只是更改了静态语言环境变量的名称(如果与子类中声明的静态存在某些冲突)并添加了额外的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
Run Code Online (Sandbox Code Playgroud)
如您所见,setLocale根本没有.格式化程序的语言环境仍然是en_GB.似乎某个类别中的init方法存在"奇怪".
请参阅下面接受的答案.
Hot*_*cks 66
有时你有一个"啊哈!" 那一刻,有时它更像是"Duh !!" 这是后者.在initWithSafeLocale"超级" 的类别init编码为self = [super init];.这inits的SUPERCLASS NSDateFormatter但不init的NSDateFormatter对象本身.
显然,当跳过此初始化时,setLocale"弹回",可能是因为对象中缺少一些数据结构.更改initto self = [self init];会导致NSDateFormatter初始化发生,并setLocale再次开心.
这是类别.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
Run Code Online (Sandbox Code Playgroud)
omz*_*omz 40
您可以NSDateFormatter使用额外的初始化程序创建一个类别,而不是子类化,该初始化程序负责分配区域设置,也可能是格式字符串,因此您在初始化之后就可以使用现成的格式化程序.
@interface NSDateFormatter (LocaleAdditions)
- (id)initWithPOSIXLocaleAndFormat:(NSString *)formatString;
@end
@implementation NSDateFormatter (LocaleAdditions)
- (id)initWithPOSIXLocaleAndFormat:(NSString *)formatString {
self = [super init];
if (self) {
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
[self setLocale:locale];
[locale release];
[self setFormat:formatString];
}
return self;
}
@end
Run Code Online (Sandbox Code Playgroud)
然后你可以NSDateFormatter在你的代码中的任何地方使用:
NSDateFormatter* fmt = [[NSDateFormatter alloc] initWithPOSIXLocaleAndFormat:@"yyyyMMddHHmmss"];
Run Code Online (Sandbox Code Playgroud)
您可能希望以某种方式为类别方法添加前缀以避免名称冲突,以防Apple决定在未来版本的OS中添加此类方法.
如果您总是使用相同的日期格式,您还可以添加返回具有特定配置的单例实例的类别方法(类似+sharedRFC3339DateFormatter).但请注意,这NSDateFormatter不是线程安全的,@synchronized当您从多个线程使用相同的实例时,必须使用锁或块.
我可以提出一些完全不同的东西,因为老实说这一切都有点像兔子洞.
您应该使用一个NSDateFormatter具有dateFormat集并locale被迫en_US_POSIX接受日期(从服务器/原料药).
那么你应该使用不同NSDateFormatter的UI来设置timeStyle/ dateStyle属性 - 这样你就没有dateFormat自己明确的设置,因此错误地假设将使用格式.
这意味着UI由用户首选项驱动(上午/下午对24小时,日期字符串正确格式化为用户选择 - 来自iOS设置),而"进入"您的应用程序的日期始终正确地"解析" NSDate为你用.
| 归档时间: |
|
| 查看次数: |
41193 次 |
| 最近记录: |