DateIntervalFormatter — podstawy, formatowanie interwałów dat

Autor: IT Sectr Opublikowano: 2026-07-13 Czas czytania: 11 min

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 — klasa Foundation do formatowania zakresów dat (iOS 8+)
  • Automatyczne grupowanie — powtarzające się składniki (miesiąc, rok) wyświetlane są raz
  • Zlokalizowane separatory — uwzględnienie reguł gramatycznych: „od–do" (polski), myślnik (angielski)
  • Trzy style daty — .none, .short, .medium, .long, .full dla każdego końca zakresu
  • Cztery style czasu — te same poziomy szczegółowości dla części czasowej zakresu

Czym jest DateIntervalFormatter?

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.

Jak działa DateIntervalFormatter w Foundation?

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.

Zakrespl_PLen_US
Jeden dzień21 lipca 2024 r.Jul 21, 2024
Wewnątrz miesiąca21–25 lipca 2024 r.Jul 21–25, 2024
Różne miesiące21 lipca – 5 sierpnia 2024 r.Jul 21 – Aug 5, 2024
Różne lata25 grudnia 2024 r. – 5 stycznia 2025 r.Dec 25, 2024 – Jan 5, 2025
Z czasem21 lipca 2024 r., 14:30–16:45Jul 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.

Style formatowania interwałów

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.

Przykłady kombinacji stylów

  • dateStyle = .medium, timeStyle = .none — tylko data: „21–25 lipca 2024 r."
  • dateStyle = .short, timeStyle = .short — data i czas: „21.07.2024, 14:30 – 25.07.2024, 16:45"
  • dateStyle = .none, timeStyle = .short — tylko czas: „14:30–16:45" (jeśli daty są takie same)
  • dateStyle = .long, timeStyle = .none — długa data: „21–25 lipca 2024 roku"
  • dateStyle = .full, timeStyle = .short — pełny format: „niedziela, 21 lipca 2024 r. – czwartek, 25 lipca 2024 r."

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.

swift
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))")

DateIntervalFormatter w Swift: przykłady

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.

swift
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.

swift
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).

Lokalizacja interwałów 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.

swift
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.

Porównanie z innymi formaterami dat

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ć.

KlasaPrzeznaczeniePrzykład wyjścia
DateIntervalFormatterZakres dat21–25 lipca 2024 r.
DateFormatterPojedyncza data21 lipca 2024 r.
RelativeDateTimeFormatterCzas względny3 dni temu
DateComponentsFormatterCzas trwania2 godziny 30 minut
ISO8601DateFormatterFormat ISO 86012024-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

Czym jest DateIntervalFormatter?

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.

Jak DateIntervalFormatter grupuje daty?

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".

Czy można zmienić separator zakresu?

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.

Czym DateIntervalFormatter różni się od DateFormatter?

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.

Jak sformatować zakres z różnymi latami?

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

  • DateIntervalFormatter — wyspecjalizowana klasa do formatowania zakresów dat (iOS 8+)
  • Automatyczne grupowanie — powtarzające się składniki (rok, miesiąc) wyświetlane są raz dla całego zakresu
  • Zlokalizowane separatory — „od–do" (polski), myślnik (angielski), „bis" (niemiecki) — automatycznie
  • Style daty i czasu — kombinacje .none, .short, .medium, .long, .full dla każdego końca zakresu
  • Nie nadaje się do czasu trwania — do obliczania czasu trwania używaj DateComponentsFormatter
  • Nie obsługuje niestandardowych separatorów — dla niestandardowych przypadków wymagane jest ręczne formatowanie
  • Integracja z DateInterval — struktura DateInterval udostępnia sprawdzanie contains i intersection

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.

Omów projekt

Przeczytaj również