Кто правильно применяет ISO8601?Свифт или Весна? - PullRequest
0 голосов
/ 02 апреля 2019

Поведение по умолчанию сериализатора Джексона, используемого в среде Spring, выглядит следующим образом: (из исходного кода spring-data-rest):

        /**
         * The most common ISO DateTime Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSZ},
         * e.g. "2000-10-31T01:30:00.000-05:00".
         * <p>This is the default if no annotation value is specified.
         */

Таким образом, одним из примеров такого форматирования может быть 2019-03-20T11:18:46.000+0000. Swift также имеет декодер ISO8601, но фактически для Swift эта строка имеет только недопустимый формат из-за миллисекунд. Если вы удалите часть в миллисекундах, swift может успешно десериализовать строку.

struct Test: Codable {
    let createdAt: Date
}

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601

let data2 = "{\"createdAt\": \"2018-12-05T14:05:35.000+0000\"}".data(using: .utf8)!
let decoded2 = try! decoder.decode(Test.self, from: data2)

Swift относится к RFC 3339 https://www.ietf.org/rfc/rfc3339.txt, и здесь нет упоминаний о миллисекундах. У меня вопрос - какой формат тогда правильный? Если что-то в формате ISO, которое должно быть строго определено, я обычно не ожидаю написать собственный десериализатор (swift) или определить пользовательский шаблон для сериализации (spring).

Ответы [ 2 ]

1 голос
/ 02 апреля 2019

Страница 23 из проекта ISO8601 2016 :

При необходимости для конкретного применения можно указать десятичную дробь часа, минуты или секунды. Если десятичная дробь включена, элементы времени более низкого порядка (если таковые имеются) должны быть опущены, а десятичная дробь должна быть отделена от целочисленной части десятичным знаком, указанным в ISO 31-0, то есть запятой [,] или полной остановкой [ .]. Из них запятая является предпочтительным знаком. Если величина числа меньше единицы, десятичному знаку должен предшествовать два нуля в соответствии с 3.6.

Так что 2018-12-05T14:05:35.000 законно. В то время как RFC 3339 не упоминает миллисекунды по имени, он говорит о «дробях», например ::

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

и

ИСО 8601 также требует (в разделе 5.3.1.3) десятичной дроби следовать за «0», если оно меньше единицы. Приложение B.2 ISO 8601 приводит примеры, где десятичным дробям не предшествует "0". Эта грамматика предполагает, что раздел 5.3.1.3 является правильным и что Приложение B.2 по ошибке.

1 голос
/ 02 апреля 2019

Вы должны установить опции iso8601.Вы можете сделать это с помощью ISO8601DateFormatter.

struct Test: Codable {
    let createdAt: Date
}

enum CustomDateDecodingStrategy {

    private static let formatter: ISO8601DateFormatter = {
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [
            .withFullDate,
            .withFullTime,
            .withTimeZone,
            .withFractionalSeconds
        ]
        return formatter
    }()

    static func decode(_ decoder: Decoder) throws -> Date {
        let container = try decoder.singleValueContainer()
        let dateStr = try container.decode(String.self)
        if let date = formatter.date(from: dateStr) {
            return date
        } else {
            throw NSError(domain: "date", code: -1, userInfo: nil)
        }
    }

}

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom(CustomDateDecodingStrategy.decode)

let data2 = "{\"createdAt\": \"2018-12-05T14:05:35.000+0000\"}".data(using: .utf8)!
let decoded2 = try! decoder.decode(Test.self, from: data2)
...