¿Cuál es la forma óptima de almacenar un NSDate en NSUserDefaults?

174

Hay dos formas de almacenar un NSDate en NSUserDefaults que he encontrado.

Opción 1 - setObject: forKey:

// Set
NSDate *myDate = [NSDate date];
[[NSUserDefaults standardUserDefaults] setObject:myDate forKey:@"myDateKey"];

// Get
NSDate *myDate = (NSDate *)[[NSUserDefaults standardUserDefaults] objectForKey:@"myDateKey"];

Opción 2 - timeIntervalSince1970

// Set
NSDate *myDate = [NSDate date];
NSTimeInterval myDateTimeInterval = [myDate timeIntervalSince1970];
[[NSUserDefaults standardUserDefaults] setFloat:myDateTimeInterval forKey:@"myDateKey"];

// Get
NSTimeInterval myDateTimeInterval = [[NSUserDefaults standardUserDefaults] floatForKey:@"myDateKey"];
NSDate *myDate = [NSDate dateWithTimeIntervalSince1970:myDateTimeInterval];

Pros y contras

Opción 1

Esto parece ser compacto y lógico. Sin embargo, me preocupa que esto salga mal debido a los errores del formateador de fechas .

opcion 2

Esto parece ser torpe. Tampoco estoy seguro de la precisión de la misma: en una prueba que hice, cuando recuperé la fecha en que estaba fuera por 48 segundos, a pesar de que los Documentos de Apple decían que NSTimeInterval tiene "precisión de un segundo".

Requisitos

Cualquier método que elija, debe ser:

  1. Preciso hasta dentro de un segundo.

  2. Legible y confiable.

Mi pregunta

¿Es la inexactitud con la opción 2 porque estoy haciendo algo mal?

¿Cuál de estas dos opciones usarías?

¿Hay otra opción que no conozca?

¡Gracias!

John Gallagher
fuente

Respuestas:

380

Estás complicando innecesariamente las cosas. ¿Por qué está convirtiendo la fecha en un intervalo de tiempo (luego el intervalo de tiempo en una primitiva diferente)? Justo [sharedDefaults setObject:theDate forKey:@"theDateKey"]y listo. NSDate es uno de los "tipos principales" admitidos por el formato PLIST (fechas, números, cadenas, datos, diccionarios y matrices), por lo que puede almacenarlo directamente.

Consulte la documentación para la prueba.

Simplemente almacene y recupere la fecha directamente y mírela Hacer lo correcto (incluidas las zonas horarias, la precisión, etc.). No hay formateador involucrado, como han dicho otros.

Joshua Nozzi
fuente
8
Joshua, gracias por tu respuesta. La razón por la que probé el enfoque tonto de flotación enrevesada fue simplemente porque había visto a otro gran desarrollador hacerlo y pensé que lo había hecho por alguna buena razón. Obviamente no. Debería tener más confianza en mi propio instinto, que era usar setObject: forKey: y haberlo hecho.
John Gallagher
77
Soy totalmente violento con la sola idea de una complicación innecesaria, un buen rasgo para los desarrolladores y, en general, para la gente perezosa. :-)
Joshua Nozzi
Los casos en los que se usa este enfoque es principalmente cuando NSUserDefaults se usa como una implementación de almacenamiento de las preferencias del usuario para un middleware genérico ...
Coyote
@Coyote: en cuyo caso todavía se accede a través de NSUserDefaults o se analiza desde un archivo de lista de propiedades, por lo que el mismo acceso o análisis debería generar un objeto NSDate adecuado, que se puede convertir según sea necesario.
Joshua Nozzi
3
@JohnGallagher [barra lateral] No confunda la implementación específica de alguien con una pobre. Su sentimiento original "pensó que lo había hecho por alguna buena razón ... Obviamente no" solo podría ser válido si entendía todo el alcance de los requisitos de dicho desarrollador. Es presuntuoso y de mente cerrada etiquetar ciegamente su enfoque como "obviamente no hay una buena razón para hacerlo de esta manera". Dicho esto, sí, estaría de acuerdo con el enfoque de Joshua para mantenerlo simple si no tiene ninguna razón para hacerlo de otra manera.
dooleyo
14

Para la opción # 1, no creo que esté involucrado un formateador de fechas. Posiblemente debajo del capó, pero imagino que no está roto. La fecha se almacena en forma ISO 8601 .

Para la opción # 2, use -setDouble:forKey:y en -doubleForKeylugar de las floatversiones basadas. Esa podría ser la causa de sus errores de precisión.

John Calsbeek
fuente
Wow, John Gracias por su pronta respuesta. ¿Cuál usarías personalmente?
John Gallagher
8
Use la fecha directamente, no el intervalo de tiempo. Dudo que una API tan básica se rompa cuando tantas aplicaciones confían en ella.
John Calsbeek
Excelente. Ese fue mi instinto, pero había visto código de terceros por un desarrollador respetado que usaba el método float, así que pensé que habría alguna buena razón para que lo usara. Obviamente no. ¡Gracias de nuevo por su respuesta!
John Gallagher
1
Es posible que ese código que viste sea el desarrollador que no recuerda qué tipos se pueden almacenar directamente en una lista de propiedades.
John Calsbeek
2
Para el registro: los flotantes de 32 bits tienen solo 24 bits para precisión, por lo que 1970 hasta ahora son 40 años, que son 40 * 365 * 86400 segundos, y (40 * 365 * 86 400) / (2 ** 24) = error de 75 segundos . La precisión doble es lo que es un intervalo DateTime, y su precisión RAW ahora es mejor que una millonésima de segundo.
Tom Andersen
5

Use NSUserDefaults; las fechas se almacenan en hora zulú, por lo que no hay problemas de zona horaria de los que preocuparse. Almacénelo en su zona horaria, sáquelo en otra zona horaria, estará bien, el sistema maneja la conversión (no hay que preocuparse por el formateador de fechas).

Ben Gottlieb
fuente
0

Si está guardando la fecha de vencimiento de la API de Facebook Graph, usaría la * Opción 2 * .

La opción dos se puede convertir fácilmente en una cadena (usando stringWithFormat). Lo más importante, funciona para la API Graph.

Además, no tiene que preocuparse por el formato de su fecha. No tratar con NSDateFormatter vale la posibilidad de un error de 48 segundos.

Nate Symer
fuente