ios example Quelle est la meilleure façon de gérer les paramètres régionaux NSDateFormatter "feechur"?



swift dateformatter month (4)

Puis-je suggérer quelque chose de totalement différent, car pour être honnête, tout cela est en train de couler dans un trou de lapin.

Vous devriez utiliser un NSDateFormatter avec l'ensemble dateFormat et les locale forcés à en_US_POSIX pour recevoir les dates (à partir des serveurs / API).

Ensuite, vous devriez utiliser un NSDateFormatter différent pour l'interface utilisateur que vous allez définir les propriétés timeStyle / dateStyle - de cette façon, vous n'avez pas de dateFormat explicite définie par vous-même, supposant ainsi faussement que ce format sera utilisé.

Cela signifie que l'interface utilisateur est guidée par les préférences de l'utilisateur (am / pm vs 24 heures et les chaînes de date formatées correctement selon les paramètres iOS), alors que les dates qui entrent dans votre application sont toujours analysées correctement dans un NSDate pour vous d'utiliser.

Il semble que NSDateFormatter a une "fonctionnalité" qui vous mord de façon inattendue: Si vous faites une simple opération de format "fixe" comme:

NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:@"yyyyMMddHHmmss"];
NSString* dateStr = [fmt stringFromDate:someDate];
[fmt release];

Ensuite, cela fonctionne très 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 règle le commutateur 12/24 heures sur 12. Ensuite, le pointage ci-dessus commence sur "AM" ou "PM". 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 bug? )

(Et voir https://developer.apple.com/library/content/qa/qa1480/_index.html )

Apparemment, Apple a déclaré que c'était "MAUVAIS" - Broken As Designed, et ils ne vont pas le réparer.

Le contournement est apparemment pour définir les paramètres régionaux du formatter de date pour une région spécifique, généralement les États-Unis, mais c'est un peu brouillon:

NSLocale *loc = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
[df setLocale: loc];
[loc release];

Pas si mal dans les onsies-twosies, mais je fais face à une dizaine d'applications différentes, et la première que je regarde a 43 occurrences de ce scénario.

Donc des idées astucieuses pour une classe macro / surchargée / quoi que ce soit pour minimiser l'effort de tout changer, sans que le code ne soit obscurci? (Mon premier instinct est de surcharger NSDateFormatter avec une version qui définirait les paramètres régionaux dans la méthode init.Vous devez changer deux lignes - la ligne alloc / init et l'import ajouté.)

Ajoutée

C'est ce que j'ai trouvé 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!

J'accorderai la prime à la meilleure suggestion / critique (légitime) que je verrai à la mi-journée de mardi. [Voir ci-dessous - délai prolongé.]

Mettre à jour

La proposition de Re OMZ, voici ce que je trouve -

Voici le fichier version - h de la catégorie:

#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 [faire en sorte qu'un iPod Touch] est réglé sur la Grande-Bretagne, avec le commutateur 12/24 réglé à 12. Il y a une différence nette 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 la catégorie IS est exécuté (et que les arrêts placés dans le code sont touchés), donc ce n'est pas simplement un cas où le code n'est pas utilisé.

Mise à jour de Bounty:

Comme je n'ai reçu aucune réponse, je vais prolonger la date limite pour un autre jour ou deux.

Bounty se termine dans 21 heures - il 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

