2009-12-05 20 views
15

Sto utilizzando un NSDateFormatter per analizzare una data RFC 822 su iPhone. Tuttavia, non è possibile specificare elementi facoltativi nel formato data. Ci sono un paio di parti opzionali nella specifica RFC 822 che sta rompendo l'analizzatore di date. Se non funziona niente, probabilmente dovrei scrivere un parser personalizzato per obbedire alle specifiche.Analisi di una data RFC 822 con NSDateFormatter

Ad esempio, il nome del giorno è opzionale nelle specifiche. Quindi sono valide entrambe queste date:

Tue, 01 Dec 2009 08:48:25 +0000 viene analizzato con il formato EEE, dd MMM yyyy HH:mm:ss z 01 Dec 2009 08:48:25 +0000 viene analizzato con il formato dd MMM yyyy HH:mm:ss z

Questo è quello che sono attualmente in uso:

+ (NSDateFormatter *)rfc822Formatter { 
    static NSDateFormatter *formatter = nil; 
    if (formatter == nil) { 
     formatter = [[NSDateFormatter alloc] init]; 
     NSLocale *enUS = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; 
     [formatter setLocale:enUS]; 
     [enUS release]; 
     [formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss z"]; 
    } 
    return formatter; 
} 

+ (NSDate *)dateFromRFC822:(NSString *)date { 
    NSDateFormatter *formatter = [NSDate rfc822Formatter]; 
    return [formatter dateFromString:date]; 
} 

E analizzando la data segue:

self.entry.published = [NSDate dateFromRFC822:self.currentString]; 

Un modo è provare entrambe le forme ats e accetta qualunque valore non nullo. Tuttavia, ci sono due parti opzionali nella specifica (nome del giorno e secondi) e ci sarebbero 4 possibili combinazioni. Ancora non male, ma è un po 'hacky.

risposta

4

Contare il numero di caratteri salienti prima di decidere quale formattatore utilizzare. Ad esempio, i due che hai hanno numeri diversi di virgole e spazi. Se nessun formato conosciuto corrisponde ai conteggi, allora non si sa nemmeno di provare ad analizzarlo come una data.

+0

Questa mi sembra la soluzione più pratica, dato che i nomi del mese e del giorno sono di lunghezza fissa e tutti gli altri valori sono numerici a lunghezza fissa. Molto più economico che provare i formati fino a quando uno funziona! –

+0

Implementata una soluzione di base. Non è molto soddisfatto, ma è il migliore finora :) Una virgola identifica la presenza del giorno della settimana e due punti aiutano a identificare i secondi. Sarebbe stato bello se la data includesse un riferimento alle specifiche che stava seguendo, dato che l'analisi delle date è davvero complicata in molte lingue con la moltitudine di formati. – Anurag

1

Credo che RFC 822 specifichi due componenti opzionali nell'ora di data: giorno della settimana e secondi oltre l'ora.

Come un hack, è possibile i simboli per i brevi giorni della settimana:

NSArray *shortWeekSymbols = [NSArray arrayWithObjects:@"Sun,", @"Mon,", @"Tue,", @"Wed,", @"Thu,", @"Fri,", @"Sat,", nil]; 
     [formatter setShortWeekdaySymbols:shortWeekSymbols]; 

Se si modifica il formato della data a questo: EEEdd MMM yyyy HH:mm:ss z. Sarai in grado di analizzare i tempi senza circa il giorno della settimana. Questo sembra consentire uno spazio anche dopo la virgola.

Per sicurezza, non dovresti solo impostare ciecamente i simboli in questo modo. Dovresti usare setShortWeekdaySymbols e iterare su di loro aggiungendo la virgola alla fine. Il motivo per cui sono potenzialmente diversi per ogni locale e il primo giorno potrebbe non essere domenica.

È interessante notare che il formato EEE, dd MMM yyyy HH:mm:ss z analizzerà le ore senza il giorno della settimana, ma la virgola deve essere presente, ad esempio , 01 Dec 2009 08:48:25 +0000. Quindi, potresti fare qualcosa come Steve, ma poi togli il giorno e passa al formattatore. Non avere la virgola nel formato non sembra consentire alla settimana di essere facoltativa. Strano.

Sfortunatamente, questo non è ancora d'aiuto con l'opzionale: ss nel formato. Ma potrebbe consentire di avere due formati anziché quattro.

+0

Grazie per il suggerimento. Penso che RFC 822 non menzioni la localizzazione e utilizzi solo il formato inglese. È comunque una buona idea aggiungere una virgola invece di codificare i valori. Ma poiché avrei ancora bisogno di controllare due combinazioni, è probabilmente una buona idea controllare i caratteri in anticipo invece di provare due volte. – Anurag

