Avatar ·

Как преобразовать строку с датой в другую строку с датой в Swift?

Привет! Я новичок в Swift и iOS-разработке. У меня есть приложение, которое получает дату из API в виде строки, например: "2023-11-15T14:30:00Z". Мне нужно отобразить её пользователю в другом формате, например: "15 ноября 2023, 14:30". Я прочитал про NSDateFormatter, но запутался. Вот мой код, который не работает: ```swift let dateString = "2023-11-15T14:30:00Z" let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" let date = dateFormatter.dateFromString(dateString) dateFormatter.dateFormat = "dd MMMM yyyy, HH:mm" let result = dateFormatter.stringFromDate(date!) print(result) // Выводит nil или крашится ``` Ошибка: приложение падает с `fatal error: unexpectedly found nil while unwrapping an Optional value`. Я понимаю, что проблема в опциональном значении, но даже после добавления `if let` результат всё равно nil. Подскажите, как правильно преобразовать строку с датой в другой формат? И почему мой код не работает? Я использую Swift 5 и Xcode 14. Спасибо! <<>>

Основная причина ошибки

Ваш код не работает по двум причинам:

  1. Вы используете NSDateFormatter (старый Objective-C класс), а в Swift рекомендуется DateFormatter.
  2. Формат Z в строке "2023-11-15T14:30:00Z" означает часовой пояс UTC, но ваш парсер не учитывает это правильно.

Правильное решение

Вот рабочий код на Swift 5 с использованием DateFormatter:

import Foundation

let inputDateString = "2023-11-15T14:30:00Z"

// Шаг 1: Создаём парсер для входного формата
let inputFormatter = DateFormatter()
inputFormatter.locale = Locale(identifier: "en_US_POSIX") // Важно для ISO 8601
inputFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
inputFormatter.timeZone = TimeZone(secondsFromGMT: 0) // Указываем UTC

// Шаг 2: Преобразуем строку в Date
guard let date = inputFormatter.date(from: inputDateString) else {
    fatalError("Невозможно распарсить дату: \(inputDateString)")
}

// Шаг 3: Создаём форматтер для вывода
let outputFormatter = DateFormatter()
outputFormatter.locale = Locale(identifier: "ru_RU") // Русская локализация
outputFormatter.dateFormat = "dd MMMM yyyy, HH:mm"
outputFormatter.timeZone = TimeZone.current // Локальный часовой пояс

// Шаг 4: Преобразуем Date в строку
let result = outputFormatter.string(from: date)
print(result) // "15 ноября 2023, 17:30" (если вы в Москве, UTC+3)

Почему это работает?

  • Locale: en_US_POSIX для парсинга гарантирует, что формат не будет зависеть от настроек устройства пользователя. Для вывода используем ru_RU, чтобы месяцы отображались по-русски.
  • TimeZone: При парсинге указываем UTC (Z в строке), при выводе — текущий часовой пояс пользователя (чтобы время отображалось корректно).
  • Безопасность: Используем guard let для обработки ошибок парсинга.

Дополнительные советы

Если вы работаете с ISO 8601 датами (как в вашем примере), в iOS 10+ есть более простой способ:

let isoFormatter = ISO8601DateFormatter()
if let date = isoFormatter.date(from: inputDateString) {
    let outputFormatter = DateFormatter()
    outputFormatter.locale = Locale(identifier: "ru_RU")
    outputFormatter.dateFormat = "dd MMMM yyyy, HH:mm"
    outputFormatter.timeZone = TimeZone.current
    print(outputFormatter.string(from: date))
}

Важно: Всегда проверяйте, что date(from:) возвращает не nil. Используйте guard или if let для безопасного извлечения значения.

<<>>

Альтернативный подход: использование DateComponentsFormatter

Если вам нужно не просто преобразовать формат, а показать относительное время (например, "2 часа назад"), используйте RelativeDateTimeFormatter (iOS 13+):

import Foundation

let dateString = "2023-11-15T14:30:00Z"
let inputFormatter = DateFormatter()
inputFormatter.locale = Locale(identifier: "en_US_POSIX")
inputFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
inputFormatter.timeZone = TimeZone(secondsFromGMT: 0)

guard let date = inputFormatter.date(from: dateString) else { return }

let relativeFormatter = RelativeDateTimeFormatter()
relativeFormatter.locale = Locale(identifier: "ru_RU")
relativeFormatter.unitsStyle = .full
let relativeString = relativeFormatter.localizedString(for: date, relativeTo: Date())
print(relativeString) // "2 недели назад" (зависит от текущей даты)

Когда это полезно?

  • Для отображения в социальных сетях, чатах, лентах новостей.
  • Когда точная дата не так важна, как относительное время.
  • Для автоматической локализации (RelativeDateTimeFormatter сам переводит на русский).

Этот подход экономит время и делает интерфейс более дружелюбным для пользователя.