I'm sure I'm missing something and the answer is very simple, but I can't seem to understand why this is happening. I'm trying to make an average of dates:
NSInteger runningSum =0;
NSInteger count=0;
for (EventoData *event in self.events) {
NSDate *dateFromString = [[NSDate alloc] init];
if (event.date != nil) {
dateFromString = [dateFormatter dateFromString:event.date];
runningSum += (NSInteger)[dateFromString timeIntervalSince1970];
count += 1;
}
}
if (count>0) {
NSLog(#"average is: %#",[NSDate dateWithTimeIntervalSince1970:(NSInteger)((CGFloat)runningAverage/count)]);
}
Everything seems to work OK, except for runningSum += (NSInteger)[dateFromString timeIntervalSince1970], which gives an incorrect result. If I put a breakpoint when taking the average of two equal dates (2009-10-10, for example, which is a timeInterval of 1255125600), runningSum is -1784716096, instead of the expected 2510251200.
I've tried using NSNumber and I get the same result. Can anybody point me in the right direction?
Thanks!
Antonio
Is there some reason you are fudging about with NSInteger?
[NSDate timeIntervalSince1970] returns an NSTimeInterval, which is basically a double. Clean up the code and see if that helps.
NSInteger can hold values up to the INT_MAX limit that equals 2147483647 - so your value overflows the integer types limit - remember that timeInterval is a double type.
You can try to use double type in all your calculations or use -timeIntervalSinceReferenceDate method - it returns interval since 1 January 2001 and you might avoid overflow as well.
If your events object is an array or other type that allows to get its size then you can add time interval values already divided by count - that also may help to avoid overflow:
NSTimeInterval runningSum = 0;
NSInteger count = [self.events count];
for (...){
...
runningSum += [dateFromString timeIntervalSince1970]/count;
}
NSInteger is a 32 bit signed integer on iPhone and is therefore limited to values between −2147483648 and +2147483647.
You may get the desired result by using NSUInteger which is an unsigned 32 bit integer able to contain values between 0 and +4294967295.
You should however pay attention to the number of runs through the loop so you don't wrap the values.
Related
I am trying to build an application that includes the on-call technicians name. I have an simple NSArray that contains 4 objects in the following format;
20130910;0800;John Doe
20130910;1400;Sally Smith
20130910;2000;Jim Jones
20130911;0800;Jane Johnson
The format above is date in yyyyMMdd, time in 2400 hour time, and the technicians name.
I have two stings *timeString and *dateString that have the local device's time and date in the same format as above.
I would like to search through the array looking for the most recent past due date/time to assign the technicians name to a new string.
Using the example above, if it is 1600 (4PM) on Sept 10 I am looking to get Sally Smith returned because she started her on-call at 1400 (2PM).
That should be pretty simple. Sort your array in alpha order, which will also be date/time/name order.
Then when you have a new date/time, do a binary search of your array, comparing your new date/time string to the date/time strings from the array. The binary search will give you the correct item in log2 comparisons at most, by my calculations.
Binary search: Have a max an min search range. Set it to the beginning/end of the array to start. Divide the array index by 2, and compare your string with the item at that index. If your string is > the string at that index, set min search range to the current index. If the string is < the string at that array index, set the max search range to the new index. If it equals, you have a match.
Repeat the above steps until you've found your item. (I'm too tired to spell out the exit conditions exactly. I'll leave that as an exercise for you.)
Depending on the size of list of technicians a loop would work. The code below iterates through the list, splits each item into three parts (date, time, technician), calculates the interval since now and determine which is the most recent/active agent (interval should be the largest negative).
To get something meaningful I changed the dates in the array.
NSArray *agents = [NSArray arrayWithObjects:
#"20130920;0800;John Doe",
#"20130920;1400;Sally Smith",
#"20130920;2000;Jim Jones",
#"20130921;0800;Jane Johnson",nil];
// Setup date formatter
NSDateFormatter* onCallFormatter = [[[NSDateFormatter alloc] init] autorelease];
[onCallFormatter setDateFormat:#"yyyyMMddHHmm"];
[onCallFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"GMT"]];
NSTimeInterval mostRecent = -9999999999999;
NSInteger agentIndex;
int i;
for ( i=0; i < [agents count]; i++ ) {
// Split string into elements
NSArray *elements = [[agents objectAtIndex:i] componentsSeparatedByString:#";"];
// Convert date/time into NSDate
NSDate *onCallDateTime = [onCallFormatter dateFromString:[NSString stringWithFormat:#"%#%#", elements[0], elements[1]]];
// Calculate the time interval against current date/time
NSTimeInterval onCallInterval = [onCallDateTime timeIntervalSinceNow];
// The agent on call would be the one with the largest negative interval
// onCallInterval should be < 0 (postive would be in the future)
if ( mostRecent < onCallInterval && onCallInterval < 0) {
mostRecent = onCallInterval;
agentIndex = i;
}
NSLog( #"%# on call since %# - %# - %f hrs ", elements[2], elements[0], elements[1], onCallInterval/(60*60) );
}
NSLog( #"On call = %#", [agents objectAtIndex:agentIndex] );
How do I convert NSTimeInterval into an Integer value?
My TimeInterval holds the value 83.01837. I need to convert it into 83. I have googled but couldn't find any help.
Direct assignment:
NSTimeInterval interval = 1002343.5432542;
NSInteger time = interval;
//time is now equal to 1002343
NSTimeInterval is a double, so if you assign it directly to a NSInteger (or int, if you wish) it'll work. This will cut off the time to the nearest second.
If you wish to round to the nearest second (rather than have it cut off) you can use round before you make the assignment:
NSTimeInterval interval = 1002343.5432542;
NSInteger time = round(interval);
//time is now equal to 1002344
According to the documentation, NSTimeInterval is just a double:
typedef double NSTimeInterval;
You can cast this to an int:
seconds = (int) myTimeInterval;
Watch out for overflows, though!
In Swift 3.0
let timestamp = round(NSDate().timeIntervalSince1970)
I suspect that NSTimeInterval values from NSDate would overflow an NSInteger. You'd likely want a long long. (64 bit integer.) Those can store honking-big integer values (-2^63 to 2^63 -1)
long long integerSeconds = round([NSDate timeIntervalSinceReferenceDate]);
EDIT:
It looks like an NSInteger CAN store an NSTimeInterval, at least for the next couple of decades. The current date's timeIntervalSinceReferenceDate is about 519,600,000, or about 2^28. On a 32 bit device, and NSInteger can hold a value from -2^31 to 2^31-1. (2^31 is 2,147,483,648
Swift 4, Swift 5
I simply cast to Int64:
Int64(Date().timeIntervalSince1970)
I had a need to store an NSDate in a Swift Number. I used the following cast which is working great.
Double(startDateTime.timeIntervalSince1970)
Can someone clarify to me please why past2 is NOT negative when this code is run? Even though past is.
Thanks.
NSTimeInterval p1 = (arc4random()%600000);
NSTimeInterval past = -p1;
NSTimeInterval past2 = -(arc4random()%600000);
arc4random() returns an unsigned int (u_int32_t), so trying to make it negative is coercing the result to unsigned as well, which is why you're getting a very large positive number instead of a negative number.
If you want to get a negative random result in one call, try:
NSTimeInterval past2 = - (int) (arc4random()%600000);
joe
I have a timer in my app. When I click on exit buton then timer gets stop and stores value into the string in format of 01:15:55 . I have an array to store this string object.
What I want is , now I want to display these values by comparing to each other. So I think first I have to convert the string into the NSDate but I am having only time format and do not want to store date.
How can I accomplish this task ? any suggestion ?
EDITED : code
NSInteger secondsSinceStart = (NSInteger)[[NSDate date] timeIntervalSinceDate:sDate]; // sDate = when app get started
myAppDelegate.seconds = secondsSinceStart % 60;
myAppDelegate.minutes = (secondsSinceStart / 60) % 60;
myAppDelegate.hours = secondsSinceStart / (60 * 60);
NSString *result = nil;
if (myAppDelegate.hours > 0)
{
result = [NSString stringWithFormat:#"%02d:%02d:%02d", myAppDelegate.hours, myAppDelegate.minutes, myAppDelegate.seconds];
}
else
{
result = [NSString stringWithFormat:#"%02d:%02d", myAppDelegate.minutes, myAppDelegate.seconds];
}
NSString *tempDateString = [NSString stringWithFormat:#"%d:%d:%d",[myAppDelegate hours],[myAppDelegate minutes],[mogsApp seconds]];
Now I want to convert tempDateString into the NSDate so I can compare with similar objects. Is it possible ?
Thanks...
Sounds like an NSTimeInterval might be more appropriate. This is just a floating-point value indicating a number of seconds (including fractional seconds). You can manually format a value like this into whatever string format you want with some simple division and remainder math. (NSDate will give you time intervals since a reference date or other dates if you want to use those to get the values.) You can store NSTimeIntervals as strings if necessary.
NSDateComponents is always a good choice when storing only parts of a date/time (or a timespan).
It also gives you easy access to time management methods via NSCalendar. Then (unlike using NSTimeInterval), you don't have to set up any of the math yourself, and it will all automagically localize.
I have a set of NSTimeIntervals like this:
NSArray *mySet = [NSArray arrayWithObjects:
[NSNumber numberWithDouble: time1], //
[NSNumber numberWithDouble: time2],
[NSNumber numberWithDouble: time3],
nil];
// suppose that at this time
// time1 = 0.00000
// time2 = 18.3200
// time3 = 21.6500
at some point in my code I want to test if currentTime is greater or equal to one of the times on the array, so I do
// currentTime = 18.32 right now
for (int i=0; i<[mySet count]; i++) {
if (currentTime >= [[mySet objectAtIndex:i] doubleValue]) {
NSLog(#"%d...", i);
}
}
The output should be "1...2..."
but the output is just "1..."
when i = 1,
it is comparing 18.32 to 18.32 and failing to recognize that the value is equal or greater than the other? WTF???
How can that be?
thanks for any help.
Typically when comparing floating point values you want to decide how close one needs to be to the other (an epsilon or error bound), then just check if they are within that. The easiest way to do that is subtract one from the other then check if the absolute value of the result is less than or equal to your epsilon.
Pseudocode:
var values = [...];
var toCheck = ...;
var epsilon = 0.00001;
for (var i = 0; i < length(values); ++i)
if (abs(values[i] - toCheck) <= epsilon)
print "they are close enough"
end
end
If you're trying to do precise calculations, you shouldn't use NSNumber but NSDecimalNumber. The former is just a wrapper for storing numbers as objects whereas the second is designed to preserve precision when doing calculations. It works better than scalars at extreme ranges.
See Numbers and Values Programming Topics for Cocoa
You can't compare double values for equality except for certain cases. (1.00000 == 1.0000) would probably ring true, but (1.3000 == 1.3000) would most likely not. This is because doubles aren't actually decimal numbers; and not all numbers can be represented by a double; your values are likely something like 18.320000123 and 18.320000473; that is to say; there's probably a lot of trailing garbage that is uninteresting at the precision you have chosen, but none the less keep them from comparing as equal.
It doesn't help that you run one of them through NSNumber; twice.
You declare var i as an int and then try to nslog it at as double value without a cast. The log might be failing independent of the precision of your test.