NSDateFormatter fails with string that seems correct - objective-c

Given the following code:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss"];
NSLog(#"Date from String: %#", [formatter dateFromString:#"2013-09-08T00:36:40"]);
The log produces: Date from String: (null)
For other strings from the same source it works, but i'm failing to see why in this case fails.
Could someone enlighten me?
Update: I have updated the code as follows, still fails:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss"];
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:#"en_US_POSIX"];
[formatter setLocale:locale];
NSDate *date = nil;
NSError *error = nil;
[formatter getObjectValue:&date
forString:#"2013-09-08T00:36:40"
range:NULL
error:&error];
NSLog(#"Date from String: %#", date);
NSLog(#"Error: %#", error);
The output:
2014-03-31 15:27:01.697 Untitled[30859:507] Date from String: (null)
2014-03-31 15:27:01.710 Untitled[30859:507] Error: Error Domain=NSCocoaErrorDomain Code=2048 "The value “2013-09-08T00:36:40” is invalid." UserInfo=0x7f89d8e04cd0 {NSInvalidValue=2013-09-08T00:36:40}
Update 2: After some playing around I found out my timezone is the reason it fails (I don't understand why, but it only fails under my timezone)
The following code fails:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss"];
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:#"en_US_POSIX"];
[formatter setLocale:locale];
[formatter setTimeZone:[NSTimeZone timeZoneWithName:#"America/Santiago"]];
NSDate *date = nil;
NSError *error = nil;
[formatter getObjectValue:&date
forString:#"2013-09-08T00:36:40"
range:NULL
error:&error];
NSLog(#"Date from String: %#", date);
NSLog(#"Error: %#", error);
Any suggestions to work around it?

The error was caused because that date doesn't exist under the Chilean zone (due to DST change).
While NSDateFormatter is correct to return nil because it doesn't exist. It's not feasible to tell the user "The file was created in a time your timezone never experienced".
The solution (that applies to any other zone with DST) is to set a timezone just like the current one without DST.
NSString *dateString = ...; //Assume it is defined
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss"];
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:#"en_US_POSIX"];
[formatter setLocale:locale];
//Date formatting might fail because of Daylight Saving Timezones and the like. So, we force the timezone to one just like it where the date always exist.
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:[[NSTimeZone localTimeZone] secondsFromGMT]]];
NSDate *date = nil;
NSError *error = nil;
[formatter getObjectValue:&date
forString:dateString
range:NULL
error:&error];
Again, this is not technically correct to do, but it is OK to do for display purposes.

According to http://www.timeanddate.com/time/zone/chile/santiago, the Daylight Savings Time
for the time zone "America/Santiago" started in the year 2013 on Sun, 8 Sep, 00:00, which means that the clocks were adjusted one hour forward to Sun, 8 Sep, 01:00.
Therefore the time "00:36:40" does not exist on that day. That is the reason that
the conversion fails.
A possible solution could be to fill a NSDateComponents with the given components
and then call dateFromComponents:
NSCalendar *cal = [NSCalendar currentCalendar];
[cal setTimeZone:[NSTimeZone timeZoneWithName:#"America/Santiago"]];
NSDateComponents *comp = [[NSDateComponents alloc] init];
comp.year = 2013;
comp.month = 9;
comp.day = 8;
comp.hour = 0;
comp.minute = 36;
comp.second = 40;
NSDate *d = [cal dateFromComponents:comp];
NSLog(#"Date from components: %#", d); // 2013-09-08 04:36:40 +0000
NSString *s = [formatter stringFromDate:d];
NSLog(#"String from date: %#", s); // 2013-09-08T01:36:40
This gives the same date for all "valid" date strings, and also a sensible result
for the "invalid" date string during the daylight saving time transition.

Related

JSON date to NSDate Issue

I have a date string in JSON format, it looks like this:
2013-05-22T10:54:40.437
And I'm trying to convert it to NSDate.
- (NSString *)dateFromString:(NSString *)datestring{
// Convert string to date object
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ss"];
NSDate *date = [dateFormat dateFromString:datestring];
//date is nil here.
NSDateFormatter *newDateFormatter = [[NSDateFormatter alloc]init];
[newDateFormatter setDateFormat:#"MM/dd/yyyy"];
NSString *newString = [newDateFormatter stringFromDate:date];
NSLog(#"Date -- %#",datestring);
return newString;
}
But date is nil after dateFromString. Does anyone know what I did wrong here?
Thanks!
Use this date format:
yyyy-MM-dd'T'HH:mm:ss.SSS
Your code with little modification:
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS"];
NSDate *date = [dateFormat dateFromString:datestring];
NSDateFormatter *newDateFormatter = [[NSDateFormatter alloc]init];
[newDateFormatter setDateFormat:#"MM/dd/yyyy"];
NSString *newString = [newDateFormatter stringFromDate:date];
NSLog(#"Date: %#, formatted date: %#", date, newString);
gives me this output:
Date: 2013-05-22 08:54:40 +0000, formatted date: 05/22/2013
Edit: how "ikinci viking" pointed out in comment, escaping the dashes is unnecessary. Escaping removed from the code
Update for iOS 11+
ISO8601DateFormatter is the recommended way to parse internet date on iOS 10+.
However it has few issues preventing it from usage with the specific format from the question:
Milliseconds are supported only on iOS 11+
It does not work for dates without timezone (the case in question). Date string must contain timezone part (e.g. +00:00 or Z for "Zulu time" UTC)
If date strings contains both milliseconds and timezone (e.g. 2018-09-07T14:04:13.143Z), it can be used as follows (Swift example):
let isoDateFormatter = ISO8601DateFormatter()
isoDateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
Your date format is incorrect. It should be:
[dateFormat setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss:SSS"];
The three capital 'S' means it will take the fractional part to 3 digits. Adjust it as you will.
If your date format is "2014-02-25T13:05:10+00:00" then,
your code should be like this....
NSURL *url = [NSURL URLWithString:#"your URL"];
NSString *str = [[NSString alloc] initWithContentsOfURL:url usedEncoding:Nil error:Nil];
NSDateFormatter *dateFor = [[NSDateFormatter alloc] init];
[dateFor setDateFormat:**#"yyyy-MM-dd'T'HH:mm:ss'+'SS:SS"**];
NSDate *yourDate = [dateFor dateFromString:str];
NSLog(#"Your Date: %#",yourDate);
-(void)viewDidLoad {
self.date = [dateForRFC3339DateTimeString:#"2015-01-18T01:00:00.000Z"];
}
-(NSDate *)dateForRFC3339DateTimeString:(NSString *)rfc3339DateTimeString {
NSDateFormatter *rfc3339DateFormatter = [[NSDateFormatter alloc] init];
[rfc3339DateFormatter setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'"];
[rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
// Convert the RFC 3339 date time string to a NSDate.
NSDate *result = [rfc3339DateFormatter dateFromString:rfc3339DateTimeString];
return result;
}

Getting correct NSDate value - is this the most efficient method?

I have this code to get the local date/time. It works but it seems a long way around the bush (10 statements) to get my current date/time value rather than the GMT time.
NSDate *currentDateGMT = [NSDate date];
NSDateFormatter *currentDateDateFormatter = [[NSDateFormatter alloc] init];
NSTimeZone *currentDateTimeZoneGMT = [NSTimeZone timeZoneWithName:#"GMT"];
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
[currentDateDateFormatter setTimeZone: currentDateTimeZoneGMT];
[currentDateDateFormatter setDateFormat:#"yyyy-MM-dd HH:mm"];
[currentDateDateFormatter setLocale:locale];
NSString *currentDateString = [currentDateDateFormatter stringFromDate:currentDateGMT];
NSDate *currentDateAdjusted = [currentDateDateFormatter dateFromString:currentDateString];
[currentDateDateFormatter release];
Can someone confirm that this is the best way to obtain the current machine value?
Thanks
NSDateFormatter will default to the users time zone so the simplest solution is
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
//lower case h for hour will also default to the users
//12/24 hour clock preference
[formatter setDateFormat:#"yyyy-MM-dd hh:mm"];
NSString *currentDate = [formatter stringFromDate:[NSDate date]];
[formatter release];

Trouble printing NSDate returned from formatter

I want to see what is getting stored in an NSDate, so I am using NSLog, but it's showing (null), whereas if I print the string stf2, it's showing the proper value.
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"YYYY-MM-dd"];
NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MMM dd, yyyy"];
NSString *stf2 = [[pact.date componentsSeparatedByString:#" "] objectAtIndex:0];
NSLog(#"date %#",stf2);
NSDate *date_ = [formatter dateFromString:stf2];
pact.date = [formatter1 stringFromDate:date_];
NSLog(#"date %#",[NSDate date_]);
There are two specific problems in the code you've presented in the question.
Format Reset
First you do,
[formatter setDateFormat:#"YYYY-MM-dd"];
and then you initialize the second formatter followed by resetting the first formatter's format,
NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MMM dd, yyyy"];
To emphasize
[formatter setDateFormat:#"MMM dd, yyyy"];
This should've been formatter1 but is formatter.
Date Format
If you look at the format you've use YYYY-MM-dd, it looks fine. But apparent YYYY have a different purpose and can be different from our usual calendar year. You should use the lowercase y instead.
[formatter setDateFormat:#"yyyy-MM-dd"];
And I don't think you meant this but
NSLog(#"date %#",[NSDate date_]);
should be
NSLog(#"date %#", date_);
you need to correct the dateformatter by setting proper date formatter. first do this
[formatter setDateFormat:#"yyyy-dd-MM"];
//it should be in the way as your string is. like if your string is 2011-Jun- 27 then fromatter should be
[formatter setDateFormat:#"yyyy-MM-dd"];
set the formatter as per your string's date format. then get the date back from this line
NSDate *date_ = [formatter dateFromString:stf2];
Assuming "stf2" is your string, then perhaps your object formatter is nil.
Below functions will be helpful to you.
"getDateTimeFromString" will take date and time as argument and it will return NSDate object.\
-(NSDate *)getDateTimeFromString :(NSString *)tempDate :(NSString *)tempTime{
NSString *dateValue = [tempDate stringByAppendingFormat:#" %#",tempTime];
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm"];
NSDate *date = [[NSDate alloc] init];
date = [dateFormatter dateFromString:dateValue];
NSTimeZone* sourceTimeZone = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
NSTimeZone* destinationTimeZone = [NSTimeZone systemTimeZone];
NSInteger sourceGMTOffset = [sourceTimeZone secondsFromGMTForDate:date];
NSInteger destinationGMTOffset = [destinationTimeZone secondsFromGMTForDate:date];
NSTimeInterval interval = destinationGMTOffset - sourceGMTOffset;
NSDate* destinationDate = [[[NSDate alloc] initWithTimeInterval:interval sinceDate:date] autorelease];
return date;
}
"getDateStringFromDate" will take NSDate as argument and it will return NSString.
So, you can NSLog that value.
-(NSString *)getDateStringFromDate :(NSDate *)dateValue{
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd"];
NSDateFormatter *timeFormat = [[NSDateFormatter alloc] init];
[timeFormat setTimeStyle:NSDateFormatterShortStyle];
[timeFormat setDateFormat:#"HH:mm a"];
NSString *theDate = [dateFormat stringFromDate:dateValue];
/*NSLog(#"\n"
"theDate: |%#| \n"
"theTime: |%#| \n"
, theDate, theTime);*/
return theDate;
}
Hope you will get the answer.

problem with setDateFormat for my iPad app

I use the following to get the date.
NSDate *today =[NSDate date];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
//manage day of week and date.
[formatter setDateFormat:#"EEE"];
dayOfWeek = [formatter stringFromDate:today];
[formatter setDateFormat:#"MMM"];
theDate = [formatter stringFromDate:today];
// manage the time and am/pm
[formatter setDateFormat:#"h:mm"];
theTime = [formatter stringFromDate:today];
[formatter setDateFormat:#"a"];
amPM = [[formatter stringFromDate:today] retain];
[formatter release];
My app crashes if I use [formatter setDateFormat:#"d MMM"]. Do I need to convert the integer to string ? Is so please correct my code on how to get the current date
This should do the trick:
[formatter setDateFormat:#"d MMM"];
NSString *theDate = [formatter stringFromDate:today];
setDateFormat doesn't return anything, stringFromDate returns what you are looking for.
And don't retain like you did in this line:
amPM = [[formatter stringFromDate:today] retain];
The formatter has already a retain count of 1 by using alloc.

Why does my NSDateFormatter return nil when parsing?

my code is like this
NSString *tempDate = [NSString stringWithString:tempReviewData.pubDate];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterLongStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
[dateFormatter setDateFormat:#"HH:mm a"];
NSDate *newDate = [dateFormatter dateFromString:tempReviewData.pubDate];
My newDate is getting nil at this point i dont know why
It seems to work for me but it depends on the format of tempReviewData.pubDate.
When I use invalid format, like #"6:30 M", I get null as well.
This is working:
NSString *tempDate = [NSString stringWithString:#"6:30 PM"];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterLongStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
[dateFormatter setDateFormat:#"HH:mm a"];
NSDate * newDate = [dateFormatter dateFromString:tempDate];
NSString * str = [dateFormatter stringFromDate:[NSDate date]];
NSLog(#"date: %#", newDate);
NSLog(#"str: %#", str);
Output:
2010-03-08 22:36:57.904 x[4340:903] date: 1970-01-01 12:30:00 +1000
2010-03-08 22:36:57.905 x[4340:903] str: 22:36 PM
NSDate *newDate = [dateFormatter dateFromString:tempReviewData.pubDate];
Does pubDate return an NSString, or an NSDate?
If it returns a string, then you should rename that property to clearly indicate that.
If it returns a date (NSDate), then trying to parse it as a string will not work, since it is not a string; moreover, you can cut out all this formatter code, since you already have the date object you're after.
It seems the NSDateFormatter has gotten very picky.
-(void)dateFormatterTests {
NSDateFormatter *formatter;
formatter = [[NSDateFormatter alloc] init];
#ifdef WORKS
[formatter setDateFormat:#"yyyy-MM-dd"];
#elif defined(ALSO_WORKS)
[formatter setDateFormat:#"yyyy MM dd"];
[formatter setLenient:YES];
#else // DOESN'T WORK
[formatter setDateFormat:#"yyyy MM dd"];
#endif
// Works per comments above
NSLog(#"dFS: %#", [formatter dateFromString:#"2010-01-13"]);
// Never works with any of the above formats
NSLog(#"dFS: %#", [formatter dateFromString:#"2010-01-13 22:00"]);
[formatter release]; formatter = nil;
}