Modification légère de 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 juste changé le nom de la variable locale statique (dans le cas où il y avait un conflit avec l'électricité statique déclarée dans la sous-classe) et ajouté 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 constater, setLocale ne l'a tout simplement pas fait. Les paramètres régionaux du formateur sont toujours en_GB. Il semble qu'il y ait quelque chose d'étrange dans une méthode init dans une catégorie.

Réponse finale

Voir la réponse acceptée ci-dessous.


Answer #1

Au lieu de sous- NSDateFormatter , vous pouvez créer une catégorie NSDateFormatter avec un initialiseur supplémentaire qui prend soin d'assigner les paramètres régionaux et éventuellement aussi une chaîne de format, de sorte que vous ayez un formateur prêt à l'emploi juste après l'avoir initialisé.

@interface NSDateFormatter (LocaleAdditions)

- (id)initWithPOSIXLocaleAndFormat:(NSString *)formatString;

@end

@implementation NSDateFormatter (LocaleAdditions)

- (id)initWithPOSIXLocaleAndFormat:(NSString *)formatString {
    self = [super init];
    if (self) {
        NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
        [self setLocale:locale];
        [locale release];
        [self setFormat:formatString];
    }
    return self;
}

@end

Ensuite, vous pouvez utiliser NSDateFormatter n'importe où dans votre code avec juste:

NSDateFormatter* fmt = [[NSDateFormatter alloc] initWithPOSIXLocaleAndFormat:@"yyyyMMddHHmmss"];

Vous pouvez préfixer votre méthode de catégorie en quelque sorte pour éviter les conflits de noms, juste au cas où Apple décide d'ajouter une telle méthode dans une future version du système d'exploitation.

Si vous utilisez toujours le (s) même (s) format (s) de date, vous pouvez également ajouter des méthodes de catégorie +sharedRFC3339DateFormatter instances singleton avec certaines configurations (quelque chose comme +sharedRFC3339DateFormatter ). Sachez toutefois que NSDateFormatter n'est pas NSDateFormatter threads et que vous devez utiliser des verrous ou des blocs @synchronized lorsque vous utilisez la même instance à partir de plusieurs threads.


Answer #2

Voici la solution à ce problème dans la version rapide. En swift, nous pouvons utiliser l'extension au lieu de la catégorie. Donc, ici, j'ai créé l'extension pour le DateFormatter et à l'intérieur que initWithSafeLocale renvoie le DateFormatter avec le Locale pertinent, Ici dans notre cas, c'est en_US_POSIX, En dehors de cela a également fourni quelques méthodes de formation de la date.

  • Swift 4

    extension DateFormatter {
    
    private static var dateFormatter = DateFormatter()
    
    class func initWithSafeLocale(withDateFormat dateFormat: String? = nil) -> DateFormatter {
    
        dateFormatter = DateFormatter()
    
        var en_US_POSIX: Locale? = nil;
    
        if (en_US_POSIX == nil) {
            en_US_POSIX = Locale.init(identifier: "en_US_POSIX")
        }
        dateFormatter.locale = en_US_POSIX
    
        if dateFormat != nil, let format = dateFormat {
            dateFormatter.dateFormat = format
        }else{
            dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        }
        return dateFormatter
    }
    
    // ------------------------------------------------------------------------------------------
    
    class func getDateFromString(string: String, fromFormat dateFormat: String? = nil) -> Date? {
    
        if dateFormat != nil, let format = dateFormat {
            dateFormatter = DateFormatter.initWithSafeLocale(withDateFormat: format)
        }else{
            dateFormatter = DateFormatter.initWithSafeLocale()
        }
        guard let date = dateFormatter.date(from: string) else {
            return nil
        }
        return date
    }
    
    // ------------------------------------------------------------------------------------------
    
    class func getStringFromDate(date: Date, fromDateFormat dateFormat: String? = nil)-> String {
    
        if dateFormat != nil, let format = dateFormat {
            dateFormatter = DateFormatter.initWithSafeLocale(withDateFormat: format)
        }else{
            dateFormatter = DateFormatter.initWithSafeLocale()
        }
    
        let string = dateFormatter.string(from: date)
    
        return string
    }   }
    
  • description d'usage:

    let date = DateFormatter.getDateFromString(string: "11-07-2001”, fromFormat: "dd-MM-yyyy")
    print("custom date : \(date)")
    let dateFormatter = DateFormatter.initWithSafeLocale(withDateFormat: "yyyy-MM-dd HH:mm:ss")
    let dt = DateFormatter.getDateFromString(string: "2001-05-05 12:34:56")
    print("base date = \(dt)")
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    let dateString = dateFormatter.string(from: Date())
    print("dateString = " + dateString)
    let date1 = dateFormatter.date(from: "2001-05-05 12:34:56")
    print("date1 = \(String(describing: date1))")
    let date2 = dateFormatter.date(from: "2001-05-05 22:34:56")
    print("date2 = \(String(describing: date2))")
    let date3 = dateFormatter.date(from: "2001-05-05 12:34:56PM")
    print("date3 = \(String(describing: date3))")
    let date4 = dateFormatter.date(from: "2001-05-05 12:34:56 PM")
    print("date4 = \(String(describing: date4))")
    

Answer #3

Duh !!

Parfois vous avez un "Aha !!" moment, parfois c'est plus d'un "Duh !!" C'est le dernier. Dans la catégorie pour initWithSafeLocale le "super" init était codé self = [super init]; . Cela crée la SUPERCLASS de NSDateFormatter mais NSDateFormatter pas l'objet NSDateFormatter lui-même.

Apparemment, lorsque cette initialisation est ignorée, setLocale "rebondit", vraisemblablement à cause d'une structure de données manquante dans l'objet. Changer l' init en self = [self init]; provoque l'initialisation de setLocale et setLocale est de nouveau satisfait.

Voici la source "finale" pour la catégorie .m:

#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




nsdateformatter