NSDateFormatter
には予期せぬ「機能」があるようです:次のような単純な「固定」フォーマット操作を行う場合:
NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:@"yyyyMMddHHmmss"];
NSString* dateStr = [fmt stringFromDate:someDate];
[fmt release];
その後、米国およびほとんどのロケールでUNTILが正常に機能します。24時間の地域に設定された電話を持つユーザーは、設定の12/24時間切り替えを12に設定します。結果の文字列の終わり。
(たとえば、 NSDateFormatterを参照してください。何か間違っているのですか、それともバグですか? )
(そして https://developer.Apple.com/library/content/qa/qa1480/_index.html を参照)
どうやらAppleはこれを「悪い」と宣言しています-Broken As Designedであり、修正するつもりはありません。
回避策は明らかに、特定の地域、一般的には米国の日付フォーマッターのロケールを設定することですが、これは少し厄介です:
NSLocale *loc = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
[df setLocale: loc];
[loc release];
おっと二人でそれほど悪くはありませんが、私は約10種類のアプリを扱っており、最初に見たアプリにはこのシナリオのインスタンスが43個あります。
だから、マクロ/オーバーライドされたクラス/何でも、コードを不明瞭にすることなく、すべてを変更する労力を最小限にするための賢いアイデアはありますか? (私の最初の本能は、NSDateFormatterをinitメソッドでロケールを設定するバージョンでオーバーライドすることです。alloc/ init行と追加されたインポートの2行を変更する必要があります。)
これは私がこれまでに思いついたものです-すべてのシナリオで動作するようです:
@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に設定されています。2つの結果に明確な違いがあり、カテゴリバージョンが間違っていると判断します。カテゴリバージョンISのログが実行される(およびコードに配置されたストップがヒットする)ので、コードが何らかの理由で使用されないというだけではないことに注意してください。
私はまだ適切な返信を受け取っていないので、賞金期限をあと1日または2日延長します。
バウンティは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です。カテゴリ内の初期化メソッドについて何か「奇妙な」ことがあるようです。
以下の 受け入れられた答え を参照してください。
時々「あは!!」瞬間、時には「ダウ!!」これは後者です。 initWithSafeLocale
のカテゴリでは、 "super" 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
サブクラス化する代わりに、ロケールの割り当てを処理する初期化子を追加する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"];
AppleがOSの将来のバージョンでこのようなメソッドを追加することを決定した場合に備えて、名前の競合を避けるために何らかの方法でカテゴリメソッドにプレフィックスを付けることができます。
常に同じ日付形式を使用している場合、特定の構成(+sharedRFC3339DateFormatter
)。ただし、NSDateFormatter
はスレッドセーフではないため、ロックまたは@synchronized
は、複数のスレッドから同じインスタンスを使用している場合にブロックします。
正直に言うと、これはすべてウサギの穴を駆け抜けているからです。
NSDateFormatter
を1つ使用してdateFormat
を設定し、locale
をen_US_POSIX
に強制して(サーバー/ APIから)日付を受信する必要があります。
次に、NSDateFormatter
/timeStyle
プロパティを設定するUIに別のdateStyle
を使用する必要があります。この方法では、明示的にdateFormat
を自分で設定する必要がないため、その形式が使用されると誤って仮定します。
つまり、UIはユーザー設定(午前/午後vs 24時間、およびユーザー設定に合わせて正しくフォーマットされた日付文字列-iOS設定から)によって駆動されますが、アプリに「入ってくる」日付は常にNSDate
に正しく「解析」されますあなたが使用するため。
Swiftバージョンでのその問題の解決策を次に示します。 Swiftでは、カテゴリの代わりに拡張子を使用できます。そのため、ここではDateFormatterの拡張機能を作成し、その中にinitWithSafeLocaleが関連するロケールでDateFormatterを返すようにします。
スイフト4
extension DateFormatter {
private static var dateFormatter = DateFormatter()
class func initWithSafeLocale(withDateFormat dateFormat: String? = nil) -> DateFormatter {
dateFormatter = DateFormatter()
var en_US_POSIX: Locale? = nil;
if (en_US_POSIX == nil) {
en_US_POSIX = Locale.init(identifier: "en_US_POSIX")
}
dateFormatter.locale = en_US_POSIX
if dateFormat != nil, let format = dateFormat {
dateFormatter.dateFormat = format
}else{
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
}
return dateFormatter
}
// ------------------------------------------------------------------------------------------
class func getDateFromString(string: String, fromFormat dateFormat: String? = nil) -> Date? {
if dateFormat != nil, let format = dateFormat {
dateFormatter = DateFormatter.initWithSafeLocale(withDateFormat: format)
}else{
dateFormatter = DateFormatter.initWithSafeLocale()
}
guard let date = dateFormatter.date(from: string) else {
return nil
}
return date
}
// ------------------------------------------------------------------------------------------
class func getStringFromDate(date: Date, fromDateFormat dateFormat: String? = nil)-> String {
if dateFormat != nil, let format = dateFormat {
dateFormatter = DateFormatter.initWithSafeLocale(withDateFormat: format)
}else{
dateFormatter = DateFormatter.initWithSafeLocale()
}
let string = dateFormatter.string(from: date)
return string
} }
使用法の説明:
let date = DateFormatter.getDateFromString(string: "11-07-2001”, fromFormat: "dd-MM-yyyy")
print("custom date : \(date)")
let dateFormatter = DateFormatter.initWithSafeLocale(withDateFormat: "yyyy-MM-dd HH:mm:ss")
let dt = DateFormatter.getDateFromString(string: "2001-05-05 12:34:56")
print("base date = \(dt)")
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let dateString = dateFormatter.string(from: Date())
print("dateString = " + dateString)
let date1 = dateFormatter.date(from: "2001-05-05 12:34:56")
print("date1 = \(String(describing: date1))")
let date2 = dateFormatter.date(from: "2001-05-05 22:34:56")
print("date2 = \(String(describing: date2))")
let date3 = dateFormatter.date(from: "2001-05-05 12:34:56PM")
print("date3 = \(String(describing: date3))")
let date4 = dateFormatter.date(from: "2001-05-05 12:34:56 PM")
print("date4 = \(String(describing: date4))")