Как преобразовать строку с датой в другую строку с датой в Swift?
Основная причина ошибки
Ваш код не работает по двум причинам:
- Вы используете NSDateFormatter (старый Objective-C класс), а в Swift рекомендуется DateFormatter.
- Формат
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 сам переводит на русский).
Этот подход экономит время и делает интерфейс более дружелюбным для пользователя.