Преобразование строки JSON yyyy-mm-ddthh:mm:ssz в читаемый формат в Swift?

Я пытаюсь преобразовать строку, переданную в мое приложение, в виде строки в формате вопроса (гггг-мм-ддчч:мм:ссз) во что-то понятное пользователю. Я попытался создать переменную для форматирования даты:

var dateFormatter: DateFormatter {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
    return formatter
}

а затем я попытался использовать его следующим образом:

Text("\(self.dateFormatter.date(from: self.dateToConvert))")

где dateToConvert — это передаваемая строка JSON. Ошибка, которую я получил, заключалась в том, что метод экземпляра «appendInterpolation» требует этой «Даты?» соответствовать '_FormatSpecified'. Есть идеи, как это сделать правильно?


person nickcoding    schedule 10.08.2020    source источник
comment
Не заключайте Z в одинарные кавычки в формате даты. Z предназначен для того, чтобы указать форматировщику даты анализировать часовой пояс как часть даты.   -  person Duncan C    schedule 10.08.2020
comment
SwiftUI.Text ожидает, что вы дадите ему значение (как Date) и отформатируете отдельно. Он сам отформатирует дату. hackingwithswift.com/quick-start/ Свифтуи/   -  person Alexander    schedule 10.08.2020


Ответы (2)


Сначала вам нужно установить удобный формат даты для вашего форматтера. Затем преобразуйте объект Date обратно в String:

dateFormatter.setLocalizedDateFormatFromTemplate("yyyyMMdd")
let text = dateFormatter.string(from: convertedDate)

И использовать его в Text напрямую:

Text(text)

ИЗМЕНИТЬ

Вот простая демонстрация того, как вы можете разобрать и отобразить Date:

struct ContentView: View {
    @State var date: Date? = Date()

    // by making `it static let` it is only computed once
    static let inputDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(abbreviation: "UTC")
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" // <- do not escape `Z` (time zone)
        return formatter
    }()

    static let outputDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.setLocalizedDateFormatFromTemplate("yyyyMMddHHmmss")
        return formatter
    }()

    // handling of optional `Date`
    var formattedDate: String {
        guard let date = date else { return "Date is nil" }
        return Self.outputDateFormatter.string(from: date)
    }

    var body: some View {
        Text(formattedDate)
            .onAppear(perform: parseDate)
    }

    func parseDate() {
        let dateStr = // your date as `String`
        if let date = Self.inputDateFormatter.date(from: dateStr) {
            self.date = date
        } else {
            print("invalid date")
        }
        // alternatively if you don't care about errors:
        // date = Self.dateFormatter.date(from: dateStr)
    }
}
person pawello2222    schedule 10.08.2020
comment
Вы никогда не должны использовать фиксированный формат даты при отображении даты пользователю. Кстати, не нужно добавлять пробелы между компонентами в вашем шаблоне при локализации dateFormat. - person Leo Dabus; 10.08.2020
comment
Другая проблема заключается в том, что OP игнорирует часовой пояс из строки даты, экранирующей Z - person Leo Dabus; 10.08.2020
comment
И не забудьте установить локаль en_US_POSIX перед установкой фиксированного формата даты - person Leo Dabus; 10.08.2020
comment
@ pawello2222 Я пытался использовать это решение, и в строке, где я устанавливаю текстовую константу, я получаю сообщение «Невозможно преобразовать значение типа «Строка» в ожидаемый тип аргумента «Дата» как ошибку. - person nickcoding; 11.08.2020
comment
@nickcoding Я предположил, что convertedDate - это дата, которую вы уже преобразовали из своего JSON. Так что это должен быть объект Date. - person pawello2222; 11.08.2020
comment
@ pawello2222 pawello2222 Думаю, я просто запутался в том, как именно я должен инициализировать dateFormatter, а затем как я должен отображать дату как Text(), когда мне дается какая-то строка JSON. - person nickcoding; 11.08.2020
comment
@nickcoding Я обновил свой ответ простой демонстрацией. Надеюсь, поможет. - person pawello2222; 11.08.2020
comment
outputDateFormatter не имеет никакого смысла. Почему вы установили часовой пояс в UTC и опустили эту информацию из выходной строки? Он должен отображать текущий часовой пояс. Кстати, вы обычно не проверяете необязательный параметр на nil и принудительно разворачиваете его позже в Swift. Вы всегда должны правильно разворачивать опции, используя guard let или if let - person Leo Dabus; 13.08.2020
comment
guard let date = date else { return "Date is nil" }, а затем return Self.outputDateFormatter.string(from: date) и установка часового пояса в вашем inputDateFormatter бессмысленна, если вы не будете использовать его для преобразования даты обратно в строку. - person Leo Dabus; 13.08.2020
comment
@LeoDabus Спасибо, я удалил часовой пояс из outputDateFormatter. И я решил использовать однострочник в formattedDate, потому что он был короче. Как вы указали, guard-let понятнее. Я обновил свой ответ. Спасибо за комментарии. - person pawello2222; 13.08.2020

Функция DateFormatter date(from:) возвращает дату Optional. Если введенная вами строка недействительна, она вернет ноль.

Как и во многих других случаях в Swift, вам нужно написать код для обработки nil. Один из способов сделать это:

let dateFromString = dateFormatter.date(from: self.dateToConvert) ?? "invalid"
print("Converted date = \(dateFromString)")

Поочередно:

if let dateFromString = dateFormatter.date(from: self.dateToConvert) {
    print("Converted date = \(dateFromString)")
} else {
    print("Date string is in an invalid format.")
}

Также обратите внимание, что когда вы регистрируете Date, оно будет отображаться в формате UTC, поэтому часовой пояс, вероятно, будет отличаться от вашего местного часового пояса. Если вы хотите отобразить его в местном часовом поясе, вам необходимо предпринять дополнительные шаги.

person Duncan C    schedule 10.08.2020
comment
OP игнорирует часовой пояс из строки даты, экранирующей Z. Это приведет к местному времени, в то время как строка является временем UTC. - person Leo Dabus; 10.08.2020
comment
Это не работает, потому что self.dateToConvert является переменной даты, а недействительным является строка. Я думаю, что они оба должны быть датами - person nickcoding; 11.08.2020