46

If I use the following code:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];   
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm"];
NSDate *myDate = [dateFormatter dateFromString:@"2010-01-28T15:22:23.863"];
NSLog(@"%@", [dateFormatter stringFromDate:myDate]);

It is successfully converted to a Date object, however, I cannot seem to format it any other way than yyyy-MM-dd'T'HH:mm, i.e. what gets logged is 2010-01-28T15:22:23

If I change the dateFormat to say [dateFormatter setDateFormat:@"yyyy-MMMM-d'T'HH:mm"]; the Date object is null...

So my ultimate question is how to format an ISO8601 timestamp from a SQL database to use, for instance, NSDateFormatterMediumStyle to return "January 1, 2010"?

rson
  • 1,455
  • 2
  • 24
  • 43

8 Answers8

42

I have a similiar but slightly more complex problem, and I've found a very simple solution!

The problem: My incoming ISO8601 dates look like this: 2006-06-14T11:06:00+02:00 They have a timezone offset at the end.

The solution: Use Peter Hosey's ISO8601DateFormatter which you can download from here.

ISO8601DateFormatter *formatter = [[ISO8601DateFormatter alloc] init];
NSDate *theDate = [formatter dateFromString:dateString];
[formatter release], formatter = nil;

and

ISO8601DateFormatter *formatter = [[ISO8601DateFormatter alloc] init];
NSString *dateString = [formatter stringFromDate:[twitch dateTwitched]];
[formatter release], formatter = nil;
Matthew
  • 913
  • 1
  • 8
  • 19
  • 3
    ISO8601DateFormatter works well, but it is extremely slow. In my tests, parsing that took NSDateFormatter 300ms took ISO8601DateFormatter 7400ms. – Sam Soffes Feb 22 '11 at 23:18
  • @SamSoffes 7 seconds? It shouldn't take anywhere near that long for a single date. Can you provide more details, please? (Preferably in a bug report: https://bitbucket.org/boredzo/iso-8601-parser-unparser ) – Peter Hosey Oct 15 '11 at 01:58
  • Seems to work. How do I edit the format of the output string? I did not find any example or documentation on that. :( – Redfox Jan 18 '12 at 14:29
  • @PeterHosey - any chance you'll refactor a version compatible with the latest iOS SDK (i.e. take out the reference counting)? I also am having a hard time figuring out how to add the DateFormatter to the object mapping. – Kyle Clegg Jul 31 '12 at 20:51
  • @Kyle: There is a very strong chance of that; I ♥ ARC. What do you mean by “add the DateFormatter to the object mapping”? – Peter Hosey Jul 31 '12 at 23:00
  • @Redfox: Set the formatter's `format`, `includeTime`, and `timeSeparator` properties. – Peter Hosey Jul 31 '12 at 23:02
  • @PeterHosey The server backend on my current project uses ISO8601 date format. I want to use your class to create a ISO8601 date formatter and add that to my object mapping, so anytime I pull down an object with a date it knows how to handle the format. Basically I'm looking for a solution to this problem. https://groups.google.com/forum/#!msg/restkit/Bf-qRiYgmIE/hDCXVt9DflYJ – Kyle Clegg Aug 01 '12 at 04:35
  • @PeterHosey Found what I needed by (1) upgrading from restkit 0.9.3 to 0.10.1 which includes your ISO8601 parser, and (2) refering to this documentation: https://github.com/RestKit/RestKit/wiki/Configuring-Date-and-Time-Mapping. Thanks! – Kyle Clegg Aug 01 '12 at 16:00
  • @PeterHosey Any chance you'll put the code in GitHub? It's much easier to keep track of versions and bugs. Thanks! – pixelfreak Sep 23 '12 at 01:33
  • 4
    @pixelfreak: The chance is 100%. I made the move last month. https://github.com/boredzo/iso-8601-date-formatter – Peter Hosey Sep 23 '12 at 18:43
  • @PeterHosey Does it support Dates and Times? Or just Dates? Do you have any plans to add time support? – GangstaGraham Apr 27 '13 at 21:12
  • 3
    @GangstaGraham: You can turn on a formatter's `includeTime` property. – Peter Hosey Apr 27 '13 at 21:27
  • @PeterHosey Oh ok, thanks, I am new to iOS so I didn't know that. I had downloaded it initially after hearing such great reviews from people. Time to redownload it! Thank you. – GangstaGraham Apr 27 '13 at 21:32
  • @GangstaGraham: It's an ISO 8601 Date Formatter feature, not an iOS feature. (NSDateFormatters have `timeStyle`, which lets you get a lot more specific about how you want the time represented. ISO 8601 Date Formatter does not give you such control, since it has a standard to follow.) – Peter Hosey Apr 27 '13 at 22:20
  • @PeterHosey I have another ISO8601 question regarding using your dateFromString:timeZone method from an ARC project here:http://stackoverflow.com/questions/16492931/using-methods-from-non-arc-libraries-in-an-arc-project I would appreciate it if you could answer this. Thank you very much, and thanks for the wonderful library! – GangstaGraham May 11 '13 at 01:43
  • @PeterHosey Actually don't bother, I have got some help from others! :) Thanks once again for an amazing date formatter! – GangstaGraham May 11 '13 at 01:45
  • @SamSoffes I've noticed the alloc/init on an NSDateFormatter object is the slowest part (I think it's doing disk I/O looking in some locale files). In my experience, caching oft used NSDateFormatters (like iso 8601 formatters) can really speed up performance if you're parsing a lot of dates. – Doug Richardson Aug 23 '13 at 22:01
  • @PeterHosey found ISO8601DateFormatter by looking at this post. Works great and fast. Thanks for sharing – fontno Dec 20 '13 at 01:30
