Is there a better way to calculate a time period? - iphone

I have the following code. This is getting calculated in cellForRowAtIndexPath, so I think it could be a little expensive. Is there a better way to calculate a time period?
+(NSString*)toShortTimeIntervalString:(NSString*)sDate
{
NSDateFormatter* df = [[NSDateFormatter alloc]init];
[df setDateFormat:#"yyyy-MM-dd'T'HH:mm:ssZ"];
NSDate* date = [df dateFromString:[sDate stringByReplacingOccurrencesOfString:#"Z" withString:#"-0000"]];
[df release];
NSDate* today = [[NSDate alloc] init];
NSDate *d = date; //[_twitter_dateFormatter dateFromString:sDate];
NSTimeInterval interval = [today timeIntervalSinceDate:d];
[today release];
//TODO: added ABS wrapper
double res = 0;
NSString* result;
if(interval > SECONDS_IN_WEEK)
{
res = fabs(interval / SECONDS_IN_WEEK);
result = [NSString stringWithFormat:#"%1.0fw ago", res];
}
else if(interval > SECONDS_IN_DAY)
{
res = fabs(interval / SECONDS_IN_DAY);
result = [NSString stringWithFormat:#"%1.0fd ago", res];
}
else if (interval > SECONDS_IN_HOUR){
res = fabs(interval / SECONDS_IN_HOUR);
result = [NSString stringWithFormat:#"%1.0fh ago", res];
}
else if (interval > SECONDS_IN_MIN) {
res = fabs(interval / SECONDS_IN_MIN);
result = [NSString stringWithFormat:#"%1.0fm ago", res];
}
else
{
interval = fabs(interval);
result = [NSString stringWithFormat:#"%1.0fs ago", interval];
}
return result;
}

This doesn't look too bad, and I doubt you'll be seeing too much of a performance hit from it. To make it more efficient, you might consider only creating one NSDataFormatter (saving it in an instance variable or static variable) and reusing it. Or even better, if you could convert everything to NSDates beforehand, then you wouldn't have to use the formatter every time.
Are you actually seeing any performance issues here, though? Before you try and optimize, you should use Instruments to investigate what's actually taking up time.

Not really, but there are a few categories out there which encapsulate that logic and make it a one line call for you.

Recently I've been in a situation to perform complex calculation (plotting graph for series of years), and I was using NSDate within loops. After analyze with time profiler NSDate, NSDateFormater, NSCalendar was the root cause. I switched to use the C type (time_t + timeinfo) and it drastically improves the speed.
Although they are much faster, they have less functionality, if you need to deal with TimeZone etc, NSDate is probably easier to deal with.

Related

Retrieving day or month int from an NSString formatted like "4/26/1980"

I've been having some issues converting strings to an NSDate, so maybe there's a better/easier way to do this.
I have an NSString of a date formatted like "dd/mm/yyyy", HOWEVER, some do not contain the year and are just "dd/mm".
I'm trying to store the day and month in my SQLite database, so I need to get those components out separately. I have tried converting them into NSDate but because some don't have years, it doesn't match the NSDate format and fails. I've also had issues following examples like this because my original dateString is not in the same format (uses slashes rather than dashes).
Any ideas? Thanks
NSString *dateString = #"01/04/1981";
NSArray *dateComponentsArray = [dateString componentsSeparatedByString:#"/"];
NSString *monthString = [dateComponentArray objectAtIndex:0];
NSString *dayString = [dateComponentsArray objectAtIndex:1];
if ([dateComponentsArray objectAtIndex:2]) {
NSString *yearString = [dateComponentsArray objectAtIndex:2];
}
NSFormatter seems like overkill as it's pretty expensive. If you're sure you're not going to have any changes to the format, this should work.
You could use good ol' fashioned NSScanner.
NSScanner *scanner = [NSScanner scannerWithString:x];
int month, day;
if (
[scanner scanInt:&month] &&
[scanner scanString:#"/" intoString:nil] &&
[scanner scanInt:&day]
) {
NSLog(#"Got month = %d and day = %d", month, day);
}
Check this against the documentation because I'm writing from memory.
SQLite doesn't even have a proper native date type (see my SO recent post on this topic), and to the extent you can store dates in SQLite, it doesn't make sense in the absence of a year. Not sure it makes sense to have a NSDate without a year, either.
Personally, I'd just store three fields, day, month, and year as separate numeric fields in the database, leaving the year blank if you don't know what year the person was born. Thus, just use a simple NSScanner as suggested by #benzado:
int day;
int month;
int year;
NSScanner *scanner = [NSScanner scannerWithString:birthdayString];
if ([scanner scanInt:&month] &&
[scanner scanString:#"/" intoString:nil] &&
[scanner scanInt:&day])
{
// found valid month/day
if ([scanner scanString:#"/" intoString:nil] &&
[scanner scanInt:&year])
{
// found year too
}
}
Or use NSString's componentsSeparatedByString to get it into an array of strings as suggested by #Jacob:
NSString *monthString, *dayString, *yearString;
NSArray *dateComponents = [birthdayString componentsSeparatedByString:#"/"];
if ([dateComponents count] >= 2)
{
monthString = [dateComponents objectAtIndex:0];
dayString = [dateComponents objectAtIndex:1];
// found month and day
if ([dates count] == 3)
{
yearString = [dateComponents objectAtIndex:2];
// found year, too
}
}
You need to figure out what it means when there's no year on the date. Does it assume the current year, or something else? Once you get that figured out, you can append the year to the string and then parse the date.
can't you just check for '/' if its that simple? try to use NSDateFormatter as well, that may help you.
I think this has the answers you need:
Apple iOS Date Formatting guide

How do I measure the time interval from a starting point using NSDate?

I have one method that I use in many places throughout my project that looks like the following:
-(void)showSignInView
{
if(check for time interval)
[[self superview] addSubview:loginController.view];
}
I'd like to note the first time that this method is called, then on every subsequent call of this method check to make sure that the interval has been more than 15 minutes from the original call. Only then will it execute the rest of its code.
I know that you can use NSDate to measure time intervals using code like the following:
NSDate *firstTime = [[NSDate date] retain];
NSDate *SecondTime = [NSDate date];
NSLog(#"Time elapsed: %f", [SecondTime timeIntervalSinceDate:firstTime]);
but I'm not sure how to implement the initial time check, then subsequent comparisons to that time. How can I do this?
Create a property named previousTime.
#property(nonatomic, retain) NSDate *previousTime;
And create a method to find the time difference.
- (NSTimeInterval)timeDifferenceSinceLastOpen {
if (!previousTime) self.previousTime = [NSDate date];
NSDate *currentTime = [NSDate date];
NSTimeInterval timeDifference = [currentTime timeIntervalSinceDate:previousTime];
self.previousTime = currentTime;
return timeDifference;
}
You could use GCD to achieve this. The dispatch_once() function can arrange that a block is only executed once in the lifetime of your app.
NSDate *firstTime = nil;
- (void)loadView {
[self calculateTime:[NSDate dateWithTimeIntervalSince1970:1312996898]];
}
- (void)calculateTime:(NSDate*)secondTime
{
double offset = [secondTime timeIntervalSinceDate:[self getFirstTime]];
if (offset >= 900.0) {
NSLog(#"15 min gone");
}
}
- (NSDate *)getFirstTime
{
if (!firstTime) {
firstTime = [[NSDate date] retain];
}
return firstTime;
}

Objective-C: Calculating hourly rate with NSDate and NSDecimalNumber

I have two instances of NSDate (a start time and an end time) and also one NSDecimalNumber (which represents the amount that was earned over the period of time between the two dates). What I'm trying to do is calculate an hourly rate based on these three variables. I've come up with something that works, but it feels to me a bit excessive and I wanted to know if there was an easier, more efficient way. This is what I am doing now.
Calculating the amount of time between the two NSDates
NSTimeInterval timeInSeconds = [endTime timeIntervalSinceDate:startTime];
Converting this into hours
double hours = timeInSeconds / 3600;
Converting the hours double into an NSNumber
NSNumber *duration = [NSNumber numberWithDouble:hours];
Converting the NSNumber into an NSString
NSLocale *locale = [NSLocale currentLocale];
NSString *durationString = [duration descriptionWithLocale:locale];
Using this string to create an NSDecimalNumber
NSDecimalNumber *decimalDuration = [NSDecimalNumber decimalNumberWithString:durationString locale:locale];
Dividing the original NSDecimalNumber (result) by this new NSDecimalNumber representation of time
NSDecimalNumber *hourlyRate = [result decimalNumberByDividingBy:decimalDuration];
If I'm understanding correctly, the only way to do any math involving my original NSDecimalNumber is to create another instance of NSDecimalNumber, so it is necessary to go through all these steps. I'm worried that I might be losing accuracy or performing unnecessary steps. Thanks for any advice.
Why don't you just use a simple mathematics?
NSTimeInterval timeInSeconds = [endTime timeIntervalSinceDate:startTime];
double hours = timeInSeconds / 3600;
double rate = [result doubleValue];
double hourlyRate = rate / hours;

how can I make a counter or timer using NSDate or NSCalendar?

I am new to iphone development, I am developing an application, which takes restaurant order data (Name of Restaurant and order price) and save to an array and display on another UITable, but now I want to start a timer such as
Royal Taj
$35
1 hour, 40 mints remaining,
so how can I count such time, on different orders, I need a little guide regarding it, and time should go in decrement, not increment ,,,,
if you are not clear about my question, then u can ask me anything again....
here is what i have done and its working ...........
{
NSDictionary* temp = [allOrder objectAtIndex:indexPath.row];
NSDate* dt = (NSDate*)[temp objectForKey:#"date"];
NSTimeInterval inter = [dt timeIntervalSinceNow];
int hour = inter / 3600;
int min = ((long)inter % 3600)/60;
NSString* str;
if (hour == 0)
{
if (min == 0)
str = [NSString stringWithFormat:#"Order is ready to serve"];
else
str = [NSString stringWithFormat:#"%d minutes remaining",min];
}
else {
if (min == 0)
str = [NSString stringWithFormat:#"%d hours remaining",hour];
else
str = [NSString stringWithFormat:#"%d hours and %d minutes remaining",hour,min];
}
cell.detailTextLabel.text = str;
}
here i supposed that u have an array allOrder which contains dictionaries which has object for keys #"date" as the date on which order has to be delivered.

Parsing JSON dates on IPhone

Forgive me as I'm new to Objective C.
I am getting back dates from a .NET webservice in the /Date(xxxxxxxxxxxxx-xxxx)/ format. I'm looking for some direction on how to best parse this into an NSDate object. I've tried using dateWithTimeIntervalSince1970 on it but it comes back with a date in the year 1969 for a date I know is in 2006.
Looking for some direction on the proper way to handle JSON dates.
Thanks in advance!
I just wrote this for iOS 4.0+ (because it uses NSRegularExpression). It handles dates with or without timezone offsets. Seems to work pretty well, what do you think?
+ (NSDate *)mfDateFromDotNetJSONString:(NSString *)string {
static NSRegularExpression *dateRegEx = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dateRegEx = [[NSRegularExpression alloc] initWithPattern:#"^\\/date\\((-?\\d++)(?:([+-])(\\d{2})(\\d{2}))?\\)\\/$" options:NSRegularExpressionCaseInsensitive error:nil];
});
NSTextCheckingResult *regexResult = [dateRegEx firstMatchInString:string options:0 range:NSMakeRange(0, [string length])];
if (regexResult) {
// milliseconds
NSTimeInterval seconds = [[string substringWithRange:[regexResult rangeAtIndex:1]] doubleValue] / 1000.0;
// timezone offset
if ([regexResult rangeAtIndex:2].location != NSNotFound) {
NSString *sign = [string substringWithRange:[regexResult rangeAtIndex:2]];
// hours
seconds += [[NSString stringWithFormat:#"%#%#", sign, [string substringWithRange:[regexResult rangeAtIndex:3]]] doubleValue] * 60.0 * 60.0;
// minutes
seconds += [[NSString stringWithFormat:#"%#%#", sign, [string substringWithRange:[regexResult rangeAtIndex:4]]] doubleValue] * 60.0;
}
return [NSDate dateWithTimeIntervalSince1970:seconds];
}
return nil;
}
I was in the same boat whilst using json-framework which doesn't support the date format as it's not official JSON. My source is from an API built using JSON.Net. This is what I came up with:
- (NSDate*) getDateFromJSON:(NSString *)dateString
{
// Expect date in this format "/Date(1268123281843)/"
int startPos = [dateString rangeOfString:#"("].location+1;
int endPos = [dateString rangeOfString:#")"].location;
NSRange range = NSMakeRange(startPos,endPos-startPos);
unsigned long long milliseconds = [[dateString substringWithRange:range] longLongValue];
NSLog(#"%llu",milliseconds);
NSTimeInterval interval = milliseconds/1000;
return [NSDate dateWithTimeIntervalSince1970:interval];
}
I don't have the appended portion in the date format that you do so I haven't dealt with that like the answer above. No error catching either, it's all new to me at this point.
I actually found the snippet with NSRegularExpression pretty useful, till i came up with another solution that uses NSCharecterSet for stipping off the milliseconds.
+ (NSDate*) dateFromJSONString:(NSString *)dateString
{
NSCharacterSet *charactersToRemove = [[ NSCharacterSet decimalDigitCharacterSet ] invertedSet ];
NSString* milliseconds = [dateString stringByTrimmingCharactersInSet:charactersToRemove];
if (milliseconds != nil && ![milliseconds isEqualToString:#"62135596800000"]) {
NSTimeInterval seconds = [milliseconds doubleValue] / 1000;
return [NSDate dateWithTimeIntervalSince1970:seconds];
}
return nil;
}
Saves a lot of the manual string processing and makes the code much cleaner.
As a .NET programmer learning Objective-C I had the same problem when I tried to consume a .Net WebService.
At first I thought I would be able to use the NSDateFormatter...
I found a really good reference for it's symbols here, but I quickly realized that I needed to convert the number from milliseconds to seconds.
I wrote the code to do it...
I'm still learning Obj-C but I dont think It should've been this hard...
- (NSDate *) getJSONDate{
NSString* header = #"/Date(";
uint headerLength = [header length];
NSString* timestampString;
NSScanner* scanner = [[NSScanner alloc] initWithString:self];
[scanner setScanLocation:headerLength];
[scanner scanUpToString:#")" intoString:&timestampString];
NSCharacterSet* timezoneDelimiter = [NSCharacterSet characterSetWithCharactersInString:#"+-"];
NSRange rangeOfTimezoneSymbol = [timestampString rangeOfCharacterFromSet:timezoneDelimiter];
[scanner dealloc];
if (rangeOfTimezoneSymbol.length!=0) {
scanner = [[NSScanner alloc] initWithString:timestampString];
NSRange rangeOfFirstNumber;
rangeOfFirstNumber.location = 0;
rangeOfFirstNumber.length = rangeOfTimezoneSymbol.location;
NSRange rangeOfSecondNumber;
rangeOfSecondNumber.location = rangeOfTimezoneSymbol.location + 1;
rangeOfSecondNumber.length = [timestampString length] - rangeOfSecondNumber.location;
NSString* firstNumberString = [timestampString substringWithRange:rangeOfFirstNumber];
NSString* secondNumberString = [timestampString substringWithRange:rangeOfSecondNumber];
unsigned long long firstNumber = [firstNumberString longLongValue];
uint secondNumber = [secondNumberString intValue];
NSTimeInterval interval = firstNumber/1000;
return [NSDate dateWithTimeIntervalSince1970:interval];
}
unsigned long long firstNumber = [timestampString longLongValue];
NSTimeInterval interval = firstNumber/1000;
return [NSDate dateWithTimeIntervalSince1970:interval];
}
Hopefully someone can provide a better Obj-C solution.
If not I may keep this or look for a way to change the serialization format in .NET
EDIT:
About that JSON DateTime format...
If you have any control on the service it would probably be best to convert the date to a string in your DataContract objects.
Formatting to RFC1123 seems like a good idea to me right now. As I can probably pick it up easily using a NSDateFormatter.
Quote from Rick Strahl
There's no JavaScript date literal and Microsoft engineered a custom date format that is essentially a marked up string. The format is a string that's encoded and contains the standard new Date(milliseconds since 1970) value.
Theory: MS encoded the C# DateTime in JSON as milliseconds since 1970.
Solution:
NSString*
dateAsString = #"/Date(1353720343336+0000)/";
dateAsString = [dateAsString stringByReplacingOccurrencesOfString:#"/Date("
withString:#""];
dateAsString = [dateAsString stringByReplacingOccurrencesOfString:#"+0000)/"
withString:#""];
unsigned long long milliseconds = [dateAsString longLongValue];
NSTimeInterval interval = milliseconds/1000;
NSDate* date = [NSDate dateWithTimeIntervalSince1970:interval];
This is the shortest solution I can think of.
Use an NSDateFormatter's dateFromString: method after setting the date format.
-(NSString*)convertToUTCTime:(NSString*)strDate{
NSDate *currentDate = [NSDate date];
myDate = [commonDateFormatter dateFromString: strDate];
NSTimeInterval distanceBetweenDates = [currentDate timeIntervalSinceDate:myDate];
return [self stringFromTimeInterval:distanceBetweenDates];
}
- (NSString *)stringFromTimeInterval:(NSTimeInterval)interval {
NSInteger ti = (NSInteger)interval;
NSInteger minutes = (ti / 60) % 60;
NSInteger hours = (ti / 3600);
if (hours > 24) {
NSInteger days = hours/24;
if (days > 30) {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"EEE d MMM, h:mm a"];
//[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"IST"]];
NSString *daydate = [dateFormatter stringFromDate:myDate];
return daydate;
}
else{
return [NSString stringWithFormat:#" %2ldd",(long)days];
}
}else{
if (hours == 0 && minutes < 1) {
return [NSString stringWithFormat:#"Today"];
}
else if (hours == 0 && minutes < 60){
return [NSString stringWithFormat:#"%2ldm ",(long)minutes];
}
else{
return [NSString stringWithFormat:#" %2ldh",(long)hours];
}
}
}