Sembra che NSDateFormatter
abbia una "caratteristica" che ti morde inaspettatamente: se esegui una semplice operazione di formattazione "fissa" come:
NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:@"yyyyMMddHHmmss"];
NSString* dateStr = [fmt stringFromDate:someDate];
[fmt release];
Quindi funziona bene negli Stati Uniti e nella maggior parte dei locali FINO A ... qualcuno con il telefono impostato su una regione di 24 ore imposta l'interruttore di 12/24 ore nelle impostazioni su 12. Quindi quanto sopra inizia a virare su "AM" o "PM" su la fine della stringa risultante.
(Vedi, ad esempio, NSDateFormatter, sto facendo qualcosa di sbagliato o è un bug? )
(E vedi https://developer.apple.com/library/content/qa/qa1480/_index.html )
Apparentemente Apple ha dichiarato che questo è "BAD" - Broken As Designed, e non lo risolveranno.
L'elusione è apparentemente quella di impostare le impostazioni locali del formatter data per una regione specifica, generalmente gli Stati Uniti, ma questo è un po 'disordinato:
NSLocale *loc = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
[df setLocale: loc];
[loc release];
Non male in onsies-twosies, ma ho a che fare con una decina di app diverse e la prima che guardo ha 43 istanze di questo scenario.
Quindi qualche idea intelligente per una macro / classe ignorata / qualunque cosa per minimizzare lo sforzo di cambiare tutto, senza rendere il codice oscuro? (Il mio primo istinto è quello di sovrascrivere NSDateFormatter con una versione che imposti le impostazioni internazionali nel metodo init. Richiede la modifica di due righe: la riga alloc / init e l'importazione aggiunta.)
aggiunto
Questo è quello che ho escogitato finora - sembra funzionare in tutti gli scenari:
@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
Bounty!
Assegnerò la generosità alla migliore (legittima) proposta / critica che vedrò martedì a metà giornata. [Vedi sotto - scadenza estesa.]
Aggiornare
Per quanto riguarda la proposta di OMZ, ecco cosa sto trovando -
Ecco la versione della categoria - file h:
#import <Foundation/Foundation.h>
@interface NSDateFormatter (Locale)
- (id)initWithSafeLocale;
@end
File categoria 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
Il codice:
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];
Il risultato:
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)
Il telefono [fa che un iPod Touch] sia impostato sulla Gran Bretagna, con l'interruttore 12/24 impostato su 12. C'è una chiara differenza nei due risultati e ritengo che la versione della categoria sia errata. Si noti che il registro nella versione della categoria viene eseguito (e vengono bloccati gli stop inseriti nel codice), quindi non è semplicemente un caso in cui il codice in qualche modo non viene utilizzato.
Aggiornamento di Bounty:
Dal momento che non ho ancora ricevuto alcuna risposta applicabile, prorogherò la scadenza per un altro giorno o due.
Bounty termina tra 21 ore - andrà a chiunque faccia il massimo sforzo per aiutare, anche se la risposta non è davvero utile nel mio caso.
Un'osservazione curiosa
Modificata leggermente l'implementazione della categoria:
#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
Fondamentalmente ha appena cambiato il nome della variabile locale statica (nel caso ci fosse qualche conflitto con la statica dichiarata nella sottoclasse) e ha aggiunto il NSLog extra. Ma guarda cosa stampa 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
Come puoi vedere, setLocale semplicemente no. La locale del formatter è ancora en_GB. Sembra che ci sia qualcosa di "strano" in un metodo init in una categoria.
Risposta finale
Vedi la risposta accettata di seguito.
- (NSDateFormatterBehavior)formatterBehavior
?