Ausgabe
Es scheint, dass NSDateFormatter
das eine “Funktion” hat, die Sie unerwartet beißt: Wenn Sie eine einfache “feste” Formatoperation ausführen, wie zum Beispiel:
NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:@"yyyyMMddHHmmss"];
NSString* dateStr = [fmt stringFromDate:someDate];
[fmt release];
Dann funktioniert es in den USA und den meisten Ländern gut, BIS … jemand mit seinem Telefon auf eine 24-Stunden-Region den 12/24-Stunden-Schalter in den Einstellungen auf 12 setzt. Dann beginnt das Obige, “AM” oder “PM” anzuheften das Ende der resultierenden Zeichenfolge.
(Siehe zB NSDateFormatter, mache ich etwas falsch oder ist das ein Bug? )
(Und siehe https://developer.apple.com/library/content/qa/qa1480/_index.html )
Anscheinend hat Apple dies als “BAD” — Broken As Designed – deklariert, und sie werden es nicht beheben.
Die Umgehung besteht anscheinend darin, das Gebietsschema des Datumsformatierers für eine bestimmte Region festzulegen, im Allgemeinen die USA, aber das ist ein bisschen chaotisch:
NSLocale *loc = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
[df setLocale: loc];
[loc release];
Nicht schlecht in Onesies-Twosies, aber ich habe es mit ungefähr zehn verschiedenen Apps zu tun, und die erste, die ich mir ansehe, hat 43 Instanzen dieses Szenarios.
Also irgendwelche cleveren Ideen für ein Makro/überschriebene Klasse/was auch immer, um den Aufwand zu minimieren, alles zu ändern, ohne den Code zu verschleiern? (Mein erster Instinkt ist, NSDateFormatter mit einer Version zu überschreiben, die das Gebietsschema in der Init-Methode festlegen würde. Erfordert das Ändern von zwei Zeilen – der alloc/init-Zeile und dem hinzugefügten Import.)
##Hinzugefügt
Folgendes habe ich mir bisher ausgedacht – scheint in allen Szenarien zu funktionieren:
@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
##Update zum Vorschlag von OMZ, hier ist, was ich finde —
Hier ist die Kategorieversion – h-Datei:
#import <Foundation/Foundation.h>
@interface NSDateFormatter (Locale)
- (id)initWithSafeLocale;
@end
Datei der Kategorie 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
Der Code:
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];
Das Ergebnis:
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)
The phone [make that an iPod Touch] is set to Great Britain, with the 12/24 switch set to 12. There’s a clear difference in the two results, and I judge the category version to be wrong. Note that the log in the category version IS getting executed (and stops placed in the code are hit), so it’s not simply a case of the code somehow not getting used.
##A curious observation
Modified the category implementation slightly:
#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
Basically just changed the name of the static locale variable (in case there was some conflict with the static declared in the subclass) and added the extra NSLog. But look what that NSLog prints:
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
As you can see, the setLocale simply didn’t. The locale of the formatter is still en_GB. It appears that there is something “strange” about an init method in a category.
Solution
Duh!!
Manchmal hat man ein “Aha!!” Moment, manchmal ist es eher ein “Duh!!” Dies ist letzteres. In der Kategorie für initWithSafeLocale
das “Super” init
wurde als kodiert self = [super init];
. Dies initialisiert die SUPERCLASS von NSDateFormatter
, aber nicht init
das NSDateFormatter
Objekt selbst.
Anscheinend “prallt” diese Initialisierung ab, wenn diese Initialisierung übersprungen wird setLocale
, vermutlich wegen einer fehlenden Datenstruktur im Objekt. Das Ändern von init
zu self = [self init];
bewirkt, dass die NSDateFormatter
Initialisierung erfolgt, und setLocale
ist wieder glücklich.
Hier ist die “endgültige” Quelle für das .m der Kategorie:
#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
Beantwortet von – Hot Licks
Antwort geprüft von – Jay B. (FixError Admin)