最近项目中用到的接口是.net写的,传过来的日期格式是 2013-04-10T10:36:48.787359+08:00 , 这么一大串想解析成NSDate格式还真不是一件容易的事,幸好已经有人把转换类写出来了。

转换类

老外写的转换类,地址不记得了,直接上代码

     /*ISO8601DateFormatter.h
     *
     *Created by Peter Hosey on 2009-04-11.
     *Copyright 2009 Peter Hosey. All rights reserved.
     */
    
    #import <Foundation/Foundation.h>
    
    /*This class converts dates to and from ISO 8601 strings. A good introduction to ISO 8601: <http://www.cl.cam.ac.uk/~mgk25/iso-time.html>
     *
     *Parsing can be done strictly, or not. When you parse loosely, leading whitespace is ignored, as is anything after the date.
     *The loose parser will return an NSDate for this string: @" \t\r\n\f\t  2006-03-02!!!"
     *Leading non-whitespace will not be ignored; the string will be rejected, and nil returned. See the README that came with this addition.
     *
     *The strict parser will only accept a string if the date is the entire string. The above string would be rejected immediately, solely on these grounds.
     *Also, the loose parser provides some extensions that the strict parser doesn't.
     *For example, the standard says for "-DDD" (an ordinal date in the implied year) that the logical representation (meaning, hierarchically) would be "--DDD", but because that extra hyphen is "superfluous", it was omitted.
     *The loose parser will accept the extra hyphen; the strict parser will not.
     *A full list of these extensions is in the README file.
     */
    
    /*The format to either expect or produce.
     *Calendar format is YYYY-MM-DD.
     *Ordinal format is YYYY-DDD, where DDD ranges from 1 to 366; for example, 2009-32 is 2009-02-01.
     *Week format is YYYY-Www-D, where ww ranges from 1 to 53 (the 'W' is literal) and D ranges from 1 to 7; for example, 2009-W05-07.
     */
    enum {
        ISO8601DateFormatCalendar,
        ISO8601DateFormatOrdinal,
        ISO8601DateFormatWeek,
    };
    typedef NSUInteger ISO8601DateFormat;
    
    //The default separator for time values. Currently, this is ':'.
    extern unichar ISO8601DefaultTimeSeparatorCharacter;
    
    @interface ISO8601DateFormatter: NSFormatter
    {
        NSString *lastUsedFormatString;
        NSDateFormatter *unparsingFormatter;
        
        NSCalendar *parsingCalendar, *unparsingCalendar;
        
        NSTimeZone *defaultTimeZone;
        ISO8601DateFormat format;
        unichar timeSeparator;
        BOOL includeTime;
        BOOL parsesStrictly;
    }
    
    //Call this if you get a memory warning.
    + (void) purgeGlobalCaches;
    
    @property(nonatomic, retain) NSTimeZone *defaultTimeZone;
    
    #pragma mark Parsing
    
    //As a formatter, this object converts strings to dates.
    
    @property BOOL parsesStrictly;
    
    - (NSDateComponents *) dateComponentsFromString:(NSString *)string;
    - (NSDateComponents *) dateComponentsFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone;
    - (NSDateComponents *) dateComponentsFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone range:(out NSRange *)outRange;
    
    - (NSDate *) dateFromString:(NSString *)string;
    - (NSDate *) dateFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone;
    - (NSDate *) dateFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone range:(out NSRange *)outRange;
    
    #pragma mark Unparsing
    
    @property ISO8601DateFormat format;
    @property BOOL includeTime;
    @property unichar timeSeparator;
    
    - (NSString *) stringFromDate:(NSDate *)date;
    - (NSString *) stringFromDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone;
    
    @end
    


    /*ISO8601DateFormatter.m
     *
     *Created by Peter Hosey on 2009-04-11.
     *Copyright 2009 Peter Hosey. All rights reserved.
     */
    
    #import <Foundation/Foundation.h>
    #import "ISO8601DateFormatter.h"
    
    #ifndef DEFAULT_TIME_SEPARATOR
    #       define DEFAULT_TIME_SEPARATOR ':'
    #endif
    unichar ISO8601DefaultTimeSeparatorCharacter = DEFAULT_TIME_SEPARATOR;
    
    //Unicode date formats.
    #define ISO_CALENDAR_DATE_FORMAT @"yyyy-MM-dd"
    //#define ISO_WEEK_DATE_FORMAT @"YYYY-'W'ww-ee" //Doesn't actually work because NSDateComponents counts the weekday starting at 1.
    #define ISO_ORDINAL_DATE_FORMAT @"yyyy-DDD"
    #define ISO_TIME_FORMAT @"HH:mm:ss"
    #define ISO_TIME_WITH_TIMEZONE_FORMAT  ISO_TIME_FORMAT @"Z"
    //printf formats.
    #define ISO_TIMEZONE_UTC_FORMAT @"Z"
    #define ISO_TIMEZONE_OFFSET_FORMAT @"%+.2d%.2d"
    
    @interface ISO8601DateFormatter(UnparsingPrivate)
    
    - (NSString *) replaceColonsInString:(NSString *)timeFormat withTimeSeparator:(unichar)timeSep;
    
    - (NSString *) stringFromDate:(NSDate *)date formatString:(NSString *)dateFormat timeZone:(NSTimeZone *)timeZone;
    - (NSString *) weekDateStringForDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone;
    
    @end
    
    static NSMutableDictionary *timeZonesByOffset;
    
    @implementation ISO8601DateFormatter
    
    + (void) initialize {
        if (!timeZonesByOffset) {
            timeZonesByOffset = [[NSMutableDictionary alloc] init];
        }
    }
    
    + (void) purgeGlobalCaches {
        NSMutableDictionary *oldCache = timeZonesByOffset;
        timeZonesByOffset = nil;
        [oldCache release];
    }
    
    - (NSCalendar *) makeCalendarWithDesiredConfiguration {
        NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
        calendar.firstWeekday = 2; //Monday
        calendar.timeZone = [NSTimeZone defaultTimeZone];
        return calendar;
    }
    
    - (id) init {
        if ((self = [super init])) {
            parsingCalendar = [[self makeCalendarWithDesiredConfiguration] retain];
            unparsingCalendar = [[self makeCalendarWithDesiredConfiguration] retain];
            
            format = ISO8601DateFormatCalendar;
            timeSeparator = ISO8601DefaultTimeSeparatorCharacter;
            includeTime = NO;
            parsesStrictly = NO;
        }
        return self;
    }
    - (void) dealloc {
        [defaultTimeZone release];
        
        [unparsingFormatter release];
        [lastUsedFormatString release];
        [parsingCalendar release];
        [unparsingCalendar release];
        
        [super dealloc];
    }
    
    @synthesize defaultTimeZone;
    - (void) setDefaultTimeZone:(NSTimeZone *)tz {
        if (defaultTimeZone != tz) {
            [defaultTimeZone release];
            defaultTimeZone = [tz retain];
            
            unparsingCalendar.timeZone = defaultTimeZone;
        }
    }
    
    //The following properties are only here because GCC doesn't like @synthesize in category implementations.
    
    #pragma mark Parsing
    
    @synthesize parsesStrictly;
    
    static NSUInteger read_segment(const unsigned char *str, const unsigned char **next, NSUInteger *out_num_digits);
    static NSUInteger read_segment_4digits(const unsigned char *str, const unsigned char **next, NSUInteger *out_num_digits);
    static NSUInteger read_segment_2digits(const unsigned char *str, const unsigned char **next);
    static double read_double(const unsigned char *str, const unsigned char **next);
    static BOOL is_leap_year(NSUInteger year);
    
    /*Valid ISO 8601 date formats:
     *
     *YYYYMMDD
     *YYYY-MM-DD
     *YYYY-MM
     *YYYY
     *YY //century
     * //Implied century: YY is 00-99
     *  YYMMDD
     *  YY-MM-DD
     * -YYMM
     * -YY-MM
     * -YY
     * //Implied year
     *  --MMDD
     *  --MM-DD
     *  --MM
     * //Implied year and month
     *   ---DD
     * //Ordinal dates: DDD is the number of the day in the year (1-366)
     *YYYYDDD
     *YYYY-DDD
     *  YYDDD
     *  YY-DDD
     *   -DDD
     * //Week-based dates: ww is the number of the week, and d is the number (1-7) of the day in the week
     *yyyyWwwd
     *yyyy-Www-d
     *yyyyWww
     *yyyy-Www
     *yyWwwd
     *yy-Www-d
     *yyWww
     *yy-Www
     * //Year of the implied decade
     *-yWwwd
     *-y-Www-d
     *-yWww
     *-y-Www
     * //Week and day of implied year
     *  -Wwwd
     *  -Www-d
     * //Week only of implied year
     *  -Www
     * //Day only of implied week
     *  -W-d
     */
    
    - (NSDateComponents *) dateComponentsFromString:(NSString *)string {
        return [self dateComponentsFromString:string timeZone:NULL];
    }
    - (NSDateComponents *) dateComponentsFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone {
        return [self dateComponentsFromString:string timeZone:outTimeZone range:NULL];
    }
    - (NSDateComponents *) dateComponentsFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone range:(out NSRange *)outRange {
        NSDate *now = [NSDate date];
        
        NSDateComponents *components = [[[NSDateComponents alloc] init] autorelease];
        NSDateComponents *nowComponents = [parsingCalendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:now];
        
        NSUInteger
        //Date
        year,
        month_or_week = 0U,
        day = 0U,
        //Time
        hour = 0U;
        NSTimeInterval
        minute = 0.0,
        second = 0.0;
        //Time zone
        NSInteger tz_hour = 0;
        NSInteger tz_minute = 0;
        
        enum {
            monthAndDate,
            week,
            dateOnly
        } dateSpecification = monthAndDate;
        
        BOOL strict = self.parsesStrictly;
        unichar timeSep = self.timeSeparator;
        
        if (strict) timeSep = ISO8601DefaultTimeSeparatorCharacter;
        NSAssert(timeSep != '\0', @"Time separator must not be NUL.");
        
        BOOL isValidDate = ([string length] > 0U);
        NSTimeZone *timeZone = nil;
        
        const unsigned char *ch = (const unsigned char *)[string UTF8String];
        
        NSRange range = { 0U, 0U };
        const unsigned char *start_of_date = NULL;
        if (strict && isspace(*ch)) {
            range.location = NSNotFound;
            isValidDate = NO;
        } else {
            //Skip leading whitespace.
            NSUInteger i = 0U;
            for(NSUInteger len = strlen((const char *)ch); i < len; ++i) {
                if (!isspace(ch[i]))
                    break;
            }
            
            range.location = i;
            ch += i;
            start_of_date = ch;
            
            NSUInteger segment;
            NSUInteger num_leading_hyphens = 0U, num_digits = 0U;
            
            if (*ch == 'T') {
                //There is no date here, only a time. Set the date to now; then we'll parse the time.
                isValidDate = isdigit(*++ch);
                
                year = nowComponents.year;
                month_or_week = nowComponents.month;
                day = nowComponents.day;
            } else {
                while(*ch == '-') {
                    ++num_leading_hyphens;
                    ++ch;
                }
                
                segment = read_segment(ch, &ch, &num_digits);
                switch(num_digits) {
                    case 0:
                        if (*ch == 'W') {
                            if ((ch[1] == '-') && isdigit(ch[2]) && ((num_leading_hyphens == 1U) || ((num_leading_hyphens == 2U) && !strict))) {
                                year = nowComponents.year;
                                month_or_week = 1U;
                                ch += 2;
                                goto parseDayAfterWeek;
                            } else if (num_leading_hyphens == 1U) {
                                year = nowComponents.year;
                                goto parseWeekAndDay;
                            } else
                                isValidDate = NO;
                        } else
                            isValidDate = NO;
                        break;
                        
                    case 8: //YYYY MM DD
                        if (num_leading_hyphens > 0U)
                            isValidDate = NO;
                        else {
                            day = segment % 100U;
                            segment /= 100U;
                            month_or_week = segment % 100U;
                            year = segment / 100U;
                        }
                        break;
                        
                    case 6: //YYMMDD (implicit century)
                        if (num_leading_hyphens > 0U)
                            isValidDate = NO;
                        else {
                            day = segment % 100U;
                            segment /= 100U;
                            month_or_week = segment % 100U;
                            year  = nowComponents.year;
                            year -= (year % 100U);
                            year += segment / 100U;
                        }
                        break;
                        
                    case 4:
                        switch(num_leading_hyphens) {
                            case 0: //YYYY
                                year = segment;
                                
                                if (*ch == '-') ++ch;
                                
                                if (!isdigit(*ch)) {
                                    if (*ch == 'W')
                                        goto parseWeekAndDay;
                                    else
                                        month_or_week = day = 1U;
                                } else {
                                    segment = read_segment(ch, &ch, &num_digits);
                                    switch(num_digits) {
                                        case 4: //MMDD
                                            day = segment % 100U;
                                            month_or_week = segment / 100U;
                                            break;
                                            
                                        case 2: //MM
                                            month_or_week = segment;
                                            
                                            if (*ch == '-') ++ch;
                                            if (!isdigit(*ch))
                                                day = 1U;
                                            else
                                                day = read_segment(ch, &ch, NULL);
                                            break;
                                            
                                        case 3: //DDD
                                            day = segment % 1000U;
                                            dateSpecification = dateOnly;
                                            if (strict && (day > (365U + is_leap_year(year))))
                                                isValidDate = NO;
                                            break;
                                            
                                        default:
                                            isValidDate = NO;
                                    }
                                }
                                break;
                                
                            case 1: //YYMM
                                month_or_week = segment % 100U;
                                year = segment / 100U;
                                
                                if (*ch == '-') ++ch;
                                if (!isdigit(*ch))
                                    day = 1U;
                                else
                                    day = read_segment(ch, &ch, NULL);
                                
                                break;
                                
                            case 2: //MMDD
                                day = segment % 100U;
                                month_or_week = segment / 100U;
                                year = nowComponents.year;
                                
                                break;
                                
                            default:
                                isValidDate = NO;
                        } //switch(num_leading_hyphens) (4 digits)
                        break;
                        
                    case 1:
                        if (strict) {
                            //Two digits only - never just one.
                            if (num_leading_hyphens == 1U) {
                                if (*ch == '-') ++ch;
                                if (*++ch == 'W') {
                                    year  = nowComponents.year;
                                    year -= (year % 10U);
                                    year += segment;
                                    goto parseWeekAndDay;
                                } else
                                    isValidDate = NO;
                            } else
                                isValidDate = NO;
                            break;
                        }
                    case 2:
                        switch(num_leading_hyphens) {
                            case 0:
                                if (*ch == '-') {
                                    //Implicit century
                                    year  = nowComponents.year;
                                    year -= (year % 100U);
                                    year += segment;
                                    
                                    if (*++ch == 'W')
                                        goto parseWeekAndDay;
                                    else if (!isdigit(*ch)) {
                                        goto centuryOnly;
                                    } else {
                                        //Get month and/or date.
                                        segment = read_segment_4digits(ch, &ch, &num_digits);
                                        NSLog(@"(%@) parsing month; segment is %lu and ch is %s", string, (unsigned long)segment, ch);
                                        switch(num_digits) {
                                            case 4: //YY-MMDD
                                                day = segment % 100U;
                                                month_or_week = segment / 100U;
                                                break;
                                                
                                            case 1: //YY-M; YY-M-DD (extension)
                                                if (strict) {
                                                    isValidDate = NO;
                                                    break;
                                                }
                                            case 2: //YY-MM; YY-MM-DD
                                                month_or_week = segment;
                                                if (*ch == '-') {
                                                    if (isdigit(*++ch))
                                                        day = read_segment_2digits(ch, &ch);
                                                    else
                                                        day = 1U;
                                                } else
                                                    day = 1U;
                                                break;
                                                
                                            case 3: //Ordinal date.
                                                day = segment;
                                                dateSpecification = dateOnly;
                                                break;
                                        }
                                    }
                                } else if (*ch == 'W') {
                                    year  = nowComponents.year;
                                    year -= (year % 100U);
                                    year += segment;
                                    
                                parseWeekAndDay: //*ch should be 'W' here.
                                    if (!isdigit(*++ch)) {
                                        //Not really a week-based date; just a year followed by '-W'.
                                        if (strict)
                                            isValidDate = NO;
                                        else
                                            month_or_week = day = 1U;
                                    } else {
                                        month_or_week = read_segment_2digits(ch, &ch);
                                        if (*ch == '-') ++ch;
                                    parseDayAfterWeek:
                                        day = isdigit(*ch) ? read_segment_2digits(ch, &ch) : 1U;
                                        dateSpecification = week;
                                    }
                                } else {
                                    //Century only. Assume current year.
                                centuryOnly:
                                    year = segment * 100U + nowComponents.year % 100U;
                                    month_or_week = day = 1U;
                                }
                                break;
                                
                            case 1:; //-YY; -YY-MM (implicit century)
                                NSLog(@"(%@) found %lu digits and one hyphen, so this is either -YY or -YY-MM; segment (year) is %lu", string, (unsigned long)num_digits, (unsigned long)segment);
                                NSUInteger current_year = nowComponents.year;
                                NSUInteger current_century = (current_year % 100U);
                                year = segment + (current_year - current_century);
                                if (num_digits == 1U) //implied decade
                                    year += current_century - (current_year % 10U);
                                
                                if (*ch == '-') {
                                    ++ch;
                                    month_or_week = read_segment_2digits(ch, &ch);
                                    NSLog(@"(%@) month is %lu", string, (unsigned long)month_or_week);
                                }
                                
                                day = 1U;
                                break;
                                
                            case 2: //--MM; --MM-DD
                                year = nowComponents.year;
                                month_or_week = segment;
                                if (*ch == '-') {
                                    ++ch;
                                    day = read_segment_2digits(ch, &ch);
                                }
                                break;
                                
                            case 3: //---DD
                                year = nowComponents.year;
                                month_or_week = nowComponents.month;
                                day = segment;
                                break;
                                
                            default:
                                isValidDate = NO;
                        } //switch(num_leading_hyphens) (2 digits)
                        break;
                        
                    case 7: //YYYY DDD (ordinal date)
                        if (num_leading_hyphens > 0U)
                            isValidDate = NO;
                        else {
                            day = segment % 1000U;
                            year = segment / 1000U;
                            dateSpecification = dateOnly;
                            if (strict && (day > (365U + is_leap_year(year))))
                                isValidDate = NO;
                        }
                        break;
                        
                    case 3: //--DDD (ordinal date, implicit year)
                        //Technically, the standard only allows one hyphen. But it says that two hyphens is the logical implementation, and one was dropped for brevity. So I have chosen to allow the missing hyphen.
                        if ((num_leading_hyphens < 1U) || ((num_leading_hyphens > 2U) && !strict))
                            isValidDate = NO;
                        else {
                            day = segment;
                            year = nowComponents.year;
                            dateSpecification = dateOnly;
                            if (strict && (day > (365U + is_leap_year(year))))
                                isValidDate = NO;
                        }
                        break;
                        
                    default:
                        isValidDate = NO;
                }
            }
            
            if (isValidDate) {
                if (isspace(*ch) || (*ch == 'T')) ++ch;
                
                if (isdigit(*ch)) {
                    hour = read_segment_2digits(ch, &ch);
                    if (*ch == timeSep) {
                        ++ch;
                        if ((timeSep == ',') || (timeSep == '.')) {
                            //We can't do fractional minutes when '.' is the segment separator.
                            //Only allow whole minutes and whole seconds.
                            minute = read_segment_2digits(ch, &ch);
                            if (*ch == timeSep) {
                                ++ch;
                                second = read_segment_2digits(ch, &ch);
                            }
                        } else {
                            //Allow a fractional minute.
                            //If we don't get a fraction, look for a seconds segment.
                            //Otherwise, the fraction of a minute is the seconds.
                            minute = read_double(ch, &ch);
                            second = modf(minute, &minute);
                            if (second > DBL_EPSILON)
                                second *= 60.0; //Convert fraction (e.g. .5) into seconds (e.g. 30).
                            else if (*ch == timeSep) {
                                ++ch;
                                second = read_double(ch, &ch);
                            }
                        }
                    }
                    
                    if (!strict) {
                        if (isspace(*ch)) ++ch;
                    }
                    
                    switch(*ch) {
                        case 'Z':
                            timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
                            break;
                            
                        case '+':
                        case '-':;
                            BOOL negative = (*ch == '-');
                            if (isdigit(*++ch)) {
                                //Read hour offset.
                                segment = *ch - '0';
                                if (isdigit(*++ch)) {
                                    segment *= 10U;
                                    segment += *(ch++) - '0';
                                }
                                tz_hour = (NSInteger)segment;
                                if (negative) tz_hour = -tz_hour;
                                
                                //Optional separator.
                                if (*ch == timeSep) ++ch;
                                
                                if (isdigit(*ch)) {
                                    //Read minute offset.
                                    segment = *ch - '0';
                                    if (isdigit(*++ch)) {
                                        segment *= 10U;
                                        segment += *ch - '0';
                                    }
                                    tz_minute = segment;
                                    if (negative) tz_minute = -tz_minute;
                                }
                                
                                NSInteger timeZoneOffset = (tz_hour * 3600) + (tz_minute * 60);
                                NSNumber *offsetNum = [NSNumber numberWithInteger:timeZoneOffset];
                                timeZone = [timeZonesByOffset objectForKey:offsetNum];
                                if (!timeZone) {
                                    timeZone = [NSTimeZone timeZoneForSecondsFromGMT:timeZoneOffset];
                                    if (timeZone)
                                        [timeZonesByOffset setObject:timeZone forKey:offsetNum];
                                }
                            }
                    }
                }
            }
            
            if (isValidDate) {
                components.year = year;
                components.day = day;
                components.hour = hour;
                components.minute = (NSInteger)minute;
                components.second = (NSInteger)second;
                
                switch(dateSpecification) {
                    case monthAndDate:
                        components.month = month_or_week;
                        break;
                        
                    case week:;
                        //Adapted from <http://personal.ecu.edu/mccartyr/ISOwdALG.txt>.
                        //This works by converting the week date into an ordinal date, then letting the next case handle it.
                        NSUInteger prevYear = year - 1U;
                        NSUInteger YY = prevYear % 100U;
                        NSUInteger C = prevYear - YY;
                        NSUInteger G = YY + YY / 4U;
                        NSUInteger isLeapYear = (((C / 100U) % 4U) * 5U);
                        NSUInteger Jan1Weekday = (isLeapYear + G) % 7U;
                        enum { monday, tuesday, wednesday, thursday/*, friday, saturday, sunday*/ };
                        components.day = ((8U - Jan1Weekday) + (7U * (Jan1Weekday > thursday))) + (day - 1U) + (7U * (month_or_week - 2));
                        
                    case dateOnly: //An "ordinal date".
                        break;
                }
            }
        } //if (!(strict && isdigit(ch[0])))
        
        if (outRange) {
            if (isValidDate)
                range.length = ch - start_of_date;
            else
                range.location = NSNotFound;
            
            *outRange = range;
        }
        if (outTimeZone) {
            *outTimeZone = timeZone;
        }
        
        return components;
    }
    
    - (NSDate *) dateFromString:(NSString *)string {
        return [self dateFromString:string timeZone:NULL];
    }
    - (NSDate *) dateFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone {
        return [self dateFromString:string timeZone:outTimeZone range:NULL];
    }
    - (NSDate *) dateFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone range:(out NSRange *)outRange {
        NSTimeZone *timeZone = nil;
        NSDateComponents *components = [self dateComponentsFromString:string timeZone:&timeZone range:outRange];
        if (outTimeZone)
            *outTimeZone = timeZone;
        parsingCalendar.timeZone = timeZone;
        
        return [parsingCalendar dateFromComponents:components];
    }
    
    - (BOOL)getObjectValue:(id *)outValue forString:(NSString *)string errorDescription:(NSString **)error {
        NSDate *date = [self dateFromString:string];
        if (outValue)
            *outValue = date;
        return (date != nil);
    }
    
    #pragma mark Unparsing
    
    @synthesize format;
    @synthesize includeTime;
    @synthesize timeSeparator;
    
    - (NSString *) replaceColonsInString:(NSString *)timeFormat withTimeSeparator:(unichar)timeSep {
        if (timeSep != ':') {
            NSMutableString *timeFormatMutable = [[timeFormat mutableCopy] autorelease];
            [timeFormatMutable replaceOccurrencesOfString:@":"
                                               withString:[NSString stringWithCharacters:&timeSep length:1U]
                                                  options:NSBackwardsSearch | NSLiteralSearch
                                                    range:(NSRange){ 0UL, [timeFormat length] }];
            timeFormat = timeFormatMutable;
        }
        return timeFormat;
    }
    
    - (NSString *) stringFromDate:(NSDate *)date {
        NSTimeZone *timeZone = self.defaultTimeZone;
        if (!timeZone) timeZone = [NSTimeZone defaultTimeZone];
        return [self stringFromDate:date timeZone:timeZone];
    }
    
    - (NSString *) stringFromDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone {
        switch (self.format) {
            case ISO8601DateFormatCalendar:
                return [self stringFromDate:date formatString:ISO_CALENDAR_DATE_FORMAT timeZone:timeZone];
            case ISO8601DateFormatWeek:
                return [self weekDateStringForDate:date timeZone:timeZone];
            case ISO8601DateFormatOrdinal:
                return [self stringFromDate:date formatString:ISO_ORDINAL_DATE_FORMAT timeZone:timeZone];
            default:
                [NSException raise:NSInternalInconsistencyException format:@"self.format was %d, not calendar (%d), week (%d), or ordinal (%d)", self.format, ISO8601DateFormatCalendar, ISO8601DateFormatWeek, ISO8601DateFormatOrdinal];
                return nil;
        }
    }
    
    - (NSString *) stringFromDate:(NSDate *)date formatString:(NSString *)dateFormat timeZone:(NSTimeZone *)timeZone {
        if (includeTime)
            dateFormat = [dateFormat stringByAppendingFormat:@"'T'%@", [self replaceColonsInString:ISO_TIME_FORMAT withTimeSeparator:self.timeSeparator]];
        
        unparsingCalendar.timeZone = timeZone;
        
        if (dateFormat != lastUsedFormatString) {
            [unparsingFormatter release];
            unparsingFormatter = nil;
            
            [lastUsedFormatString release];
            lastUsedFormatString = [dateFormat retain];
        }
        
        if (!unparsingFormatter) {
            unparsingFormatter = [[NSDateFormatter alloc] init];
            unparsingFormatter.formatterBehavior = NSDateFormatterBehavior10_4;
            unparsingFormatter.dateFormat = dateFormat;
            unparsingFormatter.calendar = unparsingCalendar;
        }
        
        NSString *str = [unparsingFormatter stringForObjectValue:date];
        
        if (includeTime) {
            NSInteger offset = [timeZone secondsFromGMT];
            offset /= 60;  //bring down to minutes
            if (offset == 0)
                str = [str stringByAppendingString:ISO_TIMEZONE_UTC_FORMAT];
            else
                str = [str stringByAppendingFormat:ISO_TIMEZONE_OFFSET_FORMAT, offset / 60, offset % 60];
        }
        
        //Undo the change we made earlier
        unparsingCalendar.timeZone = self.defaultTimeZone;
        
        return str;
    }
    
    - (NSString *) stringForObjectValue:(id)value {
        NSParameterAssert([value isKindOfClass:[NSDate class]]);
        
        return [self stringFromDate:(NSDate *)value];
    }
    
    /*Adapted from:
     *      Algorithm for Converting Gregorian Dates to ISO 8601 Week Date
     *      Rick McCarty, 1999
     *      http://personal.ecu.edu/mccartyr/ISOwdALG.txt
     */
    - (NSString *) weekDateStringForDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone {
        unparsingCalendar.timeZone = timeZone;
        NSDateComponents *components = [unparsingCalendar components:NSYearCalendarUnit | NSWeekdayCalendarUnit | NSDayCalendarUnit fromDate:date];
        
        //Determine the ordinal date.
        NSDateComponents *startOfYearComponents = [unparsingCalendar components:NSYearCalendarUnit fromDate:date];
        startOfYearComponents.month = 1;
        startOfYearComponents.day = 1;
        NSDateComponents *ordinalComponents = [unparsingCalendar components:NSDayCalendarUnit fromDate:[unparsingCalendar dateFromComponents:startOfYearComponents] toDate:date options:0];
        ordinalComponents.day += 1;
        
        enum {
            monday, tuesday, wednesday, thursday, friday, saturday, sunday
        };
        enum {
            january = 1, february, march,
            april, may, june,
            july, august, september,
            october, november, december
        };
        
        NSInteger year = components.year;
        NSInteger week = 0;
        //The old unparser added 6 to [calendarDate dayOfWeek], which was zero-based; components.weekday is one-based, so we now add only 5.
        NSInteger dayOfWeek = (components.weekday + 5) % 7;
        NSInteger dayOfYear = ordinalComponents.day;
        
        NSInteger prevYear = year - 1;
        
        BOOL yearIsLeapYear = is_leap_year(year);
        BOOL prevYearIsLeapYear = is_leap_year(prevYear);
        
        NSInteger YY = prevYear % 100;
        NSInteger C = prevYear - YY;
        NSInteger G = YY + YY / 4;
        NSInteger Jan1Weekday = (((((C / 100) % 4) * 5) + G) % 7);
        
        NSInteger weekday = ((dayOfYear + Jan1Weekday) - 1) % 7;
        
        if((dayOfYear <= (7 - Jan1Weekday)) && (Jan1Weekday > thursday)) {
            week = 52 + ((Jan1Weekday == friday) || ((Jan1Weekday == saturday) && prevYearIsLeapYear));
            --year;
        } else {
            NSInteger lengthOfYear = 365 + yearIsLeapYear;
            if((lengthOfYear - dayOfYear) < (thursday - weekday)) {
                ++year;
                week = 1;
            } else {
                NSInteger J = dayOfYear + (sunday - weekday) + Jan1Weekday;
                week = J / 7 - (Jan1Weekday > thursday);
            }
        }
        
        NSString *timeString;
        if(includeTime) {
            NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
            unichar timeSep = self.timeSeparator;
            if (!timeSep) timeSep = ISO8601DefaultTimeSeparatorCharacter;
            formatter.dateFormat = [self replaceColonsInString:ISO_TIME_WITH_TIMEZONE_FORMAT withTimeSeparator:timeSep];
            
            timeString = [formatter stringForObjectValue:date];
            
            [formatter release];
        } else
            timeString = @"";
        
        return [NSString stringWithFormat:@"%lu-W%02lu-%02lu%@", (unsigned long)year, (unsigned long)week, ((unsigned long)dayOfWeek) + 1U, timeString];
    }
    
    @end
    
    static NSUInteger read_segment(const unsigned char *str, const unsigned char **next, NSUInteger *out_num_digits) {
        NSUInteger num_digits = 0U;
        NSUInteger value = 0U;
        
        while(isdigit(*str)) {
            value *= 10U;
            value += *str - '0';
            ++num_digits;
            ++str;
        }
        
        if (next) *next = str;
        if (out_num_digits) *out_num_digits = num_digits;
        
        return value;
    }
    static NSUInteger read_segment_4digits(const unsigned char *str, const unsigned char **next, NSUInteger *out_num_digits) {
        NSUInteger num_digits = 0U;
        NSUInteger value = 0U;
        
        if (isdigit(*str)) {
            value += *(str++) - '0';
            ++num_digits;
        }
        
        if (isdigit(*str)) {
            value *= 10U;
            value += *(str++) - '0';
            ++num_digits;
        }
        
        if (isdigit(*str)) {
            value *= 10U;
            value += *(str++) - '0';
            ++num_digits;
        }
        
        if (isdigit(*str)) {
            value *= 10U;
            value += *(str++) - '0';
            ++num_digits;
        }
        
        if (next) *next = str;
        if (out_num_digits) *out_num_digits = num_digits;
        
        return value;
    }
    static NSUInteger read_segment_2digits(const unsigned char *str, const unsigned char **next) {
        NSUInteger value = 0U;
        
        if (isdigit(*str))
            value += *str - '0';
        
        if (isdigit(*++str)) {
            value *= 10U;
            value += *(str++) - '0';
        }
        
        if (next) *next = str;
        
        return value;
    }
    
    //strtod doesn't support ',' as a separator. This does.
    static double read_double(const unsigned char *str, const unsigned char **next) {
        double value = 0.0;
        
        if (str) {
            NSUInteger int_value = 0;
            
            while(isdigit(*str)) {
                int_value *= 10U;
                int_value += (*(str++) - '0');
            }
            value = int_value;
            
            if (((*str == ',') || (*str == '.'))) {
                ++str;
                
                register double multiplier, multiplier_multiplier;
                multiplier = multiplier_multiplier = 0.1;
                
                while(isdigit(*str)) {
                    value += (*(str++) - '0') * multiplier;
                    multiplier *= multiplier_multiplier;
                }
            }
        }
        
        if (next) *next = str;
        
        return value;
    }
    
    static BOOL is_leap_year(NSUInteger year) {
        return \
        ((year %   4U) == 0U)
        && (((year % 100U) != 0U)
            ||  ((year % 400U) == 0U));
  }

用法

	ISO8601DateFormatter *dateFormat = [[[ISO8601DateFormatter alloc] init] autorelease];
    NSDate *serverTime = [dateFormat dateFromString:dateTime];