5

Ho usato il following method per analizzare date RFC822. Credo che in origine era da MWFeedParser:

+ (NSDate *)dateFromRFC822String:(NSString *)dateString { 

    // Create date formatter 
    static NSDateFormatter *dateFormatter = nil; 
    if (!dateFormatter) { 
     NSLocale *en_US_POSIX = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; 
     dateFormatter = [[NSDateFormatter alloc] init]; 
     [dateFormatter setLocale:en_US_POSIX]; 
     [dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; 
     [en_US_POSIX release]; 
    } 

    // Process 
    NSDate *date = nil; 
    NSString *RFC822String = [[NSString stringWithString:dateString] uppercaseString]; 
    if ([RFC822String rangeOfString:@","].location != NSNotFound) { 
     if (!date) { // Sun, 19 May 2002 15:21:36 GMT 
      [dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm:ss zzz"]; 
      date = [dateFormatter dateFromString:RFC822String]; 
     } 
     if (!date) { // Sun, 19 May 2002 15:21 GMT 
      [dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm zzz"]; 
      date = [dateFormatter dateFromString:RFC822String]; 
     } 
     if (!date) { // Sun, 19 May 2002 15:21:36 
      [dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm:ss"]; 
      date = [dateFormatter dateFromString:RFC822String]; 
     } 
     if (!date) { // Sun, 19 May 2002 15:21 
      [dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm"]; 
      date = [dateFormatter dateFromString:RFC822String]; 
     } 
    } else { 
     if (!date) { // 19 May 2002 15:21:36 GMT 
      [dateFormatter setDateFormat:@"d MMM yyyy HH:mm:ss zzz"]; 
      date = [dateFormatter dateFromString:RFC822String]; 
     } 
     if (!date) { // 19 May 2002 15:21 GMT 
      [dateFormatter setDateFormat:@"d MMM yyyy HH:mm zzz"]; 
      date = [dateFormatter dateFromString:RFC822String]; 
     } 
     if (!date) { // 19 May 2002 15:21:36 
      [dateFormatter setDateFormat:@"d MMM yyyy HH:mm:ss"]; 
      date = [dateFormatter dateFromString:RFC822String]; 
     } 
     if (!date) { // 19 May 2002 15:21 
      [dateFormatter setDateFormat:@"d MMM yyyy HH:mm"]; 
      date = [dateFormatter dateFromString:RFC822String]; 
     } 
    } 
    if (!date) NSLog(@"Could not parse RFC822 date: \"%@\" Possibly invalid format.", dateString); 
    return date; 

} 
+0

La modifica della stringa del formato della data è costosa, è possibile che si desideri creare un formattatore della data per ognuno e aggrapparsi per tutta la durata dell'app. – SomeGuy

0

Nel caso in cui questo è utile per chiunque altro .. ecco un estensione NSDate + RFC822String.swift sulla base di Simucal's answer.

memorizza nella cache anche l'ultimo formato della data utilizzato, che ha avuto successo, dal momento che l'impostazione del dateFormatter.dateFormat è costoso.

import Foundation 

private let dateFormatter: NSDateFormatter = { 
    let dateFormatter = NSDateFormatter() 
    dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") 
    dateFormatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) 

    return dateFormatter 
}() 

private let dateFormatsWithComma = ["EEE, d MMM yyyy HH:mm:ss zzz", "EEE, d MMM yyyy HH:mm zzz", "EEE, d MMM yyyy HH:mm:ss", "EEE, d MMM yyyy HH:mm"] 
private let dateFormatsWithoutComma = ["d MMM yyyy HH:mm:ss zzz", "d MMM yyyy HH:mm zzz", "d MMM yyyy HH:mm:ss", "d MMM yyyy HH:mm"] 

private var lastUsedDateFormatString: String? 

extension NSDate { 
    class func dateFromRFC822String(RFC822String: String) -> NSDate? { 
     let RFC822String = RFC822String.uppercaseString 

     if lastUsedDateFormatString != nil { 
      if let date = dateFormatter.dateFromString(RFC822String) { 
       return date 
      } 
     } 

     if RFC822String.containsString(",") { 
      for dateFormat in dateFormatsWithComma { 
       dateFormatter.dateFormat = dateFormat 
       if let date = dateFormatter.dateFromString(RFC822String) { 
        lastUsedDateFormatString = dateFormat 
        return date 
       } 
      } 
     } else { 
      for dateFormat in dateFormatsWithoutComma { 
       dateFormatter.dateFormat = dateFormat 
       if let date = dateFormatter.dateFromString(RFC822String) { 
        lastUsedDateFormatString = dateFormat 
        return date 
       } 
      } 
     } 

     return nil 
    } 
} 
Problemi correlati