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 г.“ за bg_BG и „Jul 21–25, 2024“ за en_US. Класът интелигентно групира повтарящите се елементи: ако и двете дати са в един и същ месец, името на месеца се показва веднъж.
Основната стойност на 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 се основава на анализ на две дати, образуващи диапазона, и избор на оптимално текстово представяне, като се взема предвид locale. Класът сравнява компонентите на датата (година, месец, ден) и взема решение за групиране: ако годината е същата — показва се веднъж; ако месецът е същият — показва се веднъж; ако дните се различават — показва се диапазонът от дни.
| Диапазон | bg_BG | en_US |
|---|---|---|
| Един ден | 21 юли 2024 г. | Jul 21, 2024 |
| В рамките на месец | 21–25 юли 2024 г. | Jul 21–25, 2024 |
| Различни месеци | 21 юли – 5 август 2024 г. | Jul 21 – Aug 5, 2024 |
| Различни години | 25 декември 2024 г. – 5 януари 2025 г. | Dec 25, 2024 – Jan 5, 2025 |
| С време | 21 юли 2024 г., 14:30–16:45 | Jul 21, 2024, 2:30–4:45 PM |
Логика на групиране е имплементирана на базата на CLDR модели. Форматиращият проверява равенството на всеки компонент на датата (Calendar.Component: year, month, day) в ред от най-големия към най-малкия. Веднага щом компонент се различава, всички по-малки компоненти се показват и за двете дати. Например, за 21–25 юли: годината съвпада, месецът съвпада, денят се различава → месец веднъж, дни — за всяка дата поотделно.
Важен нюанс: DateIntervalFormatter не поддържа конфигуриране на разделителя чрез API. Разделителят се определя от locale и не може да бъде заменен. Ако е необходим персонализиран разделител (например „до“ вместо „–“), трябва да форматирате датите ръчно чрез 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 — структура с properties 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)")
// Използване с различни locale
let enFormatter = DateIntervalFormatter()
enFormatter.dateStyle = .medium
enFormatter.timeStyle = .none
enFormatter.locale = Locale(identifier: "en_US")
print("Английски: \(enFormatter.string(from: interval))")
DateInterval срещу DateIntervalFormatter: DateInterval — структура, представляваща диапазон от дати (начало и край). DateIntervalFormatter — форматиращ, който преобразува тази структура в низ. DateInterval също поддържа проверки contains(Date) и intersection(with:), което е полезно за логиката на резервации (проверка за припокриване на дати).
DateIntervalFormatter локализира изхода чрез свойството locale. По подразбиране се използва Locale.current — locale на устройството. За да покажете интервали на друг език (например английско съдържание в български интерфейс), задайте locale изрично. За разлика от DateFormatter, DateIntervalFormatter не изисква задаване на Locale за сървърни данни — винаги използва собствената си вътрешна логика за групиране, базирана на CLDR.
Езиковите разлики при форматирането на диапазони от дати са значително по-сложни, отколкото за отделни дати. Английският използва просто тире: Jul 21–25. Българският — предлог „от–до“: от 21 до 25 юли. Френският — „au“: du 21 au 25 juillet. Немският — точка след числото и тире: 21.–25. Juli. Японският — чрез тире без предлог: Jul 21–25. 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)
// Демонстрация на различни locale
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 трябва да се използва само за отделни дати, а не за слепване на две форматирани дати чрез конкатенация на низове — това води до загуба на локализация на разделителя.
Граничен случай: DateIntervalFormatter не поддържа показване на отворени диапазони (без крайна дата). Ако краят на диапазона е неизвестен (например „от 21 юли“), използвайте DateFormatter с предлог „от“ за български или „from“ за английски. DateIntervalFormatter също не поддържа диапазони с посочване на час за различни дни — часът се прилага към всяка дата поотделно, което може да изглежда излишно за кратки диапазони.
Често задавани въпроси
DateIntervalFormatter — клас Foundation за форматиране на диапазони от дати в локализирани низове. Достъпен от iOS 8, автоматично групира повтарящи се компоненти (месец, година) и избира правилния разделител за езика.
Сравнява компоненти от година до ден. Ако годината съвпада — показва се веднъж. Ако месецът съвпада — показва се веднъж. Различните дни се показват като диапазон. Това дава „21–25 юли 2024 г.“ вместо „21 юли 2024 г. – 25 юли 2024 г.“
Не, разделителят се определя от locale и не може да бъде конфигуриран чрез API. За персонализиран разделител (например „до“ вместо „–“), форматирайте всяка дата с отделен DateFormatter и слейте низовете ръчно.
DateIntervalFormatter форматира диапазон от дати (две дати) като едно цяло с групиране на компоненти. DateFormatter форматира една дата. За диапазони винаги използвайте DateIntervalFormatter — това осигурява правилна локализация на разделителя.
Автоматично — DateIntervalFormatter открива, че годините са различни и показва пълни дати за двата края: „25 декември 2024 г. – 5 януари 2025 г.“. Не са необходими допълнителни настройки, достатъчно е да зададете dateStyle.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също