NSDateFormatter dateFromString and iPhone in 24 Hour Format Confusion - iphone

I'm having a problem. I get incoming time strings in 12-hour format, and I'm turning them into NSDate objects. When the iPhone is in 12 hour format, no problem. But when it's in 24 Hour format, things go wrong. Here's some sample code to demonstrate:
NSString *theTime = #"3:19 PM";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"h:mm a"]; // "3:19 PM"
NSDate *date = [formatter dateFromString:theTime];
NSString *theString = [formatter stringFromDate:date];
In 24 hour mode, date is 1970-01-01 03:19:00, and theString is "3:19" - WRONG
In 12 hour mode, date is 1970-01-01 15:19:00, and theString is "3:19 PM" - RIGHT
So... question 1: why is the device's 24 hour setting overriding my date formatter setting?
and more importantly, question 2: How do I get a proper conversion from 12 hour time to 24 hour time?
I already have code to detect if the phone is in 24 hour mode, but other than digging around in the string and swapping the 3 with a 15, there doesn't seem to be a clean way to do this.

Not sure if you still need it, but I've had a similar problem which got solved by setting the locale for the date formatter. That is, if you want to force it to 12-hour mode, regardless of the user's 24/12 hour mode setting, you should set the locale to en_US_POSIX.

The reason for this behaviour is Locale, set the correct Locale
NSString *strAgendaDate = #"01/17/2012 12:00 AM";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"] autorelease];
[dateFormatter setDateFormat:AgendaDateFormatForMeeting];
NSDate *meetingDate = [dateFormatter dateFromString:aStrDate];
[dateFormatter setDateFormat:AgendaDateRepresentation];
strAgendaDate = [dateFormatter stringFromDate:meetingDate];
It works for both 24-hour and 12 hour format

I believe the #"h:mm a" should be #"HH:mm a".
If you use the pre-build dateformatter in cocoa, everything will be taken care of for you.
NSDateFormatter *timeFormatter = [[[NSDateFormatter alloc] init] autorelease];
[timeFormatter setTimeStyle:NSDateFormatterShortStyle];
[timeFormatter setDateStyle:NSDateFormatterNoStyle];
NSDateFormatterShortStyle and NSDateFormatterNoStyle comes in different varieties.
Using those will make sure you respect the settings the user has selected for dates and times.
The 12-14 hour clock conversion is taken care of by the SDK, if you have a model or some value object for storing your dates try to keep them as NSDate. This way you can format them only when you need to display them. Saving dates as strings could open a world of trouble when you maybe parse them from xml where the GMT is specified separately or try to add and subtract NSTimeIntervals.

I changed from #"hh:mm:ss" to #"HH:mm:ss" and time style was changed from "1:03 PM" to "13:03".
Hope this will help you.

Okay, I left a comment, but it squished all the code together, so I'll have to "answer" my question with a comment:
Thanks. I gave it a whirl with this code:
NSString *theTime = #"3:19 PM";
NSDateFormatter *timeFormatter = [[[NSDateFormatter alloc] init] autorelease];
[timeFormatter setTimeStyle:NSDateFormatterShortStyle];
[timeFormatter setDateStyle:NSDateFormatterNoStyle];
NSDate *date = [timeFormatter dateFromString:theTime];
NSString *theString = [timeFormatter stringFromDate:date];
And date comes up nil. I ran into this earlier when I tried this route, and it's not working. Very frustrating.

Related

date format in ios

