【问题标题】:Parsing a RFC 822 date with NSDateFormatter使用 NSDateFormatter 解析 RFC 822 日期
【发布时间】:2010-12-23 11:26:50
【问题描述】:

我正在使用 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。

【问题讨论】:

    标签: iphone parsing date nsdate nsdateformatter


    【解决方案1】:

    我使用following method 来解析 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;
    
    }
    

    【讨论】:

    • 更改日期格式字符串的成本很高,您可能希望为每个格式创建一个日期格式器,并在应用程序的整个生命周期内一直使用它。
    【解决方案2】:

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

    【讨论】:

    • 这对我来说似乎是最实用的解决方案,因为月份和日期的名称是固定长度的,而所有其他值都是固定长度的数字。比尝试格式要便宜得多,直到一种有效!
    • 实施了一个基本的解决方案。对它不太满意,但它是迄今为止最好的 :) 逗号标识工作日的存在,两个冒号有助于标识秒。如果日期包含对其所遵循规范的引用,那就太好了,因为在许多格式众多的语言中,日期解析确实很麻烦。
    【解决方案3】:

    我相信 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 没有帮助。但它可能允许您使用两种格式而不是四种格式。

    【讨论】:

    • 感谢您的提示。我认为 RFC 822 没有提到本地化,只使用英文格式。附加逗号而不是硬编码值仍然是一个好主意。但由于我仍然需要检查两个组合,因此最好事先检查字符而不是尝试两次。
    【解决方案4】:

    如果这对其他人有帮助.. 这是一个基于 Simucal's answer 的 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
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-22
      • 1970-01-01
      • 2010-09-22
      • 2013-05-22
      • 2013-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多