views:

727

answers:

3

There's two ways of storing an NSDate in NSUserDefaults that I've come across.

Option 1 - setObject:forKey:

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

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

Option 2 - timeIntervalSince1970

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

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

Pros and Cons

Option 1

This seems to be compact and logical. However, I've got worries about this going wrong because of Date Formatter bugs.

Option 2

This seems to be clumsy. I'm also unsure of the accuracy of it - in one test I did, when I retrieved the date back it was out by 48 seconds, despite the Apple Docs saying that NSTimeInterval has "subsecond accuracy".

Requirements

Whatever method I choose, it must be:

  1. Precise to within a second.

  2. Readable and reliable.

My Question

Is the inaccuracy with Option 2 because I'm doing something wrong?

Which out of these two options would you use?

Is there another option that I'm not aware of?

Thanks!

+1  A: 

For option #1, I don't believe a date formatter is involved. Possibly under the hood, but I imagine it's not broken. The date is stored in ISO 8601 form.

For option #2, use -setDouble:forKey: and -doubleForKey instead of the float-based versions. That might be the cause of your precision errors.

John Calsbeek
Wow, John. Thanks for your very speedy answer. Which would you personally use?
John Gallagher
Use the date directly, not the time interval. I doubt that such a basic API is broken when so many apps rely on it.
John Calsbeek
Excellent. That was my instinct, but I'd seen third party code by a respected dev that used the float method so I thought there would be some good reason he'd used it. Obviously not.Thanks again for your answer!
John Gallagher
See my answer below - why on earth would you convert the date to something else when you can store it directly? :-)
Joshua Nozzi
No problem with storing NSDate directly, but float versus double isn't the issue here. The number is in seconds, so the difference between float and double accuracy is way down in the noise. If you were seeing an error of 48s, you were almost certainly writing the number when you didn't expect to. This amount of error isn't rounding, and doesn't sound like data-mismatch. I'd put NSLog() statements around the read and write and make sure you're really reading and writing what you think you are. That said, I'd also store NSDate as NSDate.
Rob Napier
It's possible that that code you saw was the dev not remembering which types can be stored directly in a property list.
John Calsbeek
I find the 48 second mismatch that I observed also very puzzling. As soon as I switched to storing NSDates directly, this mismatch went away. I stepped through my code with the debugger and everything was working exactly as expected, except when it retrieved the float the conversion was messed up.
John Gallagher
My theory is that making it a double would have probably prevented this error - a massive number (x10^9) was being stored and so some digits will be been chopped off with a float thus resulting in the error. I think. :) I've got far too much to do to investigate any further (although strangely, I still have time to type this message...!)
John Gallagher
+2  A: 

Use NSUserDefaults; dates are stored in Zulu time, so there are no time zone issues to worry about. Store it in your time zone, pull it out in another time zone, you'll be fine, the system handles the conversion (no date formatter to worry about).

Ben Gottlieb
+9  A: 

You are needlessly complicating things. Why are you converting the date to a time interval (then the time interval to a different primitive)? Just [sharedDefaults setObject:theDate forKey:@"theDateKey"] and be done with it. NSDate is one of the "main types" supported by the PLIST format (dates, numbers, strings, data, dictionaries, and arrays), so you can just store it directly.

See the documentation for proof.

Just store and retrieve the date directly and watch it Do The Right Thing (including time zones, precision, etc). There is no formatter involved, as others have said.

Joshua Nozzi
Joshua, thanks for your answer. The reason I tried the silly convoluted float approach was simply because I'd seen another great developer do it and I thought he'd done it for some good reason. Obviously not. I should have more confidence in my own instinct, which was to use setObject:forKey: and have done with it.
John Gallagher
I'm outright violent toward the very thought of needless complication - a good trait for developers and generally lazy people. :-)
Joshua Nozzi