Il semble que cela NSDateFormatter
a une "fonctionnalité" qui vous mord de manière inattendue: Si vous effectuez une simple opération de format "fixe" telle que:
NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:@"yyyyMMddHHmmss"];
NSString* dateStr = [fmt stringFromDate:someDate];
[fmt release];
Ensuite, cela fonctionne bien aux États-Unis et dans la plupart des régions JUSQU'À ce que ... quelqu'un avec son téléphone réglé sur une région de 24 heures règle le commutateur 12/24 heures dans les paramètres sur 12. Ensuite, ce qui précède commence à clouer "AM" ou "PM" sur la fin de la chaîne résultante.
(Voir, par exemple, NSDateFormatter, est-ce que je fais quelque chose de mal ou est-ce un bogue? )
(Et voir https://developer.apple.com/library/content/qa/qa1480/_index.html )
Apparemment, Apple a déclaré que c'était "MAUVAIS" - Cassé comme conçu, et ils ne vont pas le réparer.
Le contournement consiste apparemment à définir les paramètres régionaux du formateur de date pour une région spécifique, généralement les États-Unis, mais c'est un peu compliqué:
NSLocale *loc = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
[df setLocale: loc];
[loc release];
Pas trop mal dans onsies-twosies, mais j'ai affaire à une dizaine d'applications différentes, et la première que je regarde contient 43 instances de ce scénario.
Alors, des idées intelligentes pour une macro / classe remplacée / quoi que ce soit pour minimiser l'effort de tout changer, sans rendre le code obscur? (Mon premier instinct est de remplacer NSDateFormatter par une version qui définirait les paramètres régionaux dans la méthode init. Nécessite de changer deux lignes - la ligne alloc / init et l'importation ajoutée.)
Ajoutée
C'est ce que j'ai proposé jusqu'à présent - semble fonctionner dans tous les scénarios:
@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
Prime!
Je vais attribuer la prime à la meilleure suggestion / critique (légitime) que je vois d'ici mardi midi. [Voir ci-dessous - délai prolongé.]
Mettre à jour
Concernant la proposition d'OMZ, voici ce que je trouve -
Voici la version de la catégorie - fichier h:
#import <Foundation/Foundation.h>
@interface NSDateFormatter (Locale)
- (id)initWithSafeLocale;
@end
Fichier de catégorie 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
Le 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];
Le résultat:
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)
Le téléphone [faites qu'un iPod Touch] est réglé sur la Grande-Bretagne, avec le commutateur 12/24 réglé sur 12. Il y a une nette différence entre les deux résultats, et je juge que la version de la catégorie est fausse. Notez que le journal dans la version de catégorie EST en cours d'exécution (et les arrêts placés dans le code sont frappés), donc ce n'est pas simplement un cas où le code n'est pas utilisé d'une manière ou d'une autre.
Mise à jour de la prime:
Comme je n'ai pas encore reçu de réponses applicables, je prolongerai la date limite de prime d'un jour ou deux.
La prime se termine dans 21 heures - elle ira à celui qui fait le plus d'efforts pour aider, même si la réponse n'est pas vraiment utile dans mon cas.
Une observation curieuse
Modifié légèrement l'implémentation de la catégorie:
#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
Fondamentalement, il suffit de changer le nom de la variable locale statique (au cas où il y aurait un conflit avec le statique déclaré dans la sous-classe) et d'ajouter le NSLog supplémentaire. Mais regardez ce que NSLog imprime:
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
Comme vous pouvez le voir, le setLocale ne l'a tout simplement pas. Les paramètres régionaux du formateur sont toujours en_GB. Il semble qu'il y ait quelque chose d '"étrange" à propos d'une méthode init dans une catégorie.
Réponse finale
Voir la réponse acceptée ci-dessous.
- (NSDateFormatterBehavior)formatterBehavior
?