web-dev-qa-db-fra.com

Comment obtenir une date ISO 8601 sur iOS?

Il est assez facile d’obtenir la chaîne de date ISO 8601 (par exemple, 2004-02-12T15:19:21+00:00) Dans PHP via date('c')], mais comment l'obtenir dans Objective- C (iPhone)? Y a-t-il un moyen aussi court de le faire?

Voici le long moyen que j'ai trouvé pour le faire:

NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";

NSDate *now = [NSDate date];
NSString *formattedDateString = [dateFormatter stringFromDate:now];
NSLog(@"ISO-8601 date: %@", formattedDateString);

// Output: ISO-8601 date: 2013-04-27T13:27:50-0700

Il semble y avoir énormément de rigueur pour quelque chose d'aussi central.

106
JohnK

Utilisez NSDateFormatter:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];   
NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
[dateFormatter setLocale:enUSPOSIXLocale];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
[dateFormatter setCalendar:[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]];

NSDate *now = [NSDate date];
NSString *iso8601String = [dateFormatter stringFromDate:now];

Et à Swift:

let dateFormatter = DateFormatter()
let enUSPosixLocale = Locale(identifier: "en_US_POSIX")
dateFormatter.locale = enUSPosixLocale
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
dateFormatter.calendar = Calendar(identifier: .gregorian)

let iso8601String = dateFormatter.string(from: Date())
207
rmaddy

iOS 10 introduit un nouveau NSISO8601DateFormatter classe pour gérer cela. Si vous utilisez Swift 3, votre code ressemblerait à ceci:

let formatter = ISO8601DateFormatter()
let date = formatter.date(from: "2016-08-26T12:39:00Z")
let string = formatter.string(from: Date())
48
TwoStraws

En complément de la réponse de maddy, le format de fuseau horaire doit être "ZZZZZ" (5 fois Z) pour ISO 8601 au lieu d'un seul "Z" (format RFC 822). Au moins sur iOS 6.

(voir http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns )

27
Etienne

Un problème souvent négligé est que les chaînes au format ISO 8601 peuvent prendre plusieurs millisecondes et peuvent ne pas l'être.

En d'autres termes, "2016-12-31T23: 59: 59.9999999" et "2016-12-01T00: 00: 00" sont légitimes, mais si vous utilisez un programme de formatage de date de type statique, l'un d'eux ne sera pas analysé. .

À partir de iOS 10 , vous devez utiliser ISO8601DateFormatter qui gère toutes les variations des chaînes de date ISO 8601. Voir exemple ci-dessous:

let date = Date()
var string: String

let formatter = ISO8601DateFormatter()
string = formatter.string(from: date)

let GMT = TimeZone(abbreviation: "GMT")
let options: ISO8601DateFormatOptions = [.withInternetDateTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withTimeZone]
string = ISO8601DateFormatter.string(from: date, timeZone: GMT, formatOptions: options)

Pour iOS 9 et inférieur , utilisez l’approche suivante avec plusieurs formateurs de données.

Je n'ai pas trouvé de réponse qui couvre les deux cas et résume cette différence subtile. Voici la solution qui y répond:

extension DateFormatter {

    static let iso8601DateFormatter: DateFormatter = {
        let enUSPOSIXLocale = Locale(identifier: "en_US_POSIX")
        let iso8601DateFormatter = DateFormatter()
        iso8601DateFormatter.locale = enUSPOSIXLocale
        iso8601DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
        iso8601DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
        return iso8601DateFormatter
    }()

    static let iso8601WithoutMillisecondsDateFormatter: DateFormatter = {
        let enUSPOSIXLocale = Locale(identifier: "en_US_POSIX")
        let iso8601DateFormatter = DateFormatter()
        iso8601DateFormatter.locale = enUSPOSIXLocale
        iso8601DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
        iso8601DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
        return iso8601DateFormatter
    }()

    static func date(fromISO8601String string: String) -> Date? {
        if let dateWithMilliseconds = iso8601DateFormatter.date(from: string) {
            return dateWithMilliseconds
        }

        if let dateWithoutMilliseconds = iso8601WithoutMillisecondsDateFormatter.date(from: string) {
            return dateWithoutMilliseconds
        }

        return nil
    }
}

Utilisation:

let dateToString = "2016-12-31T23:59:59.9999999"
let dateTo = DateFormatter.date(fromISO8601String: dateToString)
// dateTo: 2016-12-31 23:59:59 +0000

let dateFromString = "2016-12-01T00:00:00"
let dateFrom = DateFormatter.date(fromISO8601String: dateFromString)
// dateFrom: 2016-12-01 00:00:00 +0000

