Converting an NSNumber to NSTimeInterval - objective-c

I've converted an NSTimeInterval to an NSNumber in order to store it in NSUserDefaults. I used the numberWithDouble method like this:
NSNumber *savedTime = [NSNumber numberWithDouble:timeInterval];
I'm retrieving the information in another view, but I'm running into an issue. The number doesn't seem to want to convert properly. I know the number is carrying over, because I am able to view the number in this label, and it shows up properly:
timeLabel.text = [NSString stringWithFormat:#"%f", [[[userDefaults objectForKey:groupTitleForReference] objectForKey:#"time"] doubleValue]];
but when I go to set the interval to the double value, it doesn't work. I'm using this:
interval = [[[userDefaults objectForKey:groupTitleForReference] objectForKey:#"time"] doubleValue];
All variables are properly declared in the header. Any idea why it's not working?
Thanks,
David

In both cases, you use
[[[userDefaults objectForKey:groupTitleForReference] objectForKey:#"time"] doubleValue]
to get the double value. You need to concentrate on what might be different. Off the top of my head, look at:
Is interval correctly declared as a double or NSTimeInterval? Show us how and where you declare interval.
When you display interval to check its value, are you displaying it correctly. I've been known to do this by accident
NSTimeInterval interval = ....
NSLog(#"%#", interval);
Show us the code you use to inspect the interval and its output.
Has userDefaults changed?
Has groupTitleForReference changed.
EDIT
Another thing to check
If interval is a global make sure the declaration in the header is of the form
extern NSTimeInterval interval;
If you omit extern, you'll get a separate variable called interval in every file that includes the header.

Related

Difference between dot syntax and valueForKey

Please note that update 3 is probably most relevant
Im setting a NSTimeInterval property of a managed object with an nsdate object using setValue:forKey:
When i attempt to get the value I get weird stuff, at runtime this
NSLog(#"[managedObject valueForKey:#\"startTime\"] : %#, [NSDate dateWithTimeIntervalSince1970:[managedObject startTime]]: %#",
[managedObject valueForKey:#"startTime"],[NSDate dateWithTimeIntervalSince1970:[managedObject startTime]]);
Returns
[managedObject valueForKey:#"startTime"] : 2012-07-14 08:13:05 +0000,
[NSDate dateWithTimeIntervalSince1970:[managedObject startTime]]: 1981-07-14 08:13:05 +0000
Update 1
The value returned by [managedObject valueForKey:#"startTime"] is correct. However I would prefer to use [NSDate dateWithTimeIntervalSince1970:[managedObject startTime]] or something similar so that it is more strongly typed.
I believe [managedObject startTime] returns an incorrect value => 363954111.000000 .
However i set it with something like this:
managedObject setValue:1342261311 forKey:#"startTime"
It is worth noting that I am unsure whether this is incorrect because [managedObject valueForKey:#"startTime"] returns a correct NSDate object.
Update 2
I've logged the double values returned by KVC and . syntax.
managedObject.startTime = 363954111.000000
valueForKey timeIntervalSince1970 = 1342261311.000000
Update 3
Okay, I've set up a test, start time is set like this entity.startTime = [[NSDate dateWithTimeIntervalSince1970:1342261311] timeIntervalSince1970]; and end time is set like this [entity setValue:[NSDate dateWithTimeIntervalSince1970:1342261311] forKey:#"endTime"];
When i write them to log i get this start = 1342261311.000000, end = 363954111.000000
It seems that the NSDate object is being unwrapped incorrectly, has anyone seen this before?
The problem here is that valueForKey: is intended to be used with object values, in fact it returns an id.
As a convenience, valueForKey: wraps primitive types (such as integers and doubles) in their NSNumber counterparts.
The reason you see two different values is that valueForKey: returns an id, which essentially is a pointer to the position in memory where the NSNumber happens to be stored. Your code then just takes this arbitrary memory address and somehow interprets it as a double and then constructs an NSDate out of that.
Calling the startTime accessor method directly, on the other hand, returns the double without any further ado.
If you want to use valueForKey:, you can do something like this to get the real value:
NSTimeInterval tiv = [[managedObject valueForKey:#"startTime"] doubleValue];
and then work from there.
I am actually a bit surprised that the compiler doesn't emit a warning about this. Apple's latest compilers have become quite adept at catching problems like this one.
It was a problem with the difference in epochs. NSDate uses Jan 1 2001 as an epoch. So when I was getting the value I was using the unix epoch (1970). That gave me a difference in values.
When KVC unwraps and wraps NSTimeInterval with a NSDate object it uses the NSDate 2001 epoch.
So instead of using dateWithTimeIntervalSince1970
I used dateWithTimeIntervalSinceReferenceDate when getting the value.
NSTimeInterval is a typdef of a double
typedef double NSTimeInterval;
You can not store scalars directly in core data but you either have to wrap them in a NSNumber or in your case it may be easier to use a NSDate.
If startTime is a NSTimeInterval (and not an NSDate), you are comparing two different things there, a double and an NSDAte object.
[managedObject valueForKey:#"startTime"] will return you an NSTimeInterval, a primitive (which you should print with %f by the way).
[NSDate dateWithTimeIntervalSince1970:[managedObject startTime]] will return you a NSDate.
If you really want to comare the two, you should use [NSDate dateWithTimeIntervalSince1970:[managedObject valueForKey:#"startTime"]] to properly compare two NSDate objects.

Working with time intervals in data logging?

I'm programming a data logging application. I need to be able to store a time interval typed in by the user using Core Data. For instance, if the user completes a task in seven minutes and twenty-three seconds, he/she can type 7:28 into the NSTextField and that will be part of the data.
What class should I use to store the time? NSDate seems to be the right way of doing it, but it does not seem to store time intervals. I see that there is an NSTimeInterval class. However, with no particular reference for it, I do not know how to use it.
Also, when this time interval is stored in objects within Core Data, I need to be able to retrieve those items and sort them (using NSSortDescriptor); in order to retrieve the entry that logged the lowest time interval. This is just additional information to help figure out what I need to do here.
From the docs: NSDate objects represent a single point in time.
From your use case it sounds like you want the user to log a relative time, and then to be able to sort by which is the smallest. In that case, NSDate is not a good option. The best solution is to just store the time interval as an NSUInteger, where the integer represents your value in seconds, and then do the appropriate conversions on either end.
If the user types in 7:28, could you convert this into seconds (448 seconds) and store it in a NSUInteger? That would make sorting it easily because you would not have to deal with separate minute and second values.
Here's what I think you should do:
Have two fields for user input: one for minutes and one for seconds.
Have some code like this:
NSInteger totalTime1 = 0;
totalTime += [minuteField.text integerValue]*60;
totalTime += [secondField.text integerValue];
Now store totalTime1 using Core Data. To retrieve the times and sort them, do something like this:
//Retrive times
NSArray *retrievedTimes = [NSArray arrayWithObjects: time1FromCoreDataAsNSNumber, time2FromCoreDataAsNSNumber, etc, nil];
NSArray *sortedRetrievedTimes = [retrievedTimes sortedArrayUsingSelector:#selector(compare:)];
//Now the array is sorted from lowest to highest
NSInteger lowestValue = [[sortedRetrievedTimes objectAtIndex:0] integerValue];
Hope this helps!

JSON date to NSDate and back

I have a JSON date, e.g: 1295804021525, which is the number of milliseconds from 1970.
I have written the following code to convert this number into an NSDate:
long long seconds = [[payload valueForKey:#"starttime"]longLongValue]/1000;
NSDate *somedate = [NSDate dateWithTimeIntervalSince1970:seconds];
Which does work and returns the correct date. First I'm wondering if this is the best way of doing the conversion.
Next I am wondering how to convert back to the milliseconds format and then put into the url to send back to the server.
I have:
long long date = [somedate timeIntervalSince1970] * 1000;
NSString *urlString = [NSString stringWithFormat:#"http://someurl?since=%qi",date];
Again this seems to work but I was wondering how I could get the same functionality using NSNumber.
With your original conversion, you're losing sub-second precision. You may want to do something like
CFTimeInterval seconds = [[payload valueForKey:#"starttime"] doubleValue] / 1000.0;
The second snippet should be fine.
I'm not sure why you think using NSNumber would help in any fashion. With the modification I mentioned, both these code snippets are straightforward and should work just fine.

NSString to NSDate with float values

I'm developing an OS X desktop application which will track time for a car racing event.
The difference between pilots can be very small, so the collected data for each lap has a floating point value for the seconds:
bestLap = #"00:01:39.5930000"
But I need to compare each pilot's time and sort it. I'm trying to convert it to a NSDate object, using NSDateFormatter and I couldn't manage to make it work
Is it possible to convert a string like that to a NSDate? If so, how can I compare and sort an array containing NSDates
Thanks
An NSDate is used to represent a date, not a time interval.
Also, if the purpose is just to sort them, there is no need to convert the string into an NSDate or NSTimeInterval since they are already lexicographically ordered if a time interval is shorter than the other in your format.
That means, calling -sortUsingSelector: is enough.
[theMutableArrayOfLaps sortUsingSelector:#selector(compare:)];
As KennyTM says, lexicographic sorting is enough if all you need is ordering. If you really want to get a numeric value for comparison & reporting purposes, you can break the string up into components and convert to a double something like this:
NSArray* parts = [bestLap componentsSeparatedByString:#":"];
double bestLapInSeconds = [[parts objectAtIndex:0] doubleValue] * 3600 // hours
+ [[parts objectAtIndex:1] doubleValue] * 60 // minutes
+ [[parts objectAtIndex:2] doubleValue]; // seconds
(Note that this just blithely assumes that the original string conforms to an "H:M:S" format without any error checking. You should not normally do this in real life!)

Converting JSON date(ticks) to NSDate

Does anyone know how to convert a JSON date(ticks) to an NSDate in Objective-C? Can someone post some code?
I'm guessing here but your JSON value is the number of milliseconds since 1970, right? You can use NSDate's dateWithTimeIntervalSince1970: method to return an NSDate object with the correct time. Just make sure to convert the JSON milliseconds number to seconds before passing it to NSDate-- Cocoa uses NSTimeInterval in most places, which represents an interval in seconds.
It goes roughly like this:
// Input string is something like: "/Date(1292851800000+0100)/" where
// 1292851800000 is milliseconds since 1970 and +0100 is the timezone
NSString *inputString = [item objectForKey:#"DateTimeSession"];
// This will tell number of seconds to add according to your default timezone
// Note: if you don't care about timezone changes, just delete/comment it out
NSInteger offset = [[NSTimeZone defaultTimeZone] secondsFromGMT];
// A range of NSMakeRange(6, 10) will generate "1292851800" from "/Date(1292851800000+0100)/"
// as in example above. We crop additional three zeros, because "dateWithTimeIntervalSince1970:"
// wants seconds, not milliseconds; since 1 second is equal to 1000 milliseconds, this will work.
// Note: if you don't care about timezone changes, just chop out "dateByAddingTimeInterval:offset" part
NSDate *date = [[NSDate dateWithTimeIntervalSince1970:
[[inputString substringWithRange:NSMakeRange(6, 10)] intValue]]
dateByAddingTimeInterval:offset];
(from https://gist.github.com/726910)
You'd have to detect the client's locale in order to be able to do that, and unless your client knows how to do that, there's probably not much point.
NSDate's descriptionWithLocale: would be the way you format it for another locale. And timeIntervalSince1970 will go back to the (seconds) since 1970, which you could multiply by 1000 to get ms to return to the client. It's all in the NSDate documentation.
http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSDate_Class/Reference/Reference.html
According to this page: http://msdn.microsoft.com/en-us/library/system.datetime.ticks.aspx ticks begin on Jan 1, 0001 so dateWithTimeIntervalSince1970: is not automatically setup to work with ticks. You can still use this method but should adjust for the difference.