Formatting seconds into hh:ii:ss - objective-c

I have app that is a basic timer. It tracks the number of seconds the app has run. I want to convert it so the seconds (NSUInteger) are displayed like: 00:00:12 (hh:mm:ss). So I've read this post:
NSNumber of seconds to Hours, minutes, seconds
From which I wrote this code:
NSDate *date = [NSDate dateWithTimeIntervalSince1970:[[self meeting] elapsedSeconds]];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"hh:mm:ss"];
It works fine, but it starts out with 04:00:00. I'm not sure why. I also tried doing something like:
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:[[self meeting] elapsedSeconds] * -1];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"hh:mm:ss"];
Thinking that it would display the counter correctly, but it does a wierd 01:23:00, then just flops to 04:00:00 and stays there for the rest of the time.
MS

This is similar to a previous answer about formatting time but doesn't require a date formatter because we aren't dealing with dates any more.
If you have the number of seconds stored as an integer, you can work out the individual time components yourself:
NSUInteger h = elapsedSeconds / 3600;
NSUInteger m = (elapsedSeconds / 60) % 60;
NSUInteger s = elapsedSeconds % 60;
NSString *formattedTime = [NSString stringWithFormat:#"%u:%02u:%02u", h, m, s];

While there are easier ways of doing this (#dreamlax has a very good way), let me explain what is wrong with your example and let's get it working:
First, the reason that it is showing 04:00:00 (well, it is probably actually showing 04:00:12) is because it is converting the time from UTC/GMT to your local time. To fix this, you need to add the following line:
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
Then, it will no longer show 04:00:12 because it doesn't convert the timezone. Unfortunately, it will now show 12:00:12 instead of 00:00:12 because it is midnight. In order to fix that, have it convert the string to 24 hour time instead by using the HH formatter instead of hh:
[formatter setDateFormat:#"HH:mm:ss"];
Keep in mind that since this was designed to work with times, that it will not work for more than 24 hours (because it will "roll over" to midnight again).
The full code would be:
NSDate *date = [NSDate dateWithTimeIntervalSince1970:[[self meeting] elapsedSeconds]];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"HH:mm:ss"];
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
NSLog(#"%#", [formatter stringFromDate:date]);
// Results: 00:00:12

Related

How can I handle this European-style timestamp?

I’m trying to check through thousands of lines in video subtitle files (in .srt format) and searched the internet for days on end for a solution, but to no avail. The subs contain in/out timestamps as shown in the following example:
61
00:08:35,504 --> 00:08:38,629
Do you know where he left the car keys?
in hours, minutes, seconds and milliseconds (notice the European-style comma before the millisecond part, representing the decimal point). What I plan to do is parse the timestamp into its two components and check the difference between them, since many are faulty. I built a simple test function to handle the plain hh:mm:ss part which works well:
-(IBAction)checkSubLength:(id)sender
{
NSString *inStr = #"10:10:45";
NSString *outStr = #"10:20:57";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"hh:mm:ss"];
NSDate *inTime = [dateFormatter dateFromString:inStr];
NSDate *outTime = [dateFormatter dateFromString:outStr];
NSTimeInterval distanceBetweenDates = [outTime timeIntervalSinceDate:inTime];
NSLog(#"time difference:%.3f", distanceBetweenDates);
}
However, I just can’t get the fractional part to display no matter what I try. How can I modify/change my code do that? Any help much appreciated.
You need to specify the millis in the format string:
NSString *inStr = #"10:10:45,111";
NSString *outStr = #"10:20:57,222";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"hh:mm:ss,SSS"];
NSDate *inTime = [dateFormatter dateFromString:inStr];
NSDate *outTime = [dateFormatter dateFromString:outStr];
NSTimeInterval distanceBetweenDates = [outTime timeIntervalSinceDate:inTime];
NSLog(#"time difference:%.3f", distanceBetweenDates);
which then prints
time difference:612.111
as expected

Error when convert float to datatime

I have this command that is being called each second in background mode, Every time he is called I add +1 to my variable of type float, and convert to date format:
xis = (xis + 1) / 000001;
//The Format which you want as output
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
dateFormat.dateFormat = #"ss:mm:hh";
[dateFormat setTimeZone:[NSTimeZone systemTimeZone]];
//The Format in which your dateTime currently is
NSDateFormatter *dateFormat1 = [[NSDateFormatter alloc] init];
dateFormat1.dateFormat = #"hh.mm";
[dateFormat1 setTimeZone:[NSTimeZone systemTimeZone]];
NSString *timeStr = [NSString stringWithFormat:#"%.2f",xis];
NSDate *dates = [dateFormat1 dateFromString:timeStr];
NSLog(#"Time: %#", [dateFormat stringFromDate:dates]);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
recordTime.text = [NSString stringWithFormat:#"%#",[dateFormat stringFromDate:dates]];
}];
But this code has a problem when the time comes 00:00:12, something goes wrong and the time is the value (null), which may be causing this and how can I solve?
There are two issues:
I would advise against using the NSTimer routine to keep track of elapsed time yourself. Timers may not be called with the frequency you expect. Also, if the user suspended the app and came back to it, you don't really want to try to keep this timer going while the app is no longer in the foreground. In short, you want to decouple the updating of the UI from the calculation of the elapsed time string representation.
So, instead, one should capture the "start time" and then have the routine get the current time, compare that to the "start time" and calculates the string representation of this elapsed time. But notably, there is no incrementing of variables for seconds elapsed, but rather one should rely on the system time of the device.
Your formatting problem stems from the awkward conversion of your numeric counter to a date string. The problem was complicated by the fact that the formatting string that was backwards, ss:mm:hh.
First (and to my above point), I'd probably calculate the time elapsed in seconds, by capturing the start time up front and store it in a CFAbsoluteTime property:
self.start = CFAbsoluteTimeGetCurrent();
And, if you really wanted to use NSDateFormatter, instead of using it to interpret a numeric value, I would only use it for converting to and from NSDate objects. You could, for example, get the NSDate for 00:00:00, use dateByAddingTimeInterval to add the elapsed time to the date, and then use the formatter to get the output string:
// calculate the time elapsed in seconds
CFTimeInterval elapsed = CFAbsoluteTimeGetCurrent() - self.start;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = #"HH:mm:ss";
NSDate *start = [formatter dateFromString:#"00:00:00"];
NSDate *end = [start dateByAddingTimeInterval:elapsed];
NSString *elapsedString = [formatter stringFromDate:end];
Having said that, I'd probably favor a couple of different approaches. One is to just calculate hours, minutes, and seconds manually:
// calculate the time elapsed in seconds
CFTimeInterval elapsed = CFAbsoluteTimeGetCurrent() - self.start;
// convert this to hours, minutes, and seconds
double seconds;
double minutes;
double hours;
seconds = modf(elapsed / 60.0, &minutes) * 60.0;
minutes = modf(minutes / 60.0, &hours) * 60.0;
// create format string
NSString *elapsedString = [NSString stringWithFormat:#"%02.0f:%02.0f:%04.1f", hours, minutes, seconds];
Or a completely different approach would be to use NSCalendar. In this scenario, one might have a NSDate property that you initialize with the start time:
self.startDate = [NSDate date];
And then you could do something like:
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDateComponents *components = [calendar components:NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:self.startDate toDate:now options:0];
NSString *elapsedString = [NSString stringWithFormat:#"%02ld:%02ld:%02ld", (long)components.hour, (long)components.minute, (long)components.second];
Just a few approaches. But I'd advise against manually incrementing your counter manually, and I might suggest one of these latter techniques.

NSDate won't load/display correctly

I'm not sure what's going on with this. I'm trying to load an NSString* object from a file, convert it to an NSDate* with a date formatter, and then convert the hour and minute components back to NSString so I can display a time in Interface Builder. However, instead of the time that was saved to the file, instead I end up with 19 for the hour, and 0 for the minute. (Regardless of what was put in, the program loads four different NSDates)
Here's the code for loading the date from the file (I checked with breakpoints, and the array does indeed have the correct data, so that's not the problem)
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"yyyy-MM-dd hh:mm:ss a"];
date1 = [[df dateFromString:[loadArray objectAtIndex:3]] retain];
Here's the code for displaying the date.
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comp = [[gregorian components:NSHourCalendarUnit|NSMinuteCalendarUnit fromDate:myDrug.date1] retain];
hourField1.text = [NSString stringWithFormat:#"%d", comp.hour];
minuteField1.text = [NSString stringWithFormat:#"%d", comp.minute];
(hourField1 and minuteField1 are the IBOutlets that receive the values, by the way)
I'm not sure where I've gone wrong here, and any help would be greatly appreciated. Thanks!
Update:
On the suggestion of some of the people here, I've NSLogged the problem, and I've found that it the date formatter that's not working. An example date is 2011-02-14 06:00:00 GMT, and the date formatter is yyyy-MM-dd hh:mm:ss a, so I'm not sure why it won't work.
If the date strings in loadArray are of the form 2011-02-14 06:00:00 GMT, then the format should be set as follows:
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"yyyy-MM-dd HH:mm:ss zzz"]; <--
date1 = [[df dateFromString:[loadArray objectAtIndex:3]] retain];
// the retain above is suspicious btw (but that's another question)
[df release]; //don't forget this
I also changed the hh to HH assuming that the hours are actually in 24-hour instead of 12-hour format. See Unicode Date Format Patterns for details.
Next, when displaying the date, if you want to show the hours and minutes in GMT instead of whatever the user's current time zone is, you'll need to set the calendar's time zone:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSTimeZone *tz = [NSTimeZone timeZoneWithName:#"GMT"]; <--
[gregorian setTimeZone:tz]; <--
NSDateComponents *comp = [gregorian components:NSHourCalendarUnit|NSMinuteCalendarUnit fromDate:myDrug.date1];
//do not do a retain on comp above
hourField1.text = [NSString stringWithFormat:#"%d", comp.hour];
minuteField1.text = [NSString stringWithFormat:#"%d", comp.minute];
[gregorian release]; //don't forget this

Convert 24h to 12h time format in Obj-C

I currently display time in 24h format, because it's the easiest thing for me to do right now with the data I have.
I get the time in "minutes since midnight", so for example, 07:00 or 7:00 a.m is "420" and 21:30 or 9:30 p.m is "1290" and so on.
[NSString stringWithFormat:#"%02d:%02d - %02d:%02d", (open / 60), (open % 60), (close / 60), (close % 60)]
Is there a nice way to use NSDateFormatter to convert from 24h to 12h? I have tried a bunch of things, but I never end up with 100% correct formatting.
I have also tried with lots of if statements, only to end up with way too many lines of code, which should be completely unnecessary in my opinion for such a relatively "easy" job.
Also, no matter I try I also end up with wrong 12h formatting for hours without "1" in the beginning, for example "09:30 a.m.", etc. I can strip this by looking for the suffix, but again this just seems to tedious and weird.
You should really use the system's default date formatting:
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setHour:hours];
[comps setMinute:minutes];
NSDate* date = [[NSCalendar currentCalendar] dateFromComponents:comps];
NSString* dateString = [NSDateFormatter localizedStringFromDate:date dateStyle:NSDateFormatterNoStyle timeStyle:NSDateFormatterLongStyle];
Or if you insist you can do
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"hh:mm a"];
NSString* dateString = [dateFormatter stringFromDate:date];
For someone new like me I struggled with the date formatter and found that the [NSCalendar currentCalendar] will use the users preferences to set timezone. In my situation I wanted to convert a time that a server gave me so it was always wrong. I used this simple function in my case.
- (NSString *)formatTime:(NSString *)time
{
NSString *hour = [time substringWithRange:NSMakeRange(0, 2)];
NSString *minute = [time substringWithRange:NSMakeRange(2, 2)];
NSString *tail = ([hour integerValue] > 11) ? #"PM" : #"AM";
return [NSString stringWithFormat:#"%#:%# %#", hour, minute, tail];
}
You can try this.It works for me
NSDateFormatter* df = [[NSDateFormatter alloc] init];
[df setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]];
[df setTimeZone:[NSTimeZone systemTimeZone]];
[df setDateFormat:#"yyyy-mm-dd hh:mm:ss"];
NSDate* newDate = [df dateFromString:[df stringFromDate:[NSDate date]]];
[df setDateFormat:#"hh:mm a"];
newDate = [df stringFromDate:[NSDate date]];
check if your minutes are less than 720 (12 hours), if so its AM, if not its PM (so you would do hours -12 to get from military to 12h) then add the suffix as needed. Its not pretty, but its a relatively simple formatting job.

What's the best/easiest way to compare two times in Objective-C?

I've got a string representation of a time, like "11:13 AM." This was produced using an NSDateFormatter and the stringFromDate: method.
I'd like to compare this time to the current time, but when I use the dateFromString: method to turn the string back into a date, a year, month and day are added - which I don't want. I just need to know if right now is < or > the time stored in the string.
What's going to be the best way to handle that? Thanks in advance for your help.
Instead of using the string representation, use the NSDate you got from the picker. You can convert that hour/min/sec using NSDateComponents, then also convert [NSDate date] to NSDateComponents. Compare the hours/minutes/seconds of the two sets of components.
EDIT -- use a utility function for things like this that converts the hr/min/sec components of NSDate into a secondsOfTheDay (seconds since midnight).
You can directly use two time of day values since they are both seconds since midnight. Simple integers can be easily compared and stored and manipulated. You don't have to use NSDate all the time.
//----------------------------------------------------------------------------
// extracts hour/minutes/seconds from NSDate, converts to seconds since midnight
//----------------------------------------------------------------------------
unsigned secondOfTheDay( NSDate* time )
{
NSCalendar* curCalendar = [NSCalendar currentCalendar];
const unsigned units = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents* comps = [curCalendar components:units fromDate:time];
int hour = [comps hour];
int min = [comps minute];
int sec = [comps second];
return ((hour * 60) + min) * 60 + sec;
}
If I understand the problem correctly, you’re using the dateFromString: method of NSDateFormatter. This is giving you the correct time, but with a default date of January 1, 1970, which is useless to compare against the current date/time.
This is easy to solve. Use setDefaultDate: to set the default date to today.
NSDate *now = [NSDate date];
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateFormat:#"hh:mm a"];
[formatter setDefaultDate:now];
NSDate *theDate = [formatter dateFromString:#"11:13 AM"];
NSComparisonResult theResult = [theDate compare:now];
Can you reuse the string formatter that you used to create the string? So, let's say you created the string like this:
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init];
[formatter setDateFormat:#"HH:mm a"];
NSString *dateAsString = [formatter stringFromDate:[NSDate date]];
You can get an NSDate like this:
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init];
[formatter setDateFormat:#"HH:mm a"];
NSDate *date = [formatter dateFromString:dateAsString];
The day, month, year and timezone information will not be kept, but you'll have an NSDate object with the values of 1/1/1970 and GMT for the timezone offset.
At this point you can use the compare: (which is typically reserved for sorting operations) or the laterDate: or earlierDate: methods.
Be careful using NSDateFormatter like this, as you may run into issues with internationalization.
If you need to add information about the current date to the date you get from dateFromString:, such as the month day and year, you'll need to use NSCalendar's dateByAddingComponents:toDate:options: method.
If the string was originally created from an NSDate, then you'll want to use that original NSDate to compare against [NSDate date] using NSDate's compare: method (or some variant, such as earlierDate: or laterDate:).
You should use 24-hour based NSDateFormatter and compare as strings ("09:00am" > "08:00pm", but "09:00" < "20:00") . But in general it is right to operate with NSDate instances instead of strings