19

Here is sample code from apple

    NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
    NSDate *date = [formatter dateFromString:dateString];

link:https://developer.apple.com/library/ios/#qa/qa1480/_index.html

danielbeard
  • 9,120
  • 3
  • 44
  • 58
david
  • 391
  • 3
  • 11
  • 1
    Don't forget to set the POSIX locale as mentioned in that Apple article; otherwise, you could end up with non-conforming dates that include "AM" or "PM", if the user has an odd combination of settings (e.g. UK locale with 12-hour time enabled). – Joe Trellick Apr 09 '13 at 11:22
  • 1
    If milliseconds needed, use @"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'" – HotJard Jun 14 '13 at 09:49
  • 1
    @JoeHughes How do you set POSIX locale? – jjxtra Jun 12 '14 at 03:05
19

You need another formatter to handle the output. Put this after your code:

NSDateFormatter *anotherDateFormatter = [[NSDateFormatter alloc] init];   
[anotherDateFormatter setDateStyle:NSDateFormatterLongStyle];
[anotherDateFormatter setTimeStyle:NSDateFormatterShortStyle];
NSLog(@"%@", [anotherDateFormatter stringFromDate:myDate]);
osteven
  • 766
  • 7
  • 4
12

Just to mention that since iOS 6.0 you can use this format:

 yyyy-MM-dd'T'HH:mm:ssZZZZZ
Marcin
  • 3,694
  • 5
  • 32
  • 52
  • 1
    Thanks for sharing with us, just what I needed! Dates like 2013-06-28T15:20:55+02:00 --> yyyy-MM-dd'T'HH:mm:ssZZZZZ – mmvie Jun 28 '13 at 13:37
  • 1
    Unfortunately this doesn't parse times with UTC time zone designated by a single 'Z' instead of '+hh:mm'. Or does it? – macbirdie Aug 22 '13 at 17:52
  • @macbirdie I don't think it should, that's how it's defined http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns – Marcin Sep 06 '13 at 10:15
  • 3
    @macbirdie I can confirm that NSDateFormatter does correctly parse dates designated by a single 'Z'. The test string "2010-01-28T15:22:23Z" works with the date format "yyyy-MMMM-d'T'HH:mm:ssZZZZZ". My test platform is iOS7. The 'Z' format is special-cased in the Unicode standard. – kevinlawler Oct 05 '13 at 02:09
  • Thanks a lot! There is update_date on Facebook. It's like "2014-08-12T16:13:04+0000" – CHiP-love-NY Jul 21 '15 at 17:06
6

I have a very fast C implementation of parsing ISO8601 NSStrings to NSDates in SAMCategories. You can install it with CocoaPods or just copy the files to your project.

Here's how to use it:

[NSDate sam_dateFromISO8601String:@"2013-08-24T06:51:21-07:00"]

It's very robust and supports missing timezones, etc. Like my comment above said, NSDateFormatter was very slow for me when parsing lots of dates. Switching to this implementation helped an immense amount.

This code is used in 6 production apps (including Hipstamatic and Roon) that I've worked on with no known issues. There is a test suite to prevent regression.

In short, I think this is the best way to parse ISO8601 strings in Objective-C.


A short aside, if you're concerned about performance and transfer, sending integers instead of ISO8601 strings is greatly preferred. You can simply convert them into dates with [NSDate dateWithTimeIntervalSince1970:yourInteger]. Performance is as fast as you can make it and there are less characters to transfer over the wire.

Community
  • 1
  • 1
Sam Soffes
  • 14,831
  • 9
  • 76
  • 80
5

I should note that last time I checked Apples NSDate methods didn't support ISO8601. I still have a bug with Apple about putting official support in.

+ (id)dateWithNaturalLanguageString:(NSString *)string

will properly (last time I ran it) parse and create an NSDate object from an ISO801 string, though the documentation says you shouldn't use it, it's worked on all ISO8601 dates I've tried so far.

Jakub
  • 13,712
  • 17
  • 82
  • 139
Colin Wheeler
  • 3,343
  • 2
  • 21
  • 23
  • It's true that ` + (id)dateWithNaturalLanguageString:(NSString *)string` will parse successfully unless there are millis on your ISO8601 string. – John Wright Apr 25 '10 at 00:33
  • 1
    I should add that you should never use this now. At the time I used this it was working, but since then i've moved onto using other api's that reliably work with ISO8601. dateWithNaturalLanguageString works with ISO8601, but only by accident, don't rely on this continuing to work in the future. – Colin Wheeler Jun 07 '12 at 16:11
3

@danielbeard answer worked for me. However you need to include the timezone otherwise it converts a UTC date into your current timezone which isn't what you want.

- (NSDate *) dateFromISO8601DateString:(NSString *) dateString {
    NSDateFormatter * dateFormatter = [NSDateFormatter new];
    dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
    [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
    NSDate * date = [dateFormatter dateFromString:dateString];
    return date;
}
gngrwzrd
  • 5,902
  • 4
  • 43
  • 56
2

@Thread captain Try this Format : "yyyy-MM-dd'T'HH:mm:ss.SSS"

Swati
  • 2,870
  • 7
  • 45
  • 87