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] );
Related
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 one array and it contains the date strings. I want to compare the date strings and displayed the date string as grouping, so it should display the date as only once, when duplicate date as found.
My array
NSMutableArray *arr = [[NSMutableArray alloc] initWithObjects:#"2010-12-28 04:23:47",
#"2010-12-28 04:21:50",
#"2010-12-28 04:18:56",
#"2010-12-27 13:39:18",
#"2010-12-22 21:48:09",
#"2010-12-22 20:44:18",
#"2010-12-22 20:25:26",
#"2010-12-22 20:08:39",nil];
Expected Output is,
"2010-12-28 "
"2010-12-27 "
"2010-12-22 ".
When compared to the date string, the time is not a problem, i want to display the date wise only(Grouping date).
Please help me out.
THanks.
Code as follows,
NSMutableArray *filterdArray=[[NSMutableArray alloc]init];
for(NSString *string in arr)
{
NSArray *components = [string componentsSeparatedByString: #" "];
//Check that date is present in filterdArray
//if not present
//add the value to array using
[filterdArray addObject:[components objectAtIndex:0]];
}
Then sort the values in filterdArray
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.
I have some methods that return the number of weekdays between two given dates. Since calling these methods become very expensive to call when the two dates lie years apart, I'm wondering how these methods could be refactored in a more efficient way.
The returned result is correct but I feel that the iphone processor is struggling to keep up and consequently freezes up the application when I would call these methods over a period of say 10years.
Any suggestions ?
//daysList contains all weekdays that need to be found between the two dates
-(NSInteger) numberOfWeekdaysFromDaysList:(NSMutableArray*) daysList
startingFromDate:(NSDate*)startDate
toDate:(NSDate*)endDate
{
NSInteger retNumdays = 0;
for (Day *dayObject in [daysList objectEnumerator])
{
if ([dayObject isChecked])
{
retNumdays += [self numberOfWeekday:[dayObject weekdayNr] startingFromDate:startDate toDate:endDate];
}
}
return retNumdays;
}
-(NSInteger) numberOfWeekday:(NSInteger)day
startingFromDate:(NSDate*)startDate
toDate:(NSDate*)endDate
{
NSInteger numWeekdays = 0;
NSDate *nextDate = startDate;
NSComparisonResult result = [endDate compare:nextDate];
//Do while nextDate is in the past
while (result == NSOrderedDescending || result == NSOrderedSame)
{
if ([NSDate weekdayFromDate:nextDate] == day)
{
numWeekdays++;
}
nextDate = [nextDate dateByAddingDays:1];
result = [endDate compare:nextDate];
}
return numWeekdays;
}
You need to create an formula to calculate the number of weekdays rather than loop thru each day and count them.
Something like this (this is a rough approximation), where startJD and endJD are the Julian Dates:
nWeekdays = (endJD - startJD) * 5 / 7;
Of course that's close but not exact since it doesn't take into account what day of the week it starts and ends on. But that's the general idea, you need a formula, not a loop.
You can also find quite a bit on this topic by searching.
Why not look at the core foundation classes that handle dates?
CFAbsoluteTimeGetDayOfWeek
Returns an integer representing the day of the week indicated by the specified absolute time.
SInt32 CFAbsoluteTimeGetDayOfWeek (
CFAbsoluteTime at,
CFTimeZoneRef tz
);
Parameters
at : The absolute time to convert.
tz : The time zone to use for time correction. Pass NULL for GMT.
Return Value :
An integer (1-7) representing the day of the week specified by at. Per ISO-8601, Monday is represented by 1, Tuesday by 2, and so on.
Availability
Available in iOS 2.0 and later.
Declared In
CFDate.h
More can be found at: http://developer.apple.com/library/ios/#documentation/CoreFoundation/Conceptual/CFDatesAndTimes/
I have the following code that is meant to convert milliseconds into hours, mins and seconds:
int hours = floor(rawtime / 3600000);
int mins = floor((rawtime % 3600000) / (1000 * 60));
int secs = floor(((rawtime % 3600000) % (1000 * 60)) / 1000);
NSLog(#"%d:%d:%d", hours, mins, secs);
NSString *hoursStr = [NSString stringWithFormat:#"%d", hours];
NSString *minsStr = [NSString stringWithFormat:#"%d", mins];
NSString *secsStr = [NSString stringWithFormat:#"%d", secs];
NSLog(#"%a:%a:%a", hoursStr, minsStr, secsStr);
Fairly straightforward. Rawtime is an int with value 1200. The output is like this:
0:0:1
0x1.3eaf003d9573p-962:0x1.7bd2003d3ebp-981:-0x1.26197p-698
Why is it that converting the ints to strings gives such wild numbers? I've tried using %i and %u and they made no difference. What is happening?
You have to use %# as the conversion specifier for an NSString. Change your last line to:
NSLog(#"%#:%#:%#", hoursStr, minsStr, secsStr);
%a means something totally different. From the printf() man page:
aA
The double argument is rounded and converted to hexadecimal notation in the style
[-]0xh.hhhp[+-]d
where the number of digits after the hexadecimal-point character is equal to the precision specification.
Instead of rolling your own string formatting code, you should be using an NSNumberFormatter for numbers or an NSDateFormatter for dates/times. These data formatters take care of localization of format to the user's locale and handle a variety of formats built-in.
For your use, you need to convert your millisecond time into an NSTimeInterval (typedef'd from a double):
NSTimeInterval time = rawtime/1e3;
Now you can use an NSDateFormatter to present the time:
NSDate *timeDate = [NSDate dateWithTimeIntervalSinceReferenceDate:time];
NSString *formattedTime = [NSDateFormatter localizedStringFromDate:timeDate
dateStyle:NSDateFormatterNoStyle
timeStyle:NSDateFormatterMediumStyle];
NSString *rawTime = [[formattedTime componentsSeparatedByString:#" "] objectAtIndex:0];
on OS X where the last line removes the "AM/PM". This will work for any time less than 12 hrs and will give a formatted string in the localized format for HH:MM:SS. On the iPhone, localizedStringFromDate:dateStyle:timeStyle: isn't available (yet). You can achieve the same effect with setTimeStyle:, setDateStyle: and stringFromDate: on a date formatter instance.