首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >处理NSDateFormatter语言环境"feechur“的最好方法是什么?

处理NSDateFormatter语言环境"feechur“的最好方法是什么?
EN

Stack Overflow用户
提问于 2011-07-07 23:30:15
回答 3查看 45.8K关注 0票数 171

看起来NSDateFormatter有一个让你意想不到的“特性”:如果你做一个简单的“固定”格式操作,比如:

NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:@"yyyyMMddHHmmss"];
NSString* dateStr = [fmt stringFromDate:someDate];
[fmt release];

那么它在美国和大多数地区都运行得很好,直到...将手机设置为24小时区域的人将12/24小时开关设置为12。然后,上面的代码开始在结果字符串的末尾添加"AM“或"PM”。

(参见,例如NSDateFormatter, am I doing something wrong or is this a bug?)

(请参阅https://developer.apple.com/library/content/qa/qa1480/_index.html)

显然,苹果已经宣布这是“糟糕的”--如设计的那样坏了,他们不会修复它。

这种规避显然是为了设置特定地区(通常是美国)的日期格式化程序的区域设置,但这有点混乱:

NSLocale *loc = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
[df setLocale: loc];
[loc release];

在onsies-twosies中不算太差,但我正在处理大约10个不同的应用程序,我查看的第一个应用程序有43个这种情况的实例。

因此,对于宏/覆盖类/任何东西,有什么聪明的想法可以最大限度地减少更改一切的努力,而不会使代码变得晦涩难懂?(我的第一个直觉是用一个可以在init方法中设置地区的版本覆盖NSDateFormatter。需要更改两行-- alloc/init行和添加的import。)

已添加

这就是我到目前为止想出的--似乎在所有情况下都有效:

@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

赏金!

我会在周二中午之前把奖金奖励给我看到的最好的(合法的)建议/批评。见下文--截止日期延长。

更新

Re 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

正如您所看到的,格式化程序的区域设置仍然是en_GB,所以setLocale根本没有这样做。似乎一个类别中的init方法有些“奇怪”。

最终答案

请参阅下面的accepted answer

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-07-18 23:50:52

啊!!

有时你会发出一声“啊哈!”片刻,有时更像是“讨厌!”这是后者。在initWithSafeLocale类别中,“超级”init被编码为self = [super init];。这会初始化NSDateFormatter的超类,但不会init NSDateFormatter对象本身。

显然,当跳过此初始化时,setLocale将“反弹”,这可能是因为对象中缺少某些数据结构。将init更改为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
票数 69
EN

Stack Overflow用户

发布于 2011-07-10 01:09:02

与子类化不同,您可以创建一个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

然后,您可以在代码中的任何位置使用NSDateFormatter,只需:

NSDateFormatter* fmt = [[NSDateFormatter alloc] initWithPOSIXLocaleAndFormat:@"yyyyMMddHHmmss"];

你可能想以某种方式给你的类别方法加上前缀,以避免名称冲突,以防苹果公司决定在未来的操作系统版本中添加这样的方法。

如果您总是使用相同的日期格式,您还可以添加类别方法,这些方法返回具有特定配置的单例实例(类似于+sharedRFC3339DateFormatter)。但是请注意,NSDateFormatter不是线程安全的,当您从多个线程使用同一实例时,必须使用锁或@synchronized块。

票数 41
EN

Stack Overflow用户

发布于 2015-03-10 19:26:34

请允许我提出一些完全不同的建议,因为老实说,所有这些都有点像是在跑进兔子洞。

您应该使用一个设置了dateFormatNSDateFormatter和强制为en_US_POSIXlocale来接收数据(从服务器/API)。

然后,您应该为UI使用不同的NSDateFormatter,以设置timeStyle/dateStyle属性-这样您就不会自己设置显式的dateFormat,从而错误地假设将使用该格式。

这意味着用户界面是由用户偏好驱动的(上午/下午与24小时,日期字符串的格式符合用户的选择-从iOS设置),而“进入”你的应用程序的日期总是被正确地“解析”到一个供你使用的NSDate

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6613110

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档