Il semble que NSDateFormatter
a une "fonction" qui vous mord à l'improviste : 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 pays, JUSQU'À ce que quelqu'un dont le téléphone est réglé sur une région de 24 heures mette le commutateur 12/24 heures sur 12 dans les paramètres. Le système commence alors à ajouter "AM" ou "PM" à la fin de la chaîne de caractères résultante.
(Voir par exemple NSDateFormatter, est-ce que je fais quelque chose de mal ou est-ce que c'est un bug ? )
(Et voir https://developer.apple.com/library/content/qa/qa1480/_index.html )
Apparemment, Apple a déclaré qu'il s'agissait d'un problème "BAD" (Broken As Designed) et n'a pas l'intention de le résoudre.
La solution consiste apparemment à définir la locale 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];
Ce n'est pas si mal, mais j'ai affaire à une dizaine d'applications différentes, et la première que je consulte contient 43 cas de ce scénario.
Alors, des idées astucieuses pour une macro/classe surchargée/quoi que ce soit pour minimiser l'effort de tout changer, sans rendre le code trop obscur ? (Mon premier réflexe est de surcharger NSDateFormatter avec une version qui définirait la locale dans la méthode init. Il faut changer deux lignes - la ligne alloc/init et l'import ajouté).
Ajouté
Voici ce que j'ai trouvé jusqu'à présent - cela semble fonctionner dans tous les cas de figure :
@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 !
J'attribuerai la prime à la meilleure suggestion/critique (légitime) que je verrai d'ici mardi à la mi-journée. (Voir ci-dessous -- délai prolongé).
Mise à jour
Concernant la proposition de l'OMZ, voici ce que je constate
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 (ou plutôt l'iPod Touch) est réglé sur la Grande-Bretagne et le commutateur 12/24 sur 12. Il y a une nette différence entre les deux résultats, et je considère que la version de la catégorie est erronée. Notez que le journal de la version de la catégorie EST exécuté (et que les arrêts placés dans le code sont atteints), de sorte qu'il ne s'agit pas simplement d'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 pertinentes, je prolonge le délai de la prime d'un jour ou deux.
La prime se termine dans 21 heures - elle ira à celui qui fera le plus d'efforts pour aider, même si la réponse n'est pas vraiment utile dans mon cas.
Une observation curieuse
La mise en œuvre de la catégorie a été légèrement modifiée :
#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
J'ai simplement changé le nom de la variable locale statique (au cas où il y aurait un conflit avec la variable statique déclarée dans la sous-classe) et j'ai ajouté le NSLog supplémentaire. Mais regardez ce que ce 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 constater, le setLocale n'a tout simplement pas fonctionné. La locale du formateur est 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 le réponse acceptée ci-dessous.
5 votes
Moshe, je ne sais pas pourquoi vous avez choisi de modifier le titre. "Feechur" est un terme légitime dans l'art (et ce depuis une trentaine d'années), qui désigne un aspect ou une caractéristique d'un logiciel qui est suffisamment mal conçu pour être considéré comme un bogue, même si les auteurs refusent de l'admettre.
1 votes
Lors de la conversion d'une chaîne en date, la chaîne doit correspondre exactement à la description du formateur - il s'agit d'une question qui n'a rien à voir avec celle de la localité.
0 votes
Les différentes chaînes de dates sont là pour tester les différentes configurations possibles, correctes et erronées. Je sais que certaines d'entre elles sont invalides, compte tenu de la chaîne de formatage.
0 votes
Avez-vous expérimenté différentes valeurs de
- (NSDateFormatterBehavior)formatterBehavior
?0 votes
Je ne l'ai pas encore expérimenté. Les spécifications sont contradictoires quant à la possibilité de les modifier dans iOS. La description principale dit "iOS Note : iOS ne supporte que le comportement 10.4+", alors que la section NSDateFormatterBehavior dit que les deux modes sont disponibles (mais il se peut qu'elle ne parle que des constantes).
0 votes
Je ne serais pas surpris que seul le comportement de la 10.4+ soit disponible
0 votes
@HotLicks Je suis un peu confus, est-ce que ce problème persiste toujours dans iOS 6 ? Ou est-ce qu'Apple l'a résolu ?
0 votes
@JohnsonMathew -- AFAIK, Apple a déclaré que sa méthode était "correcte", donc ils ne la changeront pas. A moins qu'ils ne changent d'avis.
0 votes
NSDateFormatter est-il thread-safe pour l'appeler à partir de plusieurs threads ?