iOS 6.1 NSDateFormatter has changed since iOS 5 - broken parsing? - iphone

Our iOS iPhone app contains this code which produced a valid NSDate object below named resultDate in iOS 5:
static NSDateFormatter *invariantFmt = nil;
if (!invariantFmt) {
invariantFmt = [[NSDateFormatter alloc] init];
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
[invariantFmt setLocale:locale];
[locale release];
[invariantFmt setDateStyle:NSDateFormatterShortStyle];
[invariantFmt setTimeStyle:NSDateFormatterMediumStyle];
}
NSDate *resultDate = [invariantFmt dateFromString:#"08/04/2010 10:43:39 AM"];
After upgrading to XCode 4.6 and iOS 6.1, that code now gives a nil for resultDate, so something has changed with what they use to parse. The release notes says nothing about NSDateFormatter changing. Internet research has turned up only that they might have changed to use a newer Unicode UTS Locale parsing standard. Obviously they changed something. After twiddling with code and taking a known valid NSDate object and applying the same NSDateFormatter settings to get a NSString, I find that iOS 6.1 likes this string instead: #"08/04/2010, 10:43:39 AM"
The only difference being that extra comma after the date portion. Using that in iOS 6.1 gives back a valid date with the same above code. Anyone seeing this and understand why that's different or if that's an ok Unicode change or a Apple bug?

The format styles should only be used to convert NSDate objects to text to display to the user. When parsing a date string in a known format, you must use a specific format, never the styles. The use of the en_US_POSIX locale is used to ensure that the format you specify isn't tweaked by the OS based on user preferences such as the 24-hour time setting.
So, as you suspected, you need to remove the two calls to set the date and time styles and replace them with a call to set a specific format that matches your known date/time string you need to parse.

Generally you should not output a formatted Date to a file, and later want to parse it back.
A Date should be stored as long value UTC (with or without additional TimeZone offset).
Only in the last moment, before visualizing a Date to UI it should be formatted and local time applied.
This not only i smy experience, it it is also stated in Apple DateFormatting Doku.

Related

date formatting returns 12 hour format even when locale idenifier en_GB set

+(NSDate *)DateServerFormatFromString:(NSString *)date
{
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init] ;
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_GB"];
[dateFormatter setLocale:locale];
[dateFormatter setDateFormat:#"dd/MM/yyyy HH:mm:ss"];
NSDate* returnDate = [dateFormatter dateFromString:date];
return returnDate;
}
The function returns date in format "2013-05-09 08:06:04 AM +0000". But i want it in 24 hour format. The date being passed to this function is in the exact same format as given in setDateFormat method.
The device's region format is set to "United Kingdom" and time format is set to "12 hour format". This should not be changed. When time format is set to 24 hour format in the device, the function works perfectly. What is wrong with the code. I am using iPad 1 with OS 5.1.1. Setting locale identifer or even timezone didnt make a difference. Thanks in advance.
This is not necessarily an answer, just an explanation, found in the Apple docs (here):
Although in principle a format string specifies a fixed format, by
default NSDateFormatter still takes the user’s preferences (including
the locale setting) into account. You must consider the following
points when using format strings:
NSDateFormatter treats the numbers in a string you parse as if they
were in the user’s chosen calendar. For example, if the user selects
the Buddhist calendar, parsing the year 2010 yields an NSDate object
in 1467 in the Gregorian calendar. (For more about different
calendrical systems and how to use them, see Date and Time Programming
Guide.)
In iOS, the user can override the default AM/PM versus 24-hour
time setting. This may cause NSDateFormatter to rewrite the format
string you set.
The problem here is how you're getting the string. You say you got it
by printing the description of returnDate
However, according to the documentation, the -description method says this:
The representation is useful for debugging only.
There are a number of options to aquire a formated string for a date including: date formatters (see NSDateFormatter and Data Formatting Guide), and the NSDate methods descriptionWithLocale:, dateWithCalendarFormat:timeZone:, and descriptionWithCalendarFormat:timeZone:locale:
What this means is that the value returned by -description does not respect any 12- or 24-hour time settings. It's just a debug version.
If you want to express a date as a human-readable string, you must use an NSDateFormatter.
Again and again:
Did you NSLog() the date? Logging a date is always been done in a normalized standard format. If you want to log a date formatted, log the result of a formatter, not the date directly.
You want 24h format for what? Format is used to convert NSDate to NSString with correct string format. Just make reverse converting when you need it
12 hour format
[dateFormatter setDateFormat:#"dd/MM/yyyy hh:mm:ss"];
24 hour format
[dateFormatter setDateFormat:#"dd/MM/yyyy HH:mm:ss"];

Localized date not translating the days

Here is how I am localising days:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
NSString *dayFormat = [NSDateFormatter dateFormatFromTemplate:#"EEEE" options:0 locale:[NSLocale currentLocale]];
[dateFormatter setDateFormat:dayFormat];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSString *day = [dateFormatter stringFromDate:date];
And yet it seems to be returning English days of the week (Monday, Tuesday etc) rather than the device language (which has been set to German in the simulator).
Any idea where I'm going wrong?
Update after doing some research on device I've realised that its actually the region setting, not the language setting which changes the date language. Odd, but I guess its done for a reason.
Thanks
The language of the date is set by the region not the language. This has to be a bug. If I'm in Germany, but an English speaker I don't want to have my dates in German, surely?
Anyway, this is why. You have to change language and region.
I've had the same issue. It wouldn't work on the simulator, but it would on a device. Can you try it ? I did not however solve it, I did not even look more into it as it was working perfectly on the device, which is truly the main target of your app.
Edit:
This comes from Apple's doc:
currentLocale
Returns the logical locale for the current user.
+ (id)currentLocale
Return Value
The logical locale for the current user. The locale is formed from the settings for the current user’s chosen system locale overlaid with any custom settings the user has specified in System Preferences.
Discussion
Settings you get from this locale do not change as a user’s
Preferences are changed so that your operations are consistent.
Typically you perform some operations on the returned object and then
allow it to be disposed of. Moreover, since the returned object may be
cached, you do not need to hold on to it indefinitely. Contrast with
autoupdatingCurrentLocale.
Maybe you can try using:
preferredLanguages
Returns the user's language preference order as an array of strings.
+ (NSArray *)preferredLanguages
Return Value
The user's language preference order as an array of NSString objects, each of which is a canonicalized IETF BCP 47 language identifier.

date formatter different output on different devices running same iOS version

NSDate *currentDate = [NSDate date];
NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:#"MM/dd/yyyy,h,mm,a"];
NSString *dateString = [format stringFromDate:currentDate];
[format release];
NSLog(dateString);
Output in: Device iPhone4
[240:707] 10/25/2011,22,23,
Current language: auto; currently objective-c
warning: Unable to read symbols for /SDK4.2/Platforms/iPhoneOS.platform/DeviceSupport/4.3.3 (8J2)/Symbols/Developer/usr/lib/libXcodeDebuggerSupport.dylib
Output in: Device iPad2
[82:707] 10/25/2011,12,28,PM
Current language: auto; currently objective-c
warning: Unable to read symbols for /SDK4.2/Platforms/iPhoneOS.platform/DeviceSupport/4.3.3 (8J2)/Symbols/Developer/usr/lib/libXcodeDebuggerSupport.dylib
This thing works fine on Simulator for all.
How to handle this?
More Info:
it is also to notice that the same formatting string resulting in 12 hr and 24 hr clock values in return
Sounds like you have a different locale set on your iPad and your iPhone, see here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DataFormatting/Articles/dfDateFormatting10_4.html#//apple_ref/doc/uid/TP40002369-SW1
Either set the same locale on both devices (if it's just your problem) or make sure the NSDateFormatter uses the same (hard-coded) locale if you rely on the format being like a certain format.
Edit: Additional Info
It apparently IS a problem with the locale, more detailed here (with solutions):
http://developer.apple.com/library/ios/#qa/qa1480/_index.html
But I was wrong, setting the locale will not help it indeed.
Citation:
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, which can cause your time parsing to fail.
finally resolved it.
it was due to the settings in phone as 24 Hr clock.
I had to update my code accordingly.
:)

When do strings formatted with NSDateFormatterFullStyle include an era string

Apple's NSDateFormatter Class Reference lists, as an example string for NSDateFormatterFullStyle:
Specifies a full style with complete details, such as “Tuesday, April
12, 1952 AD” or “3:30:42pm PST”
In practice, I'm getting strings along the lines of:
Tuesday, April 12, 1952
by using code like:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.timeStyle = NSDateFormatterNoStyle;
dateFormatter.dateStyle = NSDateFormatterFullStyle;
[dateFormatter stringFromDate:date];
This is the behavior I see in the iOS 5.0 and 4.3 simulators.
Does -[NSDateFormatter stringFromDate:] only append the "AD" string for certain dates? If so, when can I expect it to happen?
There doesn't seem to be anything in the system's region format setting that would affect this.
It also doesn't seem to depend on -[NSDateFormatter eraSymbols] or -[NSDateFormatter longEraSymbols]. In my tests, both are set by default and changing them doesn't change the result of -[NSDateFormatter stringFromDate:].
The actual result that I want is the string that is being generated (without the "AD"), I just want to be sure that I'm doing things correctly.
Maybe this is just a documentation error. If so, I will file a bug report, but I wanted to make sure I wasn't missing anything before doing so.

Inconsistent behaviour with NSDateFormatter on two different devices

I'm having a bit of a problem with NSDateFormatter failing on one user's device (returning nil when parsing a string) and working perfectly when I run it locally (either in the simulator or on my device).
I'm trying to rule out what could be causing a difference in this behaviour. My first thought was the locale but I've tried setting it explicitly to ensure the same locale is always used but it makes no difference.
Here is the code:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ssZ"];
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_GB"];
[dateFormatter setLocale:locale];
[locale release];
NSDate *theDate = [dateFormatter dateFromString:dateString];
NSLog(#"PARSING DATE %# AS %#", dateString, theDate);
On the failing device, I get:
PARSING DATE 2010-11-28T20:30:49-0000 AS (null)
But locally I get:
PARSING DATE 2010-11-28T20:30:49-0000 AS 2010-11-28 20:30:49 +0000
This is driving me crazy, am I missing something else?
I am running 4.2 locally (simulator) and on my device (an iPhone 4). The failing device is a 3GS running 4.2.1.
Any ideas would be much appreciated!
I'm pleased to say that I eventually got to the bottom of this issue and I must pass on my thanks to #bendodson on Twitter for helping me out with this. aBitObvious also hit on the issue in his comment above; I'd have up-voted him if I could.
There was one difference between the user's device and mine, and that was that his device was set to use the 12 hour clock and mine was not. This single thing meant that the NSDateFormatter was unable to parse the time in the above examples and returned nil.
By far the biggest issue for me with this problem was being unable to reproduce the problem locally!
So, to be clear, to solve this issue; that is, if you are parsing date/time strings that are in a known, fixed format (often coming from some API as this was in my case), you should set the correct locale for the date formatter, which will often be en_US_POSIX.
...
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
[dateFormatter setLocale:locale];
[locale release];
For more information on this, read Apple QA1480.