DateIntervalFormatter to klasa Foundation w iOS i macOS przeznaczona do formatowania zakresów dat w zlokalizowane ciągi znaków: „21–25 lipca 2024 r.", „od 21 do 25 lipca 2024 r." lub „Jul 21–25, 2024". Według Apple Developer Documentation, 2024, DateIntervalFormatter automatycznie określa, które składniki daty powtarzać (jeśli zakres mieści się w jednym miesiącu — miesiąc jest wyświetlany raz) i lokalizuje separatory oraz szyk wyrazów. W przeciwieństwie do ręcznego łączenia dwóch sformatowanych dat, ta klasa uwzględnia reguły gramatyczne języka: dla polskiego — przyimek „od–do", dla angielskiego — myślnik bez przyimka. Klasa dostępna od iOS 8 i macOS 10.10.
Najważniejsze
DateIntervalFormatter to podklasa Formatter w Foundation, która przyjmuje obiekt DateInterval (zakres między dwiema datami) i zwraca zlokalizowany ciąg znaków z opisem tego zakresu. Na przykład dla zakresu od 21 do 25 lipca 2024 roku wynik będzie „21–25 lipca 2024 r." dla pl_PL i „Jul 21–25, 2024" dla en_US. Klasa inteligentnie grupuje powtarzające się elementy: jeśli obie daty są w tym samym miesiącu, nazwa miesiąca wyświetlana jest raz.
Podstawową wartością DateIntervalFormatter jest automatyczna obsługa gramatyki zakresów. Podczas ręcznego formatowania programista musiałby porównywać miesiąc/rok początku i końca, wybierać separator (myślnik, „od–do", „to", „bis") i dbać o odmianę. DateIntervalFormatter robi to automatycznie na podstawie danych CLDR (Common Locale Data Repository), co zmniejsza liczbę błędów w aplikacjach międzynarodowych, szczególnie dla języków o złożonej gramatyce.
Według Unicode CLDR v44 (2024), reguły formatowania zakresów dat znacznie różnią się między językami. Niemiecki używa przyimka „bis" (21.–25. Juli 2024), francuski — „au" (du 21 au 25 juillet 2024), japoński — przez myślnik (2024 Jul 21–25). DateIntervalFormatter pokrywa wszystkie te warianty bez dodatkowego kodu po stronie programisty.
Zasada działania DateIntervalFormatter opiera się na analizie dwóch dat tworzących zakres i wyborze optymalnej reprezentacji tekstowej z uwzględnieniem locale. Klasa porównuje składniki dat (rok, miesiąc, dzień) i podejmuje decyzję o grupowaniu: jeśli rok jest taki sam — wyświetlany jest raz; jeśli miesiąc jest taki sam — wyświetlany jest raz; jeśli dni się różnią — wyświetlany jest zakres dni.
| Zakres | pl_PL | en_US |
|---|---|---|
| Jeden dzień | 21 lipca 2024 r. | Jul 21, 2024 |
| Wewnątrz miesiąca | 21–25 lipca 2024 r. | Jul 21–25, 2024 |
| Różne miesiące | 21 lipca – 5 sierpnia 2024 r. | Jul 21 – Aug 5, 2024 |
| Różne lata | 25 grudnia 2024 r. – 5 stycznia 2025 r. | Dec 25, 2024 – Jan 5, 2025 |
| Z czasem | 21 lipca 2024 r., 14:30–16:45 | Jul 21, 2024, 2:30–4:45 PM |
Logika grupowania jest zaimplementowana na podstawie wzorców CLDR. Formatter sprawdza równość każdego składnika daty (Calendar.Component: year, month, day) w kolejności od najstarszego do najmłodszego. Gdy składnik się różni, wszystkie młodsze składniki są wyświetlane dla obu dat. Na przykład dla 21–25 lipca: rok się zgadza, miesiąc się zgadza, dzień się różni → miesiąc wyświetlany jest raz, dni — dla każdej daty.
Ważny niuans: DateIntervalFormatter nie obsługuje ustawiania separatora przez API. Separator jest określany przez locale i nie może być nadpisany. Jeśli wymagany jest niestandardowy separator (na przykład „do" zamiast „–"), należy sformatować daty ręcznie przez DateFormatter i połączyć ciągi z niestandardowym separatorem.
DateIntervalFormatter udostępnia dwa zestawy stylów: dateStyle i timeStyle — dla każdego końca zakresu. Te style działają analogicznie do DateFormatter: .none ukrywa składnik, .short daje krótki format (21.07.2024), .medium — średni (21 lip 2024 r.), .long — pełny (21 lipca 2024 r.), .full — maksymalnie szczegółowy (niedziela, 21 lipca 2024 r.). Łącząc dateStyle i timeStyle, można uzyskać różne poziomy szczegółowości.
Ważne: dateStyle i timeStyle są stosowane do obu końców zakresu jednakowo. Nie można ustawić różnych stylów dla początku i końca (na przykład .medium dla początku i .short dla końca). Jeśli wymagany jest inny styl, należy sformatować każdą datę oddzielnym DateFormatter i połączyć wyniki.
import Foundation
let formatter = DateIntervalFormatter()
// Różne kombinacje stylów
let startDate = Date()
let endDate = Date().addingTimeInterval(345600) // +4 dni
let interval = DateInterval(start: startDate, end: endDate)
// Tylko data, format średni
formatter.dateStyle = .medium
formatter.timeStyle = .none
print("Tylko data: \(formatter.string(from: interval))")
// Krótki format z czasem
formatter.dateStyle = .short
formatter.timeStyle = .short
print("Krótki z czasem: \(formatter.string(from: interval))")
// Pełny format
formatter.dateStyle = .full
formatter.timeStyle = .none
print("Pełna data: \(formatter.string(from: interval))")
Podstawowe użycie DateIntervalFormatter obejmuje utworzenie instancji, skonfigurowanie stylów i wywołanie string(from:). Metoda przyjmuje DateInterval — strukturę z właściwościami start i end. DateInterval można utworzyć zarówno z dwóch Date, jak i zainicjalizować z Duration (iOS 16+). Po sformatowaniu wynik jest gotowy do wyświetlenia w UI bez dodatkowego przetwarzania.
import Foundation
let calendar = Calendar.current
let now = Date()
// Przykłady konkretnych interwałów
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)")
}
// Formatowanie przez Calendar (iOS 16+)
if let nextWeek = calendar.date(
byAdding: .day, value: 7, to: now
) {
let weekInterval = DateInterval(start: now, end: nextWeek)
print("Następne 7 dni: \(intervalFormatter.string(from: weekInterval))")
}
DateIntervalFormatter i Calendar Booking: w aplikacjach rezerwacyjnych (hotele, bilety) zakresy dat są kluczowym elementem UI. DateIntervalFormatter poprawnie obsługuje zakresy międzyroczne (na przykład od 28 grudnia do 3 stycznia), grupując miesiąc i rok dla każdej daty osobno. W takich scenariuszach zaleca się używanie dateStyle = .medium i timeStyle = .none — najbardziej czytelny format dla dat bez czasu.
import Foundation
let formatter = DateIntervalFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .none
// Przykłady rezerwacji
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("Rezerwacja: \(booking.formattedInterval)")
// Użycie z różnymi locale
let enFormatter = DateIntervalFormatter()
enFormatter.dateStyle = .medium
enFormatter.timeStyle = .none
enFormatter.locale = Locale(identifier: "en_US")
print("Angielski: \(enFormatter.string(from: interval))")
DateInterval vs DateIntervalFormatter: DateInterval — struktura reprezentująca zakres dat (początek i koniec). DateIntervalFormatter — formatter przekształcający tę strukturę w ciąg znaków. DateInterval obsługuje również sprawdzanie contains(Date) i intersection(with:), co jest przydatne w logice rezerwacji (sprawdzanie nakładania się dat).
DateIntervalFormatter lokalizuje wyjście poprzez właściwość locale. Domyślnie używany jest Locale.current — locale urządzenia. Aby wyświetlić zakresy w innym języku (na przykład treść po angielsku przy polskim interfejsie), ustaw locale jawnie. W przeciwieństwie do DateFormatter, DateIntervalFormatter nie wymaga ustawiania Locale dla danych serwerowych — zawsze używa swojej wewnętrznej logiki grupowania opartej na CLDR.
Różnice językowe w formatowaniu zakresów dat są znacznie bardziej złożone niż dla pojedynczych dat. Angielski używa prostego myślnika: Jul 21–25. Polski — przyimka „od–do": od 21 do 25 lipca. Francuski — przyimka „au": du 21 au 25 juillet. Niemiecki — kropki po liczbie i myślnika: 21.–25. Juli. Japoński — przez myślnik bez przyimków: Jul 21–25. DateIntervalFormatter pokrywa wszystkie te warianty.
import Foundation
let formatter = DateIntervalFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
let start = Date()
let end = Date().addingTimeInterval(259200) // +3 dni
let interval = DateInterval(start: start, end: end)
// Demonstracja różnych 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))")
}
// Lokalizacja dla interwałów czasu
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) czas: \(formatter.string(from: timeInterval))")
}
Specyfika lokalizacji: dla języka polskiego DateIntervalFormatter używa przyimków „od" i „do" dla długich formatów daty. Dla krótkich (short) — tylko myślnika: 21.07–25.07.2024. Dla pełnego formatu (full) — z podaniem dnia tygodnia. Wybór formatu zależy od kontekstu: dla listy rezerwacji używaj .medium, dla szczegółowego widoku — .long lub .full.
DateIntervalFormatter zajmuje niszę formatowania zakresów dat, której nie pokrywają inne klasy Foundation. DateFormatter formatuje pojedyncze daty, RelativeDateTimeFormatter — względne daty (temu/za), DateComponentsFormatter — czas trwania (2 godziny 30 minut). Każda z tych klas rozwiązuje własne zadanie i nie można ich zamiennie stosować.
| Klasa | Przeznaczenie | Przykład wyjścia |
|---|---|---|
| DateIntervalFormatter | Zakres dat | 21–25 lipca 2024 r. |
| DateFormatter | Pojedyncza data | 21 lipca 2024 r. |
| RelativeDateTimeFormatter | Czas względny | 3 dni temu |
| DateComponentsFormatter | Czas trwania | 2 godziny 30 minut |
| ISO8601DateFormatter | Format ISO 8601 | 2024-07-21T14:30:00Z |
Kiedy używać DateIntervalFormatter: do wyświetlania zakresów dat w UI — rezerwacja hoteli, wydarzeń, filtrowanie po datach, historia operacji. Do obliczania czasu trwania między datami używaj DateComponentsFormatter, a nie DateIntervalFormatter — drugi nie jest przeznaczony do wyświetlania czasu trwania. DateFormatter należy używać tylko dla pojedynczych dat, a nie do łączenia dwóch sformatowanych dat przez konkatenację ciągów — prowadzi to do utraty lokalizacji separatora.
Edge case: DateIntervalFormatter nie obsługuje wyświetlania zakresów otwartych (bez daty końcowej). Jeśli koniec zakresu jest nieznany (na przykład „od 21 lipca"), używaj DateFormatter z przyimkiem „od" dla języka polskiego lub „from" dla angielskiego. DateIntervalFormatter nie obsługuje również zakresów z podaniem czasu dla różnych dni — czas jest stosowany do każdej daty osobno, co może wyglądać nadmiarowo dla krótkich zakresów.
Często zadawane pytania
DateIntervalFormatter — klasa Foundation do formatowania zakresów dat w zlokalizowane ciągi znaków. Dostępna od iOS 8, automatycznie grupuje powtarzające się składniki (miesiąc, rok) i wybiera odpowiedni separator dla języka.
Porównuje składniki od roku do dnia. Jeśli rok się zgadza — wyświetlany jest raz. Jeśli miesiąc się zgadza — wyświetlany jest raz. Różniące się dni są wyświetlane jako zakres. Daje to „21–25 lipca 2024" zamiast „21 lipca 2024 – 25 lipca 2024".
Nie, separator jest określany przez locale i nie można go skonfigurować przez API. Dla niestandardowego separatora (na przykład „do" zamiast „–") formatuj każdą datę oddzielnym DateFormatter i łącz ciągi ręcznie.
DateIntervalFormatter formatuje zakres dat (dwie daty) jako jedną całość z grupowaniem składników. DateFormatter formatuje pojedynczą datę. Dla zakresów zawsze używaj DateIntervalFormatter — daje to poprawną lokalizację separatora.
Automatycznie — DateIntervalFormatter określa, że lata są różne, i wyświetla pełne daty dla obu końców: „25 grudnia 2024 r. – 5 stycznia 2025 r.". Nie są wymagane żadne dodatkowe ustawienia, wystarczy ustawić dateStyle.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również