cocoa osx ios nsdateformatter rfc3339

cocoa - Analizar las fechas de rfc3339 con NSDateFormatter en iOS 4.xy MacOS X 10.6: ¿imposible?



osx (3)

El análisis de una fecha de rfc3339 con NSDateFormatter parece ser imposible, en el caso general. ¿Me equivoco? [Editar 2 años después: ¡ahora hay un camino! Ver a continuación y nota al pie.]

Un servicio web no especialmente maleable me está dando fechas como:

2009-12-31T00:00:00-06:00

Rfc3339 compatible con la salida predeterminada de la biblioteca jaxb que están utilizando. Tenga en cuenta los dos puntos, que requiere rfc3339 cuando el desplazamiento no es una "z" literal:

time-numoffset = ("+" / "-") time-hour ":" time-minute time-offset = "Z" / time-numoffset

Quiero analizar estos en NSDates.

NSDateFormatter quiere patrones en la sintaxis especificada por Unicode , que ofrece símbolos de campo de fecha para zonas horarias como "PDT", "-0800", "GMT-08: 00" pero no "-08: 00".

Google y otras preguntas similares SO solo producen formatos de fecha como

[myDateParser setDateFormat:@"yyyy''-''MM''-''dd''T''HH'':''mm'':''ssZ"]; /* or: */ [myDateParser setDateFormat:@"yyyy''-''MM''-''dd''T''HH'':''mm'':''ss''Z''"];

El último de los cuales requiere una "Z" literal, y el primero insiste en la ausencia de un colon o la presencia de un "GMT". Sin embargo, parecían funcionar antes de ios 4.x (posiblemente descartando completamente el desplazamiento de tz; mis datos no son claros).

Mis opciones en este momento son un montón lamentable:

  • descubra algún especificador de formato no documentado, o algún modo extraño para poner NSDateFormatter en, que aceptará el colon vago: longshot, probablemente inexistente. [nota]
  • convencer a mi editor de servicios para que convierta todas las fechas en zulú y especifique ''Z'': políticamente desafiante.
  • escribir mi propia subclase NSFormatter o investigar buena vieja strptime_l : work. :)
  • manipular por cadena mi entrada y eliminar el último punto: frágil y feo, pero el camino probable de menor resistencia.

¿He entendido la situación con precisión, que NSDateFormatter actual sigue unicode estrictamente sin extensiones; y los formatos Unicode son insuficientes para describir completamente una fecha rfc3339?

[NOTA] Vuelvo a esto tres años después para agregar un pequeño apéndice: Unicode y Apple han agregado esta característica a las cadenas de formato, a partir de iOS6 / OSX10.8. Compare La última revisión a la fecha de este escrito con su predecesora inmediata , y observe la adición de 5 "Z" s, que produce un formato de zona como "-08: 00". Entonces, si puede salirse con la ayuda de abandonar 5.x / 10.7, hay una nueva forma correcta de hacerlo. Dejaré la respuesta anterior en pie, ya que sigue siendo el mejor enfoque cuando se requiere compatibilidad con versiones anteriores.


El análisis de cadenas de fechas en Cocoa puede ser un problema, especialmente si tiene que lidiar con las fechas generadas por los servicios web basados ​​en .NET.

Sugiero mirar la categoría NSDate + InternetDateTime que Michael Waterfall tiene en NSDate como parte de su proyecto MWFeedParser en github. Me funcionó bien al analizar exactamente el formato de la fecha que describes.

https://github.com/mwaterfall/MWFeedParser/


- getObjectValue: forString: rango: error: en realidad puede analizar las fechas RFC3339 correctamente. No tengo idea de por qué - dateWithString: no se puede:

// RFC3339 date formatting NSString *dateString = @"2012-04-11T18:34:19+00:00"; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; formatter.dateFormat = @"yyyy-MM-dd''T''HH:mm:ssZ"; NSDate *date; NSError *error; [formatter getObjectValue:&date forString:dateString range:nil error:&error];


Los caracteres de formato de cadena no están documentados en ninguna parte de la documentación de Apple. En su lugar, hay un enlace oculto en un documento que apunta al estándar Unicode en

http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_Patterns

Usar la información en ese enlace es bastante simple:

- (NSDate*)dateFromRFC3339String:(NSString*)aString { static NSDateFormatter* sRFC3339DateFormatter = nil; static NSDateFormatter* sRFC3339DateFormatterSubSeconds = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; sRFC3339DateFormatter = [[NSDateFormatter alloc] init]; [sRFC3339DateFormatter setLocale:enUSPOSIXLocale]; [sRFC3339DateFormatter setDateFormat:@"yyyy''-''MM''-''dd''T''HH'':''mm'':''ssXXXXX"]; [sRFC3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; sRFC3339DateFormatterSubSeconds = [[NSDateFormatter alloc] init]; [sRFC3339DateFormatterSubSeconds setLocale:enUSPOSIXLocale]; [sRFC3339DateFormatterSubSeconds setDateFormat:@"yyyy''-''MM''-''dd''T''HH'':''mm'':''ss.SSSSSSXXXXX"]; [sRFC3339DateFormatterSubSeconds setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; }); NSDate* date = [sRFC3339DateFormatter dateFromString:aString]; if (date == nil) date = [sRFC3339DateFormatterSubSeconds dateFromString:aString]; return date; }