Je recommande également de vérifier article Apple à propos des formateurs de date.

14
Vadim Bulavin

Donc utilisez la catégorie de Sam Soffee sur NSDate trouvée ici . Avec ce code ajouté à votre projet, vous pouvez désormais utiliser une seule méthode sur NSDate:

- (NSString *)sam_ISO8601String

Ce n'est pas seulement une ligne, c'est beaucoup plus rapide que l'approche NSDateFormatter, car elle est écrite en C pur.

8
David H

Depuis IS8601 , les problèmes sont la représentation et le fuseau horaire

  • ISO 8601 = year-month-day time timezone
  • Pour la date et l'heure, il existe des formats de base (AAAAMMJJ, hhmmss, ...) et étendu (AAAA-MM-JJ, hh: mm: ss, ...)
  • Le fuseau horaire peut être zoulou, offset ou GMT
  • Le séparateur pour la date et l'heure peut être un espace ou T
  • Il existe un format de semaine pour la date, mais il est rarement utilisé
  • Le fuseau horaire peut être beaucoup d'espaces après
  • La seconde est optionnelle

Voici quelques chaînes valides

2016-04-08T10:25:30Z
2016-04-08 11:25:30+0100
2016-04-08 202530GMT+1000
20160408 08:25:30-02:00
2016-04-08 11:25:30     +0100

Solutions

NSDateFormatter

Donc, voici le format que j'utilise dans onmyway133 ISO8601

let formatter = NSDateFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
formatter.dateFormat = "yyyyMMdd HHmmssZ"

À propos de l'identifiant Ztable des symboles de champs date

Z: The ISO8601 basic format with hours, minutes and optional seconds fields. The format is equivalent to RFC 822 zone format (when optional seconds field is absent)

À propos de localeMise en forme des données à l’aide des paramètres régionaux

Les paramètres régionaux représentent les choix de formatage pour un utilisateur particulier, pas la langue préférée de l'utilisateur. Ce sont souvent les mêmes mais peuvent être différents. Par exemple, un anglophone vivant en Allemagne peut choisir l'anglais comme langue et l'Allemagne comme région.

À propos de en_US_POSIXQuestions techniques QA1480, NSDateFormatter et Internet

D'autre part, si vous utilisez des dates au format fixe, vous devez d'abord définir les paramètres régionaux du formateur de date sur quelque chose correspondant à votre format fixe. Dans la plupart des cas, la meilleure langue à choisir est "en_US_POSIX", une langue spécialement conçue pour produire des résultats en anglais américain indépendamment des préférences de l'utilisateur et du système. "en_US_POSIX" est également invariant dans le temps (si les États-Unis, à un moment donné, modifient le format des dates, "en_US" changera pour refléter le nouveau comportement, mais "en_US_POSIX" ne le sera pas) et entre les machines ( "en_US_POSIX" fonctionne de la même manière sur iOS que sur OS X et sur les autres plates-formes).

Questions connexes intéressantes

6
onmyway133

Il suffit d'utiliser NSISO8601DateFormatter à partir du framework Foundation.

let isoDateFormatter = ISO8601DateFormatter()
print("ISO8601 string: \(isoDateFormatter.string(from: Date()))")
// ISO8601 string: 2018-03-21T19:11:46Z

https://developer.Apple.com/documentation/foundation/nsiso8601dateformatter?language=objc

4
LLIAJLbHOu

Sur la base de cet élément essentiel: https://github.com/justinmakaila/NSDate-ISO-8601/blob/master/NSDateISO8601.Swift , la méthode suivante peut être utilisée pour convertir NSDate en chaîne de date ISO 8601 au format aaaa-MM-jj'T'HH: mm: ss.SSSZ

-(NSString *)getISO8601String
    {
        static NSDateFormatter *formatter = nil;
        if (!formatter)
        {
            formatter = [[NSDateFormatter alloc] init];
            [formatter setLocale: [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
            formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation: @"UTC"];
            [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS"];

        }
        NSString *iso8601String = [formatter stringFromDate: self];
        return [iso8601String stringByAppendingString: @"Z"];
    }
3
Zack Zhu

Ceci est un peu plus simple et met la date en UTC.

extension NSDate
{
    func iso8601() -> String
    {
        let dateFormatter = NSDateFormatter()
        dateFormatter.timeZone = NSTimeZone(name: "UTC")
        let iso8601String = dateFormatter.stringFromDate(NSDate())
        return iso8601String
    }
}

let date = NSDate().iso8601()
0
iphaaw