DateIntervalFormatter — це клас Foundation в iOS та macOS, призначений для форматування діапазонів дат у локалізовані рядки: «21–25 липня 2024 р.», «з 21 по 25 липня 2024 р.» або «Jul 21–25, 2024». За даними Apple Developer Documentation, 2024, DateIntervalFormatter автоматично визначає, які компоненти дати повторювати (якщо діапазон у межах одного місяця, місяць виноситься один раз), і локалізує розділювачі та порядок слів. На відміну від ручного склеювання двох відформатованих дат, цей клас враховує граматичні правила мови. Клас доступний починаючи з iOS 8 та macOS 10.10.
Головне
DateIntervalFormatter — це підклас Formatter в Foundation, який приймає об'єкт DateInterval (діапазон між двома датами) і повертає локалізований рядок з описом цього діапазону. Наприклад, для діапазону з 21 по 25 липня 2024 року результат буде «21–25 липня 2024 р.» для uk_UA. Клас розумно групує повторювані елементи: якщо обидві дати в одному місяці, назва місяця виводиться один раз.
Основна цінність DateIntervalFormatter — автоматична обробка граматики діапазонів. При ручному форматуванні розробнику довелося б порівнювати місяць/рік початку та закінчення, вибирати розділювач (тире, «по», «to», «bis») і слідкувати за відмінюванням. DateIntervalFormatter робить це автоматично на основі даних CLDR (Common Locale Data Repository), що зменшує кількість помилок в інтернаціональних додатках, особливо для мов зі складною граматикою.
За даними Unicode CLDR v44 (2024), правила форматування інтервалів дат сильно відрізняються між мовами. Німецька використовує прийменник «bis» (21.–25. Juli 2024), французька — «au» (du 21 au 25 juillet 2024), японська — через дефіс (2024 Jul 21–25). DateIntervalFormatter покриває всі ці варіації без додаткового коду з боку розробника.
Принцип роботи DateIntervalFormatter заснований на аналізі двох дат, що утворюють діапазон, і виборі оптимального рядкового представлення з урахуванням локалі. Клас порівнює компоненти дат (рік, місяць, день) і приймає рішення про групування: якщо рік однаковий — виводиться один раз; якщо місяць однаковий — виводиться один раз; якщо дні різняться — виводиться діапазон днів.
| Діапазон | uk_UA |
|---|---|
| Один день | 21 липня 2024 р. |
| В межах місяця | 21–25 липня 2024 р. |
| Різні місяці | 21 липня – 5 серпня 2024 р. |
| Різні роки | 25 грудня 2024 р. – 5 січня 2025 р. |
| З часом | 21 липня 2024 р., 14:30–16:45 |
Логіка групування реалізована на основі шаблонів CLDR. Форматувальник перевіряє рівність кожного компонента дати (Calendar.Component: year, month, day) в порядку від старшого до молодшого. Як тільки компонент відрізняється, всі молодші компоненти виводяться для обох дат. Наприклад, для 21–25 липня: рік збігається, місяць збігається, день відрізняється → місяць виводиться один раз, дні — для кожної дати.
Важливий нюанс: DateIntervalFormatter не підтримує налаштування розділювача через API. Розділювач визначається локаллю і не може бути перевизначений. Якщо потрібен нестандартний розділювач (наприклад, «до» замість «по»), доведеться форматувати дати вручну через DateFormatter і склеювати рядки з кастомним розділювачем.
DateIntervalFormatter надає два набори стилів: dateStyle та timeStyle для кожного кінця діапазону. Ці стилі працюють аналогічно DateFormatter: .none приховує компонент, .short дає короткий формат (21.07.2024), .medium — середній (21 лип. 2024 р.), .long — повний (21 липня 2024 р.), .full — максимально детальний (неділя, 21 липня 2024 р.). Комбінуючи dateStyle та timeStyle, можна отримати різні рівні деталізації.
Важливо: dateStyle та timeStyle застосовуються до обох кінців діапазону однаково. Не можна задати різний стиль для початку та кінця (наприклад, .medium для початку та .short для кінця). Якщо потрібен різний стиль, необхідно форматувати кожну дату окремим DateFormatter і склеювати результати.
import Foundation
let formatter = DateIntervalFormatter()
// Різні комбінації стилів
let startDate = Date()
let endDate = Date().addingTimeInterval(345600) // +4 дні
let interval = DateInterval(start: startDate, end: endDate)
// Лише дата, середній формат
formatter.dateStyle = .medium
formatter.timeStyle = .none
print("Лише дата: \(formatter.string(from: interval))")
// Короткий формат з часом
formatter.dateStyle = .short
formatter.timeStyle = .short
print("Короткий з часом: \(formatter.string(from: interval))")
// Повний формат
formatter.dateStyle = .full
formatter.timeStyle = .none
print("Повна дата: \(formatter.string(from: interval))")
Базове використання DateIntervalFormatter включає створення екземпляра, налаштування стилів і виклик string(from:). Метод приймає DateInterval — структуру з властивостями start та end. DateInterval можна створити як з двох Date, так і ініціалізувати з Duration (iOS 16+). Після форматування результат готовий до відображення в UI без додаткової обробки.
import Foundation
let calendar = Calendar.current
let now = Date()
// Приклади конкретних інтервалів
let examples: [(title: String, start: Date, end: Date)] = [
(
"2-hour event",
now,
now.addingTimeInterval(7200)
),
(
"Week-long vacation",
now,
now.addingTimeInterval(604800)
),
(
"Cross-year period",
calendar.date(from: DateComponents(
year: 2024, month: 12, day: 25
))!,
calendar.date(from: DateComponents(
year: 2025, month: 1, day: 5
))!
)
]
let intervalFormatter = DateIntervalFormatter()
intervalFormatter.dateStyle = .medium
intervalFormatter.timeStyle = .short
for (title, start, end) in examples {
let interval = DateInterval(start: start, end: end)
let result = intervalFormatter.string(from: interval)
print("\(title): \(result)")
}
// Форматування через Calendar (iOS 16+)
if let nextWeek = calendar.date(
byAdding: .day, value: 7, to: now
) {
let weekInterval = DateInterval(start: now, end: nextWeek)
print("Наступні 7 днів: \(intervalFormatter.string(from: weekInterval))")
}
DateIntervalFormatter та Calendar Booking: в додатках бронювання (готелі, квитки) інтервали дат — ключовий елемент UI. DateIntervalFormatter коректно обробляє міжрічні інтервали (наприклад, з 28 грудня по 3 січня), групуючи місяць та рік для кожної дати окремо. Для таких сценаріїв рекомендується використовувати dateStyle = .medium та timeStyle = .none — найбільш читабельний формат для дат без часу.
import Foundation
let formatter = DateIntervalFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .none
// Приклади бронювання
struct Booking {
let checkIn: Date
let checkOut: Date
var formattedInterval: String {
let interval = DateInterval(start: checkIn, end: checkOut)
return formatter.string(from: interval)
}
}
let booking = Booking(
checkIn: calendar.date(from: DateComponents(
year: 2024, month: 7, day: 21
))!,
checkOut: calendar.date(from: DateComponents(
year: 2024, month: 7, day: 25
))!
)
print("Бронювання: \(booking.formattedInterval)")
// Використання з різними локалями
let enFormatter = DateIntervalFormatter()
enFormatter.dateStyle = .medium
enFormatter.timeStyle = .none
enFormatter.locale = Locale(identifier: "en_US")
print("Англійська: \(enFormatter.string(from: interval))")
DateInterval vs DateIntervalFormatter: DateInterval — структура, що представляє діапазон дат (початок та кінець). DateIntervalFormatter — форматувальник, що перетворює цю структуру в рядок. DateInterval також підтримує перевірку contains(Date) та intersection(with:), що корисно для логіки бронювання (перевірка перетину дат).
DateIntervalFormatter локалізує виведення через властивість locale. За замовчуванням використовується Locale.current — локаль пристрою. Для відображення інтервалів іншою мовою (наприклад, контент англійською при українському інтерфейсі) встановіть locale явно. На відміну від DateFormatter, DateIntervalFormatter не вимагає встановлення Locale для серверних даних — він завжди використовує свою внутрішню логіку групування на основі CLDR.
Мовні відмінності у форматуванні інтервалів дат значно складніші, ніж для одиночних дат. Англійська використовує просте тире: Jul 21–25. Українська — прийменник «по»: з 21 по 25 липня. Французька — прийменник «au»: du 21 au 25 juillet. Німецька — крапка після числа та тире: 21.–25. Juli. DateIntervalFormatter покриває всі ці варіанти.
import Foundation
let formatter = DateIntervalFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
let start = Date()
let end = Date().addingTimeInterval(259200) // +3 дні
let interval = DateInterval(start: start, end: end)
// Демонстрація різних локалей
let localeIds = ["ru_RU", "en_US", "de_DE", "fr_FR", "ja_JP", "zh_CN"]
for localeId in localeIds {
formatter.locale = Locale(identifier: localeId)
print("\(localeId): \(formatter.string(from: interval))")
}
// Локалізація часових інтервалів
formatter.dateStyle = .none
formatter.timeStyle = .short
let todayStart = Date()
let todayEnd = Date().addingTimeInterval(3600)
let timeInterval = DateInterval(start: todayStart, end: todayEnd)
for localeId in ["ru_RU", "en_US", "de_DE"] {
formatter.locale = Locale(identifier: localeId)
print("\(localeId) час: \(formatter.string(from: timeInterval))")
}
Особливості локалізації: для української мови DateIntervalFormatter використовує прийменники «з» та «по» для довгих форматів дати. Для коротких (short) — лише тире: 21.07–25.07.2024. Для повного формату (full) — із зазначенням дня тижня. Вибір формату залежить від контексту: для списку бронювань використовуйте .medium, для детального перегляду — .long або .full.
DateIntervalFormatter займає нішу форматування діапазонів дат, яку не покривають інші класи Foundation. DateFormatter форматує одиночні дати, RelativeDateTimeFormatter — відносні дати (назад/через), DateComponentsFormatter — тривалість (2 години 30 хвилин). Кожен із цих класів вирішує своє завдання, і їх не можна взаємозамінювати.
| Клас | Призначення | Приклад виведення |
|---|---|---|
| DateIntervalFormatter | Діапазон дат | 21–25 липня 2024 р. |
| DateFormatter | Одиночна дата | 21 липня 2024 р. |
| RelativeDateTimeFormatter | Відносний час | 3 дні тому |
| DateComponentsFormatter | Тривалість | 2 години 30 хвилин |
| ISO8601DateFormatter | Формат ISO 8601 | 2024-07-21T14:30:00Z |
Коли використовувати DateIntervalFormatter: для відображення діапазонів дат в UI — бронювання готелів, подій, фільтрація за датами, історія операцій. Для підрахунку тривалості між датами використовуйте DateComponentsFormatter, а не DateIntervalFormatter — другий не призначений для відображення тривалості. DateFormatter слід використовувати лише для одиночних дат, а не для склеювання двох відформатованих дат через рядкову конкатенацію — це призводить до втрати локалізації розділювача.
Edge case: DateIntervalFormatter не підтримує відображення open-ended інтервалів (без дати закінчення). Якщо кінець діапазону невідомий (наприклад, «з 21 липня»), використовуйте DateFormatter з прийменником «з» для української мови або «from» для англійської. Також DateIntervalFormatter не підтримує інтервали із зазначенням часу для різних днів — час застосовується до кожної дати окремо, що може виглядати надмірно для коротких інтервалів.
Часті запитання
DateIntervalFormatter — клас Foundation для форматування діапазонів дат у локалізовані рядки. Доступний з iOS 8, автоматично групує повторювані компоненти (місяць, рік) та вибирає правильний розділювач для мови.
Порівнює компоненти від року до дня. Якщо рік збігається — виводиться один раз. Якщо місяць збігається — виводиться один раз. Різні дні виводяться як діапазон. Це дає «21–25 липня 2024» замість «21 липня 2024 – 25 липня 2024».
Ні, розділювач визначається локаллю і не налаштовується через API. Для кастомного розділювача (наприклад, «до» замість «по») форматуйте кожну дату окремим DateFormatter і склеюйте рядки вручну.
DateIntervalFormatter форматує діапазон дат (дві дати) як єдине ціле з групуванням компонентів. DateFormatter форматує одну дату. Для діапазонів завжди використовуйте DateIntervalFormatter — це дає коректну локалізацію розділювача.
Автоматично — DateIntervalFormatter визначає, що роки різні, і виводить повні дати для обох кінців: «25 грудня 2024 р. – 5 січня 2025 р.». Жодних додаткових налаштувань не потрібно, достатньо встановити dateStyle.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також