16

我正在使用 NSDateFormatter 来解析 iPhone 上的 RFC 822 日期。但是,无法以日期格式指定可选元素。RFC 822 规范中有几个可选部分破坏了日期解析器。如果没有任何效果,我可能不得不编写一个自定义解析器来遵守规范。

例如,日期名称在规范中是可选的。所以这两个日期都是有效的:

Tue, 01 Dec 2009 08:48:25 +0000 用格式EEE, dd MMM yyyy HH:mm:ss z 01 Dec 2009 08:48:25 +0000解析 用格式解析dd MMM yyyy HH:mm:ss z

这是我目前正在使用的:

+ (NSDateFormatter *)rfc822Formatter {
    static NSDateFormatter *formatter = nil;
    if (formatter == nil) {
        formatter = [[NSDateFormatter alloc] init];
        NSLocale *enUS = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
        [formatter setLocale:enUS];
        [enUS release];
        [formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss z"];
    }
    return formatter;
}

+ (NSDate *)dateFromRFC822:(NSString *)date {
    NSDateFormatter *formatter = [NSDate rfc822Formatter];
    return [formatter dateFromString:date];
}

并解析日期如下:

self.entry.published = [NSDate dateFromRFC822:self.currentString];

一种方法是尝试两种格式,并采用返回非空值的任何内容。但是,规范中有两个可选部分(日期名称和秒),并且有 4 种可能的组合。仍然不是太糟糕,但它有点hacky。

4

4 回答 4

6

我使用以下方法来解析 RFC822 日期。我相信它最初来自MWFeedParser

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

    // Create date formatter
    static NSDateFormatter *dateFormatter = nil;
    if (!dateFormatter) {
        NSLocale *en_US_POSIX = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
        dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setLocale:en_US_POSIX];
        [dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
        [en_US_POSIX release];
    }

    // Process
    NSDate *date = nil;
    NSString *RFC822String = [[NSString stringWithString:dateString] uppercaseString];
    if ([RFC822String rangeOfString:@","].location != NSNotFound) {
        if (!date) { // Sun, 19 May 2002 15:21:36 GMT
            [dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm:ss zzz"]; 
            date = [dateFormatter dateFromString:RFC822String];
        }
        if (!date) { // Sun, 19 May 2002 15:21 GMT
            [dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm zzz"]; 
            date = [dateFormatter dateFromString:RFC822String];
        }
        if (!date) { // Sun, 19 May 2002 15:21:36
            [dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm:ss"]; 
            date = [dateFormatter dateFromString:RFC822String];
        }
        if (!date) { // Sun, 19 May 2002 15:21
            [dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm"]; 
            date = [dateFormatter dateFromString:RFC822String];
        }
    } else {
        if (!date) { // 19 May 2002 15:21:36 GMT
            [dateFormatter setDateFormat:@"d MMM yyyy HH:mm:ss zzz"]; 
            date = [dateFormatter dateFromString:RFC822String];
        }
        if (!date) { // 19 May 2002 15:21 GMT
            [dateFormatter setDateFormat:@"d MMM yyyy HH:mm zzz"]; 
            date = [dateFormatter dateFromString:RFC822String];
        }
        if (!date) { // 19 May 2002 15:21:36
            [dateFormatter setDateFormat:@"d MMM yyyy HH:mm:ss"]; 
            date = [dateFormatter dateFromString:RFC822String];
        }
        if (!date) { // 19 May 2002 15:21
            [dateFormatter setDateFormat:@"d MMM yyyy HH:mm"]; 
            date = [dateFormatter dateFromString:RFC822String];
        }
    }
    if (!date) NSLog(@"Could not parse RFC822 date: \"%@\" Possibly invalid format.", dateString);
    return date;

}
于 2013-10-01T14:31:57.463 回答
4

在决定使用哪个格式化程序之前计算显着字符的数量。例如,您给出的两个逗号和空格的数量不同。如果没有已知格式与计数匹配,那么您甚至都不知道尝试将其解析为日期。

于 2009-12-05T06:00:09.663 回答
1

我相信 RFC 822 在日期时间中指定了两个可选组件:星期几和一小时后的秒数。

作为一种技巧,可以将符号用于一周中的短短几天:

NSArray *shortWeekSymbols = [NSArray arrayWithObjects:@"Sun,", @"Mon,", @"Tue,", @"Wed,", @"Thu,", @"Fri,", @"Sat,", nil];
        [formatter setShortWeekdaySymbols:shortWeekSymbols];

如果您随后将日期格式更改为:EEEdd MMM yyyy HH:mm:ss z. 您将能够在没有星期几的情况下解析时间。这似乎也允许逗号后有一个空格。

为了安全起见,您不应该像这样盲目地设置符号。您应该开始使用setShortWeekdaySymbols并遍历它们,并在末尾添加逗号。原因是它们可能因每个地区而异,而且第一天可能不是星期天。

有趣的是,该格式EEE, dd MMM yyyy HH:mm:ss z将解析没有星期几的时间,但必须有逗号,例如, 01 Dec 2009 08:48:25 +0000. 因此,您可以像史蒂夫所说的那样做一些事情,然后剥离一天并将其传递给格式化程序。格式中没有逗号似乎不允许星期是可选的。奇怪的。

不幸的是,这仍然对格式中的可选 :ss 没有帮助。但它可能允许您拥有两种格式而不是四种格式。

于 2009-12-05T11:56:15.437 回答
0

如果这对其他人有帮助.. 这是一个基于Simucal 回答的 NSDate+RFC822String.swift 扩展。

它还缓存上次使用的成功日期格式,因为设置 dateFormatter.dateFormat 的成本很高。

import Foundation

private let dateFormatter: NSDateFormatter = {
    let dateFormatter = NSDateFormatter()
    dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
    dateFormatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)

    return dateFormatter
}()

private let dateFormatsWithComma = ["EEE, d MMM yyyy HH:mm:ss zzz", "EEE, d MMM yyyy HH:mm zzz", "EEE, d MMM yyyy HH:mm:ss", "EEE, d MMM yyyy HH:mm"]
private let dateFormatsWithoutComma = ["d MMM yyyy HH:mm:ss zzz", "d MMM yyyy HH:mm zzz", "d MMM yyyy HH:mm:ss", "d MMM yyyy HH:mm"]

private var lastUsedDateFormatString: String?

extension NSDate {
    class func dateFromRFC822String(RFC822String: String) -> NSDate? {
        let RFC822String = RFC822String.uppercaseString

        if lastUsedDateFormatString != nil {
            if let date = dateFormatter.dateFromString(RFC822String) {
                return date
            }
        }

        if RFC822String.containsString(",") {
            for dateFormat in dateFormatsWithComma {
                dateFormatter.dateFormat = dateFormat
                if let date = dateFormatter.dateFromString(RFC822String) {
                    lastUsedDateFormatString = dateFormat
                    return date
                }
            }
        } else {
            for dateFormat in dateFormatsWithoutComma {
                dateFormatter.dateFormat = dateFormat
                if let date = dateFormatter.dateFromString(RFC822String) {
                    lastUsedDateFormatString = dateFormat
                    return date
                }
            }
        }

        return nil
    }
}
于 2016-04-20T15:42:50.533 回答