Анализ даты RFC 822 с помощью NSDateFormatter
-
13-09-2019 - |
Вопрос
Я использую NSDateFormatter для анализа даты RFC 822 на iPhone.Однако нет способа указать необязательные элементы в формате даты.В спецификации 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 возможных комбинации.Все еще не так уж плохо, но это немного халтурно.
Решение
Подсчитайте количество характерных символов, прежде чем принимать решение о том, какой форматировщик использовать.Например, те два, которые вы приводите, содержат разное количество запятых и пробелов.Если ни один известный формат не соответствует подсчетам, то вам, как известно, даже не стоит пытаться разобрать его как дату.
Другие советы
Я использовал следующий метод для анализа дат 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;
}
Я полагаю, что 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 в формате.Но это может позволить вам иметь два формата, а не четыре.
На случай, если это будет полезно кому-то еще..вот расширение 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
}
}