Nil NSDate when trying to get date from UTC string

2020-02-04 07:14发布

Writing an iPhone app in Objective-C, I have a date in string form (in UTC format, with a Z on the end to denote zero UTC offset, or zulu time), which I need to parse into an NSDate object.

A bit of code:

NSDateFormatter* df = [[NSDateFormatter alloc]init];
[df setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
NSString* str = @"2009-08-11T06:00:00.000Z";
NSDate* date = [df dateFromString:str];
Running this through the debugger, date ends up nil! I'm assuming it has something to do with my date format string.

How can I fix it to correctly parse the date string?

A thought would be to make the Z in the date format literal, a la setting the date format to yyyy-MM-dd'T'HH:mm:ss.SSS'Z'.

That would work, except when the Z is parsed as a literal, the date loses offset information, and so is ambiguous, and therefore interpreted to be local time.

For example, if the string to parse was 2009-08-11T06:00:00.000Z (6:00 zulu time) it would be interpreted as 6:00 local time, and an incorrect offset would then be applied. It would then be parsed as 2009-08-11T06:00:00.000-600 (12:00 zulu time) with the offset depending on the user's offset.

Thanks!

7条回答
Animai°情兽
2楼-- · 2020-02-04 07:37

I think you need to put single quotes around the Z in the format string, because the Z actually means something to the formatter and you want it to represent a literal character instead.

    [df setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
查看更多
三岁会撩人
3楼-- · 2020-02-04 07:44

this may help you.. the "Z" is a literal for the time zone code. try using "o" (the letter, not zero). The literal "o" means UTC offset. I ran into this a while back, I hope this helped you.

查看更多
再贱就再见
4楼-- · 2020-02-04 07:47

There's no need to manipulate the string. Simply set the time zone on the NSDateFormatter (and the locale while you're at it):

NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
[formatter setLocale:[NSLocale systemLocale]];
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];

And parse date strings as needed:

NSDate * value = [formatter dateFromString:@"2012-03-01T23:08:25.000Z"];
NSLog(@"%@", value); // prints 2012-03-01 23:08:25 +0000
查看更多
兄弟一词,经得起流年.
5楼-- · 2020-02-04 07:48

You can use this method to get date from UTC.

+ (NSDate*)getDateFromUTCDateTimeString:(NSString*)dateString {

NSDateFormatter *isoDateFormatter = [[NSDateFormatter alloc] init];

[isoDateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];

[isoDateFormatter setTimeZone:[NSTimeZone  timeZoneWithAbbreviation:@"UTC"]];

NSDateFormatter *userFormatter = [[NSDateFormatter alloc] init];

[userFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];

[dateFormatter setTimeZone:[NSTimeZone  timeZoneWithAbbreviation:@"UTC"]];

NSDate *date = [isoDateFormatter dateFromString:dateString];

return [dateFormatter dateFromString:[userFormatter stringFromDate:date]];

}

查看更多
smile是对你的礼貌
6楼-- · 2020-02-04 07:54
-(NSDate*)dateFromZulu:(NSString*)str {
    if (str == nil) {
        NSLog(@"Error getting date");
        return [NSDate date];
    }

    NSDateFormatter *f = [[NSDateFormatter alloc] init];
    [f setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss Z"];
    NSDate *ret = [f dateFromString:[str stringByReplacingOccurrencesOfString:@"Z" withString:@" +0000"]];
    [f release];

    if (ret == nil) {
        ret = [NSDate date];
        NSLog(@"Error formatting date (%@)",str);       
    }   
    return ret;     
}
查看更多
贪生不怕死
7楼-- · 2020-02-04 07:58

Most answers suggest you to treat 'Z' as a literal character. Do not do this!

The Z actually means that the date is offset by 0 to UTC (+0000).

This is according to the time zone format ISO8601:

ISO 8601 time zone format: A constant, specific offset from UTC, which always has the same format except UTC itself ("Z").

"-08:00"

"Z"

What you want to do is use the following format for your NSDateFormatter:

 NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
                                  // 2013-11-18T23:00:00.324Z
                                  [formatter setTimeZone:[NSTimeZone localTimeZone]];
                                  [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"];
                                  return formatter;

By repeating the Z five times, you tell the formatter to use ISO8601 when parsing the string.

Bonus:

  • Use one to three Zs for RFC 822 GMT format.
  • Use four Zs for localized GMT format.

For more information check this document.

查看更多
登录 后发表回答