NSDateFormatter Bug : incorrect string to date conversion - objective-c

I have an a string in UTC which i want to convert to NSDate. Somehow Im getting 30 minutes error
The string is
2012-04-10T00:00:00+05:30
the converted date is
2012-04-09 19:00:00 +0000
where as it should have been
2012-04-09 18:30:00 +0000
Im using this function to convert string to date
- (NSDate *)parseRFC3339Date{
NSDateFormatter *rfc3339TimestampFormatterWithTimeZone = [[NSDateFormatter alloc] init];
[rfc3339TimestampFormatterWithTimeZone setDateFormat:#"yyyy-MM-dd'T'HH:mm:ssZ"];
NSDate *theDate = nil;
NSError *error = nil;
if (![rfc3339TimestampFormatterWithTimeZone getObjectValue:&theDate forString:self range:nil error:&error]) {
NSLog(#"Date '%#' could not be parsed: %#", self, error);
}
[rfc3339TimestampFormatterWithTimeZone release];
return theDate;
}

If you were to create an NSRange covering the whole of the string and passed its address in to -getObjectValue:forString:range:error:, then you'd see that on output it would be short. Parsing stopped at the colon because the 'Z' in the format string only matches something like "-0800" (no colon). If you were to specify 4 Zs, 'ZZZZ', it would match something like "GMT-08:00". That allows a colon, but requires "GMT". You can try inserting the "GMT" if you're sure of the format of your date string.

I ran into ISOdateFormatter here http://boredzo.org/iso8601unparser/ which works perfectly. Fund it better than modifying incoming date strings.

Related

Error when converting date string to NSObject

I'm sending details to a server which requires that I convert a date string into an NSDate object. The dictionary that will carry this object is declared like this
NSMutableDictionary *detailsRequest = [NSMutableDictionary dictionary];
Then the code which converts the date string to an object looks like this
[_dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"]];
[_dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSSZZZ"];
[_dateFormatter setAMSymbol:#"am"];
[_dateFormatter setPMSymbol:#"pm"];
[detailsRequest setObject:[_dateFormatter stringFromDate:[_dateFormatter dateFromString:val]] forKey:NDUserServerDateOfBirthKey];
The key NDUserServerDateOfBirthKey is declared as a constant like this
NSString * const NDUserServerDateOfBirthKey = #"dob";
The value for val is always '09/08/1987' and when it hits the last line it throws a SIGABRT error which says
2018-08-23 10:33:04.612769+0100 ClientCore[353:36543] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSDictionaryM setObject:forKey:]: object cannot be nil (key: dob)'
I've tried reducing the _dateFormatter to just [_dateFormatter setDateFormat:#"yyyy-MM-dd"]; but it still crashes with the same error. My string date always has a value so what is it I'm doing wrong?
The error occurs because the date format is wrong and the date string cannot be converted.
Please try to understand the format. The date string contains day (d), month (M) and year (y) slash separated – or month (M), day (d) and year (y) - but there is no letter T, no hyphens (-), no hours (H), no minutes (m), no seconds (s), no milliseconds (S), no timezone (Z) and no am/pm
The format is #"dd/MM/yyyy" or #"MM/dd/yyyy"
And why are you converting string to date and then immediately back to string?
If you want to convert dd/MM/yyyy to ISO8601 you need two different formats for input and output
NSString *val = #"09/08/1987";
NSDateFormatter *_dateFormatter = [[NSDateFormatter alloc] init];
_dateFormatter.locale = [NSLocale localeWithLocaleIdentifier:#"en_US_POSIX"];
_dateFormatter.dateFormat = #"dd/MM/yyyy";
NSDate *date = [_dateFormatter dateFromString:val];
_dateFormatter.dateFormat = #"yyyy-MM-dd'T'HH:mm:ssZZZ";
NSString *convertedString = [_dateFormatter stringFromDate:date];

Adding an object to NSMutableArray causing crash

I am trying to combine a string date and time then convert that to an NSDate. My code is:
NSMutableArray *arrayOfDatesAsDates = [[NSMutableArray alloc] init];
NSDateFormatter *dateAndTimeFormatter = [[NSDateFormatter alloc] init];
[dateAndTimeFormatter setLocale:enUSPOSIXLocale];
[dateAndTimeFormatter setDateFormat:#"dd-MM-yyyy HH:mm"];
NSLog(#"here");
//create an NSDate with todays date and the right prayer time
NSString *prayerDateString = [curDate stringByAppendingString: #" "];
prayerDateString = [prayerDateString stringByAppendingString: timeOfMagrib];
NSDate *prayerDateAndTime = [dateAndTimeFormatter dateFromString:prayerDateString]; //convert string back to date
NSLog(#"nsdate %#", prayerDateAndTime);
[arrayOfDatesAsDates addObject:prayerDateAndTime];
The output to the log of prayerDateAndTime is 2013-07-08 20:26:00 +0000 as expected and the error message is Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'.
It crashes at the [arrayOfDatesAsDates addObject:prayerDateAndTime]; line.
Why is this?
Many thanks
It looks like [dateAndTimeFormatter dateFromString:#"2013-07-08 20:26:00 +0000"] is returning nil because your date string "2013-07-08 20:26:00 +0000" does not match your dateFormat: #"dd-MM-yyyy HH:mm" ... try running after replacing:
[dateAndTimeFormatter setDateFormat:#"dd-MM-yyyy HH:mm"]
with
[dateAndTimeFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss ZZZ"]
// you really want this to match: 2013-07-08 20:26:00 +0000
// yyyy: 2013, four digit year
// MM: two digit numerical month
// dd: day of month
// HH: 24 hour hour
// mm: two digit minute
// ss: two digit second, zero padded
// ZZZ: time zone, {Z,Z,Z} -> RFC 822 GMT format
Format strings for Date Formats given here:
http://www.unicode.org/reports/tr35/tr35-25.html#Date_Field_Symbol_Table
Timezone string acquired from: https://stackoverflow.com/a/3299389/2022405

parsing date from string, nsdate, objective c

Below is a string represented a date
NSString *dateStr = #"2011-07-06";
And when I am trying to convert it to NSDate by doing :
NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:#"yyyy-MM-dd"];
NSDate *tmp = [format dateFromString:dateStr];
NSLog(#"tmp is %#",[tmp description]);
What I am getting from the console is
tmp is 2011-07-06 04:00:00 +0000
I dont understand why I am getting extra info :04:00:00 +0000 for the result
Please help if you experienced it before
Your code
NSString *dateStr = #"2011-07-06";
NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:#"yyyy-MM-dd"];
NSDate *tmp = [format dateFromString:dateStr]
will result in a NSDate object, that represents your local time at 0:00 — the beginning of the day.
but if you print a plain date object, it will result in a string that represents the time in GMT timezone, as internally all dates are normalized to that timezone.
As your string is 4 hours ahead, we can tell, that you most likely are in East Europe, maybe Moscow.
So if you want to see the string in your timezone, you need to use a NSDateFormatter to create it from the date object.
NSLog(#"tmp is %#",[formatter stringFromDate:tmp]);
to check, if it is correct, what I said, change the format to contain the time components.
formatter.format = [#"yyyy-MM-dd HH:mm"];
NSLog(#"tmp is %#",[formatter stringFromDate:tmp]);
The formatter will also take "oddities" like Leap Day, Leap Month (yes — those exits), Daylight Saving Times, Leap Seconds … in account — accordantly to the current calendar.
A great WWDC 2011 Video: Performing Calendar Calculations — a must-see for every cocoa developer.
BTW: to print a object with NSLog you dont need to call -description on it to pass in a string. NSLog will do this internally.
NSLog(#"tmp is %#", tmp);
is just fine.
The answer is simple, NSLog just converts the NSDate to a NSString, using its formatter with GMT (zero) timezone.
Your formatter is by default set to your default time zone, which is probably -4:00. When you print it out, NSLog converts it to 0:00, adding 4 hours.
In general, it's unsafe to parse dates without specifying their timezone.

How do you convert date time including milliseconds and timezone info to NSDate

I'm trying to convert a string to an NSDate,
however the format always comes out as nil
The date I'm trying to convert is:
2012-08-16T16:20:52.619000+00:00
The date format I'm trying is:
#"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZZZZ"
If I change the date to:
#"2012-08-16T16:20:52.619000+0000" // removing the : from +00:00
it works a treat, however I would
(I have also tried
#"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZZ:ZZ"
#"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZZ':'ZZ"
but that didn't work either).
Is it even possible to do this without doing string manipulation and removing the final ":"?
I did a final search around this and found out that you have to use
getObjectValue
rather than
dateFromString
In case someone else runs in to this issue, I post my method for converting such strings to NSDate
+ (NSDate *)dateFromString:(NSString *)dateString {
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"];
NSDate *theDate = nil;
NSError *error = nil;
[dateFormat getObjectValue:&theDate forString:dateString range:nil error:&error];
[dateFormat release];
return theDate;
}
It looks like you are using ISO 8601 formatted dates. If you are getting these from a web service, the format changes according to the format. Check this out:
http://boredzo.org/iso8601parser/
This will convert dates according to the format, and even when the format changes slightly.
How about something like
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
the Z has to be in single quotes.

Getting an NSDate via NSDateFormatter from a string of unknown format

I know there are many NSDateFormatter questions on here, so if I duplicate, I'm sorry. I just couldn't find anything that was quite what Im asking.
From all the questions here on SO, I have come to the conclusion that -[NSDateFormatter dateFromString:] will always return NULL if your formatter object doesn't have the correct date format. How do you get a date from a string if you don't know the format? I'm trying to get a date from a UITextField.
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[NSLocale currentLocale]];
[formatter setLenient:YES];
NSDate *tempDate = [formatter dateFromString:self.birthdayTxtfld.text];
self.currentCustomer.birthday = ([self.birthdayTxtfld.text isEqualToString:#""]) ? NULL : tempDate;
[formatter release];
tempDate is always NULL.
I think your taking the wrong approach. I would on the other hand restrict and format the UITextField so the user has to enter the date in a specific format. Or just use a date picker. There are just way too may different inputs the user could give you.
Or you can read through this: NSDate
Another option is to create a list of accepted date formats:
#define DATEFORMATS #[#"MM/dd/yyyy", #"MM/dd/yy",...
Then Have a method that you pass the date string to and check if you can format it:
+ (NSDateFormatter*)getDateFormat:(NSString*)dateString {
NSArray *dateFormats = DATEFORMATS;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
NSDate *date = nil;
for (NSString *dateFormat in dateFormats) {
[formatter setDateFormat:dateFormat];
date = [formatter dateFromString:dateString];
if (date) {
return formatter;
}
}
return nil;
}
If you get nil its not a date or its in a format you don't support. Otherwise you will have the correct format you need. You can switch this around to return the date instead of the format. I have it this way because I needed the format not the date for a project.
While I'm in agreement with #Jaybit that you probably need to ditch the text box and use a better input, the answer to this specific question lies in some crafty string parsing. Whenever you are doing string parsing, RegEx is your friend. Web developers end up having to do this crap all the time. This example is in JavaScript, but the RegEx ought to be portable enough that it works in ObjC:
http://www.codingoptimist.com/2009/07/using-javascript-and-regex-to-parse.html
You can do this with RegExKit or NSRegularExpression