How to convert a UTC date to NSDate? - iphone

I have a string that is UTC and would like to convert it to an NSDate.
static NSDateFormatter* _twitter_dateFormatter;
[_twitter_dateFormatter setFormatterBehavior:NSDateFormatterBehaviorDefault];
[_twitter_dateFormatter setDateFormat:#"EEE MMM dd HH:mm:ss ZZZ yyyy"];
[_twitter_dateFormatter setLocale:_en_us_locale];
NSDate *d = [_twitter_dateFormatter dateFromString:sDate];
When I go through the debugger d is nil even though sDate is "2010-03-24T02:35:57Z"
Not exactly sure what I'm doing wrong.

Ditto on the need to alloc/init your NSDateFormatter object, but you have another problem too: your Date format string does not match the actual date you're giving it.
"EEE MMM dd HH:mm:ss ZZZ yyyy"
- vs -
"2010-03-24T02:35:57Z"
That format string would match something like:
"Wed Mar 24 00:07:33 -0400 2010"
See the unicode standard for the meaning of the date format string.

You aren't allocating or initialising your NSDateFormatter object.
Try something like this:
NSDateFormatter* _twitter_dateFormatter = [[NSDateFormatter alloc] init];
[_twitter_dateFormatter setFormatterBehavior:NSDateFormatterBehaviorDefault];
[_twitter_dateFormatter setDateFormat:#"EEE MMM dd HH:mm:ss ZZZ yyyy"];
[_twitter_dateFormatter setLocale:_en_us_locale];
NSDate *d = [_twitter_dateFormatter dateFromString:sDate];
[_twitter_dateFormatter release];
It's also unclear why you are declaring _twitter_dateFormatter as a static. If you are trying to avoid re-allocating it, make it an ivar of your class.

Apple gives source code for Parsing an RFC 3339 date-time. If using ARC, you'll may want to adjust that source by removing the calls to 'autorelease'.
FYI, RFC 3339 is a subset of ISO 8601 with one deviation (negative zero offset is allowed in RFC 3339). So Apple’s code is killing two birds with one stone, handling the so-called 'Internet-style' RFC 3339 datetimes as well as simple datetimes of ISO 8601.

Related

Writing a proper timestamp for string date

I'm trying to obtain NSDate from the given string:
NSString* dateString = #"March 23 04:00 AM";
NSDateFormatter* firstDateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[firstDateFormatter setDateFormat:#"MMММ dd h:mm a"];
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
[firstDateFormatter setLocale:locale];
NSDate* date = [firstDateFormatter dateFromString:dateString];
NSLog(#"date:%#", date);
The date I am getting is null where I've made a mistake?
There is an issue with the date format you set. According to apple
The format string uses the format patterns from the Unicode Technical
Standard #35
In your case you need to use stand-alone version of month that is LLLL not MMMM.
If you change this line, your code will work just fine
[firstDateFormatter setDateFormat:#"LLLL dd hh:mm a"];
The standard explains the standalone version as follow:
The most important distinction to make between format and stand-alone
forms is a grammatical distinction, for languages that require it. For
example, many languages require that a month name without an
associated day number be in the basic nominative form, while a month
name with an associated day number should be in a different
grammatical form: genitive, partitive, etc. Another common type of
distinction between format and stand-alone involves capitalization;
however, this can be controlled separately and more precisely using
the element as described in Section 5.19
ContextTransform Elements.
Btw. Read this it contains a lot of info along with better ways of initializing the dateformatter.
Use hh for two-digit hours:
[firstDateFormatter setDateFormat:#"MMММ dd hh:mm a"];

NSDateFormat buggy?

I have the following code:
NSDateFormatter * df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"yyyy-MM-dd hh:mm:ss"];
date1 = [df dateFromString:#"2010-08-12 08:00:00"];
works as a charm, however when it is above 12:00:00, such as 23:00:00 the returned NSDate is null.
Am I being stupid?
use HH instead of hh to represent 24-hr format
[df setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
You need to use a capital HH for military time, hh is for AM/PM
Change your hh to HH.
I assume the format string is not correct for your format.
Looking at the strings that can be supplied to setDateFormat, the Apple doc points to spec http://unicode.org/reports/tr35/tr35-10.html#Date_Format_Patterns.
hour
h 1..2 11 Hour [1-12].
H 1..2 13 Hour [0-23].

NSDate from NSString when time is in 24 hr format

I have a string of the format Jan 25, 2011 3:17 AM. I need to convert it to NSDate.
I used NSDateFormatter with format #"MMM d, yyyy h:mm a". It Works well if iphone time is in 12 hr format, but returns nil if time is in 24 hr format. Can any one help me with this????
Capital H is used for 24 hour format. Don't 24 hour times usually exclude the AM/PM part? If so, your format string should be: #"MMM d yyyy H:mm".
Here's a reference for Unicode date format strings.
It's a bug in NSDateFormatter. You can work around it by manually setting a locale on the date formatter:
[dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:#"en_GB"] autorelease]];

What's wrong with how I'm using NSDateFormatter?

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.locale = [[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"] autorelease];
[dateFormatter setDateFormat:#"EEE, d MMM yyyy HH:mm:ss zzz"];
NSString *dateString = #"Tue, 08 Jun 2010 17:00:00 EDT";
NSDate *eventDate = [dateFormatter dateFromString:dateString];
In this case the eventDate object is nil. Can somebody clue me in? This code used to work.
UPDATE:
Can't talk about why this doesn't work due to NDA. Suffice it to say, when iOS 4 is out I will post the answer to my own question.
/*
x number
xx two digit number
xxx abbreviated name
xxxx full name
a AM/PM
A millisecond of day
c day of week (c,cc,ccc,cccc)
d day of month
e day of week (e,EEE,EEEE)
F week of month
g julian day (since 1/1/4713 BC)
G era designator (G=GGG,GGGG)
h hour (1-12, zero padded)
H hour (0-23, zero padded)
L month of year (L,LL,LLL,LLLL)
m minute of hour (0-59, zero padded)
M month of year (M,MM,MMM,MMMM)
Q quarter of year (Q,QQ,QQQ,QQQQ)
s seconds of minute (0-59, zero padded)
S fraction of second
u zero padded year
v general timezone (v=vvv,vvvv)
w week of year (0-53, zero padded)
y year (y,yy,yyyy)
z specific timezone (z=zzz,zzzz)
Z timezone offset +0000
sql y-M-d H:m:s
rss [E, ]d MMM y[y] H:m:s Z|z[zzz]
*/
This is my comment for date parsing. I use the following, where toDateUsingFormat uses an NSDateFormatter with the passed in string. I do not use a locale, because rss dates are not localized.
if ( 0 == [string rangeOfString:#","].length ) {
result = [string toDateUsingFormat:#"d MMM y H:m:s z"];
} else {
result = [string toDateUsingFormat:#"E, d MMM y H:m:s z"];
}
Edit:
I use getObjectValue: instead of dateFromString.
NSDate *result = nil;
NSError *error = nil;
[dataFormatter getObjectValue:&result forString:dateString errorDescription:&error];
The answer to this question is the following:
I was using the wrong date format string:
#"EEE, d MMM yyyy HH:mm:ss zzz"
when it should have been:
#"EEE, dd MMM y HH:mm:ss zzz"
The part about iOS 4 and NDA was that I thought I had to use the NSDateFormatter method dateFormatFromTemplate:options:locale: which would have looked like this:
NSString *format = [NSDateFormatter dateFormatFromTemplate:#"EEE, d MMM yyyy HH:mm:ss zzz" options:0 locale:[NSLocale currentLocale]];
However, that method should only be used when you want to DISPLAY the date to a user of unknown locale. In my case, I knew exactly what the date format was going to look like and I was trying to PARSE the date string so that I could store it in CoreData. Therefore, that method wasn't useful.
Bonus bookmark: Read this table very carefully and you will definitely figure out what the problem is... Unicode date formats should follow these specifications: http://unicode.org/reports/tr35/tr35-6.html#Date_Field_Symbol_Table
TL;DR The format string was wrong. D'oh!
The full list of format specifiers is UTS#35 Date Format Patterns.
Does the 'c' character work in place of 'E'? The document has it as a very close alternative and it may produce the result you want.
(If you really want characters in the format string that are not in the table you can escape them, like hh 'o''clock' a, zzzz - produces format like "12 o'clock PM, Pacific Daylight Time".)
The problem that I found is that the string that I was parsing has some trailing characters "\n\t\t". The solution was to remove them:
[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
you have a zero padded day, namely 08 in your date string, however in your format string the format is trying to parse a non-zero padded day, namely d. changing d to dd should fix the problem

my NSDateFormatter works only in the iPhone simulator

I use a NSDateFormatter which works fine in the simulator, but I get a nil when I run it in the iPhone. I hardcoded the date to be sure of the format, but it fails anyway.
NSString *strPubDate = #"Fri, 8 May 2009 08:08:35 GMT";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[dateFormatter setDateFormat:#"EEE, d MMM yyyy HH:mm:ss Z"];
NSDate *myDate = [dateFormatter dateFromString:strPubDate];
I tried with different region settings, languages etc. on the iPhone. Any idea what is going wrong?
This code seems to work correctly against the "GMT" tag.
NSString *strPubDate = #"Fri, 8 May 2009 08:08:35 GMT";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"EEE, d MMM yyyy HH:mm:ss zzzz"];
NSDate *myDate = [dateFormatter dateFromString:strPubDate];
RFC822 Date and Time specs use a few "zone" tags and I found the correct symbol to parse the "GMT" tag Format String for the iPhone NSDateFormatter
z~zzz: (Specific GMT Timezone Abbreviation)
zzzz: (Specific GMT Timezone Name)
Z: +0000 (RFC 822 Timezone
Thanks for all your help!
If you need to use a specific date format you might want to parse it "by hand" rather than using NSDateFormatter. Its behaviour does change depending on the locale, etc. and there are some bugs particularly when you have a timezone in your string.
Having said that, one option in finding what the problem is might be to use the getObjectValue:forString:range:error: method instead of dateFromString:. This way you get an NSError object that (in theory) would tell you what the problem is.
BTW, you don't need the NSDateFormatterBehavior10_4 line. iPhone OS only supports the 10.4+ options, though you won't get any errors if you use the "old" style in the Simulator.
I had also this problem recently (iOS5).
I needed to set also the locale of the NSDateFormatter to work on the device.
Without this, [dateFormatServer dateFromString:dateServer] was returning null.
NSString *dateServer = #"Fri, 8 May 2009 08:08:35 GMT";
NSDateFormatter *dateFormatServer = [[NSDateFormatter alloc] init];
[dateFormatServer setDateFormat:#"EEE, d MMM yyyy HH:mm:ss zzzz"];
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_GB"];
[dateFormatServer setLocale:locale];
[locale release];
NSDate * date = [dateFormatServer dateFromString:dateServer];
Nothing stands out, but to help debug this you could try feeding NSDateFormatter a date object and seeing if the resulting string has any minor differences from the one you're trying to parse.
When I've parsed a date string with "GMT" at the end, I've used the "zzz" format, not "Z".
I second Manuel Spuhler's advice of manually parsing - not my favorite option, but Objective-C's options for that are way too complicated (and lacking in error reporting - anything wrong just spits nil, without a hint on the error).
One thing that worked for me is to use C's strptime to decouple the date, then reconstruct it as a NSDate object. For example, the code below tkes a string received as something like "Monday, 28-Sep-09 18:13:50 UTC" and converts it to a NSDate object adapting the UTC time for the local time:
struct tm time;
strptime([currentStringValue UTF8String], "%A, %d-%b-%y %H:%M:%S %z", &time);
NSDate* myDate = [NSDate dateWithString:
[NSString stringWithFormat:#"%04d-%02d-%02d %02d:%02d:%02d +0000",
time.tm_year+1900, time.tm_mon+1, time.tm_mday,
time.tm_hour, time.tm_min, time.tm_sec]
];
(could handle other zones by adding other struct tm parameters instead of the +0000 fixed time zone, see time.h entry on wikipedia for details):
strptime rescued me aswell.
struct tm time;
if (strptime([modDateString UTF8String], "%A, %d %b %Y %H:%M:%S", &time)) {
NSString *rDate=[NSString stringWithFormat:#"%04d-%02d-%02d %02d:%02d:%02d +0000",
time.tm_year+1900, time.tm_mon+1, time.tm_mday,
time.tm_hour, time.tm_min, time.tm_sec];
This code works with HTTP dates like Thu, 15 Apr 2010 13:53:56 GMT and Fri, 8 May 2009 08:08:35 GMT which was the original question