Parece que NSDateFormatter
tiene una "característica" que te muerde inesperadamente: si haces una operación de formato "fijo" simple como:
NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:@"yyyyMMddHHmmss"];
NSString* dateStr = [fmt stringFromDate:someDate];
[fmt release];
Entonces funciona bien en los EE. UU. Y en la mayoría de los lugares HASTA ... alguien con su teléfono configurado en una región de 24 horas establece el interruptor de 12/24 horas en la configuración a 12. Luego lo anterior comienza a agregar "AM" o "PM" en El final de la cadena resultante.
(Ver, por ejemplo, NSDateFormatter, ¿estoy haciendo algo mal o es un error? )
(Y ver https://developer.apple.com/library/content/qa/qa1480/_index.html )
Aparentemente, Apple ha declarado que esto es "MALO", roto como está diseñado, y no lo van a arreglar.
Aparentemente, la elusión es establecer la configuración regional del formateador de fecha para una región específica, generalmente en los Estados Unidos, pero esto es un poco desordenado:
NSLocale *loc = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
[df setLocale: loc];
[loc release];
No está mal en onsies-twosies, pero estoy lidiando con unas diez aplicaciones diferentes, y la primera que miro tiene 43 instancias de este escenario.
Entonces, ¿alguna idea inteligente para una macro / clase anulada / lo que sea para minimizar el esfuerzo de cambiar todo, sin hacer que el código se oscurezca? (Mi primer instinto es anular NSDateFormatter con una versión que establezca la configuración regional en el método init. Requiere cambiar dos líneas: la línea alloc / init y la importación agregada).
Adicional
Esto es lo que se me ocurrió hasta ahora: parece funcionar en todos los escenarios:
@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
¡Generosidad!
Otorgaré la recompensa a la mejor sugerencia / crítica (legítima) que vea hasta el mediodía del martes. [Ver abajo - plazo extendido]
Actualizar
Re propuesta de OMZ, esto es lo que estoy encontrando:
Aquí está la versión de categoría - archivo h:
#import <Foundation/Foundation.h>
@interface NSDateFormatter (Locale)
- (id)initWithSafeLocale;
@end
Archivo de categoría 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
El código:
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];
El resultado:
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)
El teléfono [hace que un iPod Touch] esté configurado en Gran Bretaña, con el interruptor 12/24 configurado en 12. Hay una clara diferencia en los dos resultados, y considero que la versión de categoría es incorrecta. Tenga en cuenta que el registro en la versión de categoría SE está ejecutando (y las paradas colocadas en el código se golpean), por lo que no se trata simplemente de que el código de alguna manera no se esté utilizando.
Actualización de recompensa:
Como aún no he recibido ninguna respuesta aplicable, extenderé el plazo de recompensa por otro día o dos.
Bounty finaliza en 21 horas: irá a quien haga el mayor esfuerzo para ayudar, incluso si la respuesta no es realmente útil en mi caso.
Una curiosa observación
Se modificó ligeramente la implementación de la categoría:
#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
Básicamente, solo cambié el nombre de la variable de entorno local estático (en caso de que hubiera algún conflicto con el estático declarado en la subclase) y agregó el NSLog adicional. Pero mira lo que imprime 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
Como puede ver, el setLocale simplemente no lo hizo. La configuración regional del formateador sigue siendo en_GB. Parece que hay algo "extraño" sobre un método init en una categoría.
Respuesta final
Vea la respuesta aceptada a continuación.
- (NSDateFormatterBehavior)formatterBehavior
?