I want to ask that how i get the device date and time format in my like device setting am/pm or 24 hrs what time and what format is set???
Like if i use this format
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"mm-dd-yyyy"];
insertCmd = [insertCmd stringByAppendingString:formatter setDateFormat:#"MM.dd.yyyy"];
it set the date format but i want the setting user set in device?
Try this,
NSDate *date = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
NSLog(#"date = %#",[dateFormatter stringFromDate:date]);
This is for converting current date to string. It should be based on the currentLocale of the device. You dont have to set anything. However I am not sure what you meant by the code in your question.
This is what mentioned in documentation,
The format for these date and time styles is not exact because they
depend on the locale, user preference settings, and the operating
system version. Do not use these constants if you want an exact
format, for example if you are parsing an external data file which
contains date information in a fixed format. There are several
different “lengths” of the formats:
Or:
NSDate* date = [NSDate date];
NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
[formatter setFormat:#"mm-dd-yyyy"];
NSString* dateString = [formatter stringFromDate:date];
insertCmd = [insertCmd stringByAppendingString:dateString];

NSDateFormatter ignores 24hour time format

I have been sitting with this for quite sometime now. I'm tot able to figure out, what I am doing wrong.
NSString * dateString = #"2011-11-21 13:00";
NSDateFormatter *serverDateFormatter = [[NSDateFormatter alloc] init];
[serverDateFormatter setDateFormat:#"yyyy-MM-dd HH:mm"];
NSDate *serverDate = [serverDateFormatter dateFromString:dateString];
NSLog(#"%#", serverDate);
Output :
2011-11-21 12:00:00 +0000
Why does 13:00 get converted to 12:00? What am I doing wrong?
You need to set the time zone, example
NSString * dateString = #"2011-11-21 23:20";
NSDateFormatter *serverDateFormatter = [[NSDateFormatter alloc] init];
[serverDateFormatter setDateFormat:#"yyyy-MM-dd HH:mm"];
[serverDateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
NSDate *serverDate = [serverDateFormatter dateFromString:dateString];
NSLog(#"%#", serverDate);
OUTPUT:
2011-11-23 14:22:10.924 xxx [23235:207] 2011-11-21 23:20:00 +0000
I think you might find the answer in this discussion. Apparently NSDateFormatter likes to use the user's settings, which may not be appropriate.
http://developer.apple.com/library/ios/#qa/qa1480/_index.html
Though I think this answer is for stopping 24-hour behavior, which is what you want to force.
I'd say you want to get the setLocale part right.
ETA: Here's some more info on this:
https://stackoverflow.com/q/7538489/290072
This will allow your code to detect whether the device will try to do 12hr or 24hr times.
What if you output via [serverDateFormatter stringFromDate:serverDate]; ?

Why does my NSDateFormatter sometimes return an a.m. or p.m. with yyyyMMddHHmmssSSS?

Sometimes my code is returning an a.m. or p.m. but not always. Most of the time it just returns what I expect, which is something like: 20110815170852164
But other times it's returning: 20100412010241 a.m.450
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateFormat:#"yyyyMMddHHmmssSSS"];
NSString *dateString = [dateFormatter stringFromDate:[NSDate date]];
What could be causing this? Specific date/time country settings on users iPhones? I have this out to thousands of people, and most aren't returning the a.m. (which is expected) but others are. WHY?
The problem you are describing is a known bug. Check out some discussion on the problem on stackoverflow, and you can find some possible work-arounds there.
Here's an excerpt of huyz's explaination of the bug:
The problem comes from NSDateFormatter somehow “getting stuck” in the 12 or 24-hour time mode that the user has manually selected. So if a French user manually selects 12-hour mode, and the application requested NSDateFormatter to output time with the 24-hour format “HHmm”, it would actually receive time in a 12-hour format, e.g. “01:00 PM”, as if the application had instead requested “hhmm aa”. The reverse would happen if a US user manually selected 24-hour mode: outputting time with the 12-hour format “hhmm aa” would actually get you time in the 24-hour format instead, e.g. “17:00″.
Right. The solution is to explicitly set the locale of the date formatter, ideally to en_US_POSIX. See the final answer to this question.
I was working on a data-share function of a project which was highly depending on the values/results entered in advance when I bumped into the same problem. I implemented a nasty little workaround for saving the right datetime in 24h format to the database.
The solution is very simple
First, I get the local date from the device:
NSDate* now = [NSDate date];
Second step is to create a NSDateFormatter with US locale regardless of system's locale to make sure that "PM" is going to be "PM":
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]];
Then, I get the date (year, month, day) into a string:
[formatter setDateFormat:#"yyyy-MM-dd"];
NSString *date = [formatter stringFromDate:now];
For the next part, I get the hours, minutes and seconds:
[formatter setDateFormat:#"hh"];
NSString *hours = [formatter stringFromDate:now];
[formatter setDateFormat:#"mm"];
NSString *minutes = [formatter stringFromDate:now];
[formatter setDateFormat:#"ss"];
NSString *seconds = [formatter stringFromDate:now];
Next, I get the part of the day:
[formatter setDateFormat:#"a"];
NSString *partOfDay = [formatter stringFromDate:now];
Then comes the logic: if the part of the day is PM but the hours are less then 12, we just have to correct that. In code:
if([partOfDay isEqualToString:#"PM"] && [hours intValue] < 12) {
hours = [[NSString alloc] initWithFormat:#"%i", ([hours intValue] + 12)];
}
After this modification, we are ready to put the string together in order to have a datetime ready to be saved into SQL:
NSString *sqlDateTime = [[NSString alloc] initWithFormat:#"%# %#:%#:%#", date, hours, minutes, seconds];
That's all there is to it. And from now we can only hope that Apple is going to fix this bug to make it work as it should be.
I hope this helps.

NSDateFormatter when iPhone set to 12-hour clock

I'm using an NSDateFormatter to format an NSDate to be in the format "HH:mm 'on' dd MMMM YYYY".
This is the code I've written:
[dateFormatter setDateFormat:#"HH:mm 'on' dd MMMM YYYY"];
[dateFormatter setLocale:[NSLocale currentLocale]];
Then I'm updating a label to display "Data from 12:45 on 22 September 2010", using the method stringFromDate.
NSString *timeAndDateUpdated = [[NSString alloc] initWithFormat:#"Data from %#.", [dateFormatter stringFromDate:date]]
The label is updated correctly if the time on the iPhone is set to 24-hour, but if it's set to 12-hour, the label displays "Data from (null)".
Does anyone know how to solve this problem?
Thanks.
I just ran into this too.
It appears that dates are parsed by the NSDateFormatter according to locale, even if you explicitly specify a date format string!
So in this case, the 24-hour clock HH is being ignored and it's trying to parse using the 12-hour clock as set on the device.
The way to fix it is to explicitly override the NSDateFormatter's locale; in this example setting it to en_US_POSIX will force the 24-hour clock to be used when parsing the hour, i.e.
[dateFormatter setDateFormat:#"HH:mm 'on' dd MMMM YYYY"];
[dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"] autorelease]];
If the date is solely presented to the user (the "on" suggests it is), I wouldn't force a date format. It's much more friendly to use the long/medium/short date and time styles, which behave according to the user's region format:
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
[timeFormatter setDateStyle:NSDateFormatterNoStyle];
[timeFormatter setTimeStyle:NSDateFormatterMediumStyle];
NSString * timeAndDate = [NSString stringWithFormat:"%# on %#",
[timeFormatter stringFromDate:date],
[dateFormatter stringFromDate:date]];
Also note that configuring an NSDateFormatter is incredibly expensive (about 10 ms IIRC). It's fine if you're only going to display it once and not update very often, but it makes scrolling a bit slow if you have a date per table cell. I recently made graph-drawing 50x faster by cacheing date formatters...
I ran this code on 3.1.2 and 4.1 with both 12 and 24 hour settings and it worked fine.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm 'on' dd MMMM YYYY"];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSString *timeAndDateUpdated = [NSString stringWithFormat:#"Data from %#.", [dateFormatter stringFromDate:[NSDate date]]];
NSLog(#"%#", timeAndDateUpdated);
If this code doesn't work for you, check for custom extensions/categories for NSDateformatter or NSDate.

NSDateFormatter, am I doing something wrong or is this a bug?

I'm trying to print out the date in a certain format:
NSDate *today = [[NSDate alloc] init];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyyMMddHHmmss"];
NSString *dateStr = [dateFormatter stringFromDate:today];
If the iPhone is set to 24 hour time, this works fine, if on the other hand the user has set it to 24 hour time, then back to AM/PM (it works fine until you toggle this setting) then it appends the AM/PM on the end even though I didn't ask for it:
20080927030337 PM
Am I doing something wrong or is this a bug with firmware 2.1?
Edit 1: Made description clearer
Edit 2 workaround: It turns out this is a bug, to fix it I set the AM and PM characters to "":
[dateFormatter setAMSymbol:#""];
[dateFormatter setPMSymbol:#""];
The reason for this behaviour is Locale, It sets the correct Locale.
Set the local of your NSDateFormatter to en_US_POSIX will fix this.
It works for both 24-hour and 12 hour format.
On iPhone OS, the user can override the default AM/PM versus 24-hour time setting (via Settings > General > Date & Time > 24-Hour Time), which causes NSDateFormatter to rewrite the format string you set. From apple doc
Try this,
NSDate *today = [[NSDate alloc] init];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"]];
[dateFormatter setDateFormat:#"yyyyMMddHHmmss"];
NSString *dateStr = [dateFormatter stringFromDate:today];
Here's the explanation of the iPhone SDK bug (also still there in 3.1 beta SDK)
First, a little background on the iPhone user interface. When iPhone
users change their region format between, say, “United States” and
“France”, the users’ “24-Hour Time” setting is automatically switched
to the mode that is most prevalent in that region. In France, that
would set 24-Hour Time to “ON”, and in the U.S., that would set it to
“OFF”. The users can then manually override that setting and that’s
where trouble starts.
The problem comes from NSDateFormatter somehow “getting stuck” in the
12 or 24-hour time mode that the user has manually selected. So if a
French user manually selects 12-hour mode, and the application
requested NSDateFormatter to output time with the 24-hour format
“HHmm”, it would actually receive time in a 12-hour format, e.g.
“01:00 PM”, as if the application had instead requested “hhmm aa”.
The reverse would happen if a US user manually selected 24-hour mode:
outputting time with the 12-hour format “hhmm aa” would actually get
you time in the 24-hour format instead, e.g. “17:00″.
More details and a possible workaround can be found on this blog.
Using the code you posted on both the simulator and a phone with the 2.1 firmware and 24-hour time set to off, I never had an AM/PM appended to dateStr when I do:
NSLog(#"%#", dateStr);
Are you doing anything else with dateStr that you didn't post here? How are you checking the value?
Follow up
Try turning the am/pm setting on then off. I didn't have the problem either, until I did that. I am printing it out the same way you are.
Okay, I see it when I do this also. It's gotta be a bug. I recommend you file a bug report and just check for and filter out the unwanted characters in the meantime.
Setting locale on date formatter to en_US fixes the problem for me:
NSDateFormatter * f = [[NSDateFormatter alloc] init];
[f setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
f.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
f.calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
f.locale = [[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"] autorelease];
I'm not sure if adding the calendar is also needed, but this works well.
I think this is the solution .
NSDateFormatter *df =[[NSDateFormatter alloc] init];
[df setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
[df setLocale: usLocale];
[usLocale release];
NSDate *documento_en_Linea =[[[NSDate alloc] init]autorelease];
documento_en_Linea=[df dateFromString:#"2010-07-16 21:40:33"];
[df release];
NSLog(#"fdocumentoenLineaUTC:%#!",documento_en_Linea);
//ouput
fdocumentoenLineaUTC:2010-07-16 09:40:33 p.m. -0500!
For those finding this question who want to use NSDateFormatter to parse 24-hour time and are hitting this bug, using NSDateComponents to parse dates and times which have a known format sidesteps this issue:
NSString *dateStr = #"2010-07-05";
NSString *timeStr = #"13:30";
NSDateComponents *components = [[NSDateComponents alloc] init];
components.year = [[dateStr substringToIndex:4] intValue];
components.month = [[dateStr substringWithRange:NSMakeRange(5, 2)] intValue];
components.day = [[dateStr substringFromIndex:8] intValue];
components.hour = [[timeStr substringToIndex:2] intValue];
components.minute = [[timeStr substringFromIndex:3] intValue];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *date = [calendar dateFromComponents:components];
[components release];
[calendar release];
This should also work (I am seeing some bizzare results though).
-(NSString*)lowLevTime:(NSString*)stringFormat {
char buffer[50];
const char *format = [stringFormat UTF8String];
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
strftime(buffer, sizeof(buffer), format, timeinfo);
return [NSString stringWithCString:buffer encoding:NSASCIIStringEncoding];
}
Short answer: try [dateFormatter setDateFormat:#"yyyyMMddhhmmss"]; for 12 hour format (note the lowercase hh).
It's been a frustrating topic because so many websites indicate to use HH for hours (including the official Apple documentation), but that sets it to 24 hour format, whereas hh uses 12 hour format. See http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns for more details.
As a bonus, note that you can also use KK or kk for hour of the day format, which will likely be off by one.
Update:
I was recently looking at NSLocale (https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSLocale_Class/Reference/Reference.html) and it would seem that you can use autoupdatingCurrentLocale to apply changes made from within the app to the Locale. The upshot of this is that even if the phone is set to use a 24 hour clock (like when you switched to France), you can make a 12/24 toggle for the app that won't impact any other apps on the phone, or require you to leave the app to make the change.