使用 NSDateFormatter 解析 RFC 822 日期

发布于 2024-08-14 05:02:23 字数 1142 浏览 3 评论 0原文

我正在使用 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 种可能的组合。还不错,但有点老套。

I'm using a NSDateFormatter to parse a RFC 822 date on the iPhone. However, there is no way to specify optional elements in the date format. There are a couple of optional parts in the RFC 822 specification which is breaking the date parser. If nothing works out, I'd probably have to write a custom parser to obey the specs.

For example, the day name is optional in the spec. So both these dates are valid:

Tue, 01 Dec 2009 08:48:25 +0000 is parsed with the format EEE, dd MMM yyyy HH:mm:ss z
01 Dec 2009 08:48:25 +0000 is parsed with the format dd MMM yyyy HH:mm:ss z

This is what I am currently using:

+ (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];
}

And parsing the date as follows:

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

One way is to try both formats, and take whatever returns non null value. However, there are two optional parts in the spec (day name and seconds) and there would be 4 possible combinations. Still not too bad, but it's a bit hacky.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

開玄 2024-08-21 05:02:23

我使用以下方法来解析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;

}

I've used the following method to parse RFC822 dates. I believe it originally was from 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;

}
囍笑 2024-08-21 05:02:23

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

Count the number of salient characters before deciding which formatter to use. For example, the two you give have different numbers of commas and spaces. If no known format matches the counts, then you known not even to try parsing it as a date.

破晓 2024-08-21 05:02:23

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

I believe RFC 822 specifies two optional components in the date time: day of week and the seconds past the hour.

As a hack, it is possible to the symbols for the short days of the week:

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

If you then change the date format to this: EEEdd MMM yyyy HH:mm:ss z. You'll be able to parse times with about without the day of the week. This seems to allow a space after the comma too.

To be safe you should not just blindly set the symbols like this. You should get using setShortWeekdaySymbols and iterate over them adding the comma at the end. The reason being they are potentially different for each locale and the first day might not be Sunday.

Interestingly the format EEE, dd MMM yyyy HH:mm:ss z will parse times without the day of week, but the comma must be there, for example , 01 Dec 2009 08:48:25 +0000. Therefore, you could do something like Steve said but then strip off the day and pass though to the formatter. Not having the comma in the format does not seem to allow the week to be optional. Strange.

Unfortunately, this still doesn't help with the optional :ss in the format. But it might allow you to have two formats rather than four.

挥剑断情 2024-08-21 05:02:23

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

In case this is helpful to anyone else.. here is a NSDate+RFC822String.swift extension based on Simucal's answer.

It also caches the last used date format that was successful, since setting the dateFormatter.dateFormat is expensive.

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
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文