Подтвердить что ты не робот

Как разобрать ISO 8601 с использованием NSDateFormatter с дополнительной частью миллисекунд

Я пытаюсь использовать NSDateFormatter для анализа дат, которые находятся в любом из этих форматов.

@"2013-02-01T14:21:00"

или

@"2013-02-01T14:21:56.345"

В настоящее время я использую метод ниже для синтаксического анализа строки и возврата даты:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss"];

[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
NSDate *date = [dateFormatter dateFromString:dateToFormat];

Это отлично работает для первого стиля даты, но возвращает nil для строки, которая включает в себя миллисекундную часть.

Я предполагаю, что могу проверить существование миллисекунд и разбить их, но мне было интересно, могу ли я изменить формат даты для обработки .SSS как необязательного?

Спасибо за помощь

4b9b3361

Ответ 1

Насколько я знаю, нет способа сделать необязательные параметры.

Обычное решение - использовать два форматирования, по одному для каждого формата. Чтобы решить, какой форматтер использовать, вы можете либо

  • Подсчитайте количество символов в строке даты (как предложено в Разбор даты RFC 822 с помощью NSDateFormatter)

  • Просто попробуйте оба форматирования и получите первый результат не nil.

Поскольку ваши форматы дат похожи, вы можете использовать только один форматировщик, а если строка даты слишком короткая, добавьте .000 перед использованием форматирования.

Ответ 2

Я написал универсальный парсер, который удалял миллисекундную часть.

@implementation JSONModel(NSPAdditions)

- (NSDate *)NSDateFromNSString:(NSString*)string {
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
    NSArray* parts = [string componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@" T"]];
    if ([parts count] <= 1) {
        return [formatter dateFromString:string];
    }
    NSString *part0 = parts[0];
    NSAssert([part0 length] == [@"yyyy-MM-dd" length], @"Date format error");
    NSString *part1 = parts[1];
    if ([part1 length] > [@"HH:mm:ss" length]) {
        part1 = [part1 substringToIndex:[@"HH:mm:ss" length]];
    }
    NSString *fmted = [NSString stringWithFormat:@"%@ %@", part0, part1];
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    return [formatter dateFromString:fmted];
}

@end

Ответ 3

Вот решение, которое я использую (Swift 3):

extension DateFormatter {

    static let iso8601DateFormatter: DateFormatter = {
        let enUSPOSIXLocale = Locale(identifier: "en_US_POSIX")
        let iso8601DateFormatter = DateFormatter()
        iso8601DateFormatter.locale = enUSPOSIXLocale
        iso8601DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
        iso8601DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
        return iso8601DateFormatter
    }()

    static let iso8601WithoutMillisecondsDateFormatter: DateFormatter = {
        let enUSPOSIXLocale = Locale(identifier: "en_US_POSIX")
        let iso8601DateFormatter = DateFormatter()
        iso8601DateFormatter.locale = enUSPOSIXLocale
        iso8601DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
        iso8601DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
        return iso8601DateFormatter
    }()

    static func date(fromISO8601StringWithOptionalMilliseconds string: String) -> Date? {
        if let dateWithMilliseconds = iso8601DateFormatter.date(from: string) {
            return dateWithMilliseconds
        }

        if let dateWithoutMilliseconds = iso8601WithoutMillisecondsDateFormatter.date(from: string) {
            return dateWithoutMilliseconds
        }

        return nil
    }
}

Применение:

let dateToString = "2016-12-31T23:59:59.9999999"
let dateTo = DateFormatter.date(fromISO8601StringWithOptionalMilliseconds: dateToString)
// dateTo: 2016-12-31 23:59:59 +0000

let dateFromString = "2016-12-01T00:00:00"
let dateFrom = DateFormatter.date(fromISO8601StringWithOptionalMilliseconds: dateFromString)
// dateFrom: 2016-12-01 00:00:00 +0000

Я также рекомендую проверить статью Apple о форматах даты.