DateComponents – co to jest, komponenty kalendarza i NSCalendar

Autor: IT Sectr Opublikowano: 2026-07-12 Czas czytania: 7 min

DateComponents to struktura Foundation, która przechowuje komponenty daty kalendarzowej jako osobne pola: rok, miesiąc, dzień, godzina, minuta, sekunda i inne. W przeciwieństwie do Date, reprezentującego absolutny moment w czasie, DateComponents zawiera wartości czytelne dla człowieka, zależne od kalendarza i strefy czasowej. Według Apple Developer Documentation (2025), DateComponents jest używana jako pośrednie ogniwo między Date i Calendar – za jej pomocą wyodrębnia się i konstruuje daty kalendarzowe, wykonuje obliczenia i przesunięcia dat bez ręcznej arytmetyki.

Najważniejsze

  • DateComponents – struktura do przechowywania komponentów daty (rok, miesiąc, dzień) jako opcjonalnych pól całkowitoliczbowych.
  • Calendar.dateComponents – metoda wyodrębniająca wskazane komponenty z Date z uwzględnieniem strefy czasowej.
  • Calendar.date(from:) – odwrotna konwersja DateComponents na Date z automatycznym uzupełnianiem brakujących pól.
  • Pola opcjonalne – każde pole DateComponents może być nil, co pozwala na określanie niepełnych dat.
  • Range i komponenty – DateComponents jest używana w Calendar do obliczania różnicy między datami i wyszukiwania dat w zakresie.

Czym są DateComponents?

DateComponents to typ wartościowy Foundation przeznaczony do przechowywania kalendarzowych komponentów czasu. Każdy komponent jest reprezentowany jako opcjonalne pole Int: year, month, day, hour, minute, second, nanosecond, weekday, weekOfMonth, weekOfYear, quarter, yearForWeekOfYear i inne.

Główna różnica w porównaniu z Date to powiązanie z kalendarzem. Date przechowuje absolutny czas (liczbę sekund od reference date), a DateComponents – czytelną dla człowieka reprezentację, która ma sens tylko w kontekście konkretnego Calendar. Ten sam Date może być reprezentowany przez różne DateComponents w różnych kalendarzach i strefach czasowych.

DateComponents to nie samodzielny typ czasu, a kontener danych. Do interpretacji DateComponents jako daty wymagany jest Calendar, który rozumie, jak komponenty odnoszą się do systemu kalendarzowego. Calendar.dateComponents(from: Date) wykonuje wyodrębnianie komponentów, Calendar.date(from: DateComponents) – odwrotne składanie.

Opcjonalność pól

Każde pole DateComponents jest opcjonalne (Int?), co ma zasadnicze znaczenie dla pracy z niepełnymi datami. Jeśli podasz tylko rok i miesiąc, Calendar uzupełni brakujące polami wartościami domyślnymi: dzień = 1, godzina = 0, minuta = 0. Jest to wygodne do tworzenia dat początku okresu – wystarczy określić tylko interesujące komponenty.

Przy porównywaniu DateComponents operatorem == porównywane są tylko określone (nie nil) pola. Dwie struktury DateComponents z rokiem 2026, ale różnymi miesiącami, są uznawane za różne. isEqual z NSObjectProtocol nie jest używane dla DateComponents – DateComponents nie dziedziczy po NSObject.

Komponenty daty: rok, miesiąc, dzień

Podstawowe pola DateComponents obejmują year, month, day, hour, minute, second, nanosecond. Każde pole przechowuje wartość liczbową w odpowiedniej jednostce: rok – 2026, miesiąc – 1..12, dzień – 1..31, godzina – 0..23, minuta – 0..59, sekunda – 0..59. Nanosekundy mogą przyjmować wartości 0..999999999.

Pola tygodnia – weekday (1..7, gdzie 1 = niedziela w kalendarzu gregoriańskim), weekOfMonth, weekOfYear. Te pola zależą od Calendar i nie mają sensu poza jego kontekstem. weekday zależy od ustawienia firstWeekday kalendarza: w polskiej lokalizacji tydzień zaczyna się od poniedziałku (weekday = 2 w systemie gregoriańskim), a w amerykańskiej – od niedzieli (weekday = 1).

Pola specjalistyczne – quarter (1..4), yearForWeekOfYear (rok, do którego należy tydzień), isLeapMonth (flaga logiczna dla miesięcy przestępnych w kalendarzu hebrajskim lub chińskim). Pola calendar i timeZone przechowują odniesienia do odpowiednich obiektów, z którymi struktura została utworzona.

KategoriaPolaZakres
Kalendarzoweyear, month, day1..∞, 1..12, 1..31
Czasowehour, minute, second, nanosecond0..23, 0..59, 0..59, 0..999999999
Tygodnioweweekday, weekOfMonth, weekOfYear1..7, 1..5, 1..53
Specjalnequarter, yearForWeekOfYear1..4, zależne

Przy wyodrębnianiu komponentów przez Calendar.dateComponents ważne jest, aby żądać tylko potrzebnych pól dla wydajności. Calendar wyodrębnia wszystkie żądane pola w jednym przebiegu – jest to znacznie szybsze niż wywoływanie Calendar.component dla każdego pola osobno.

Tworzenie DateComponents

Inicjalizacja DateComponents – najprostszy sposób: tworzysz pustą strukturę i wypełniasz potrzebne pola. Wszystkie nieokreślone pola automatycznie otrzymują nil. Data utworzona z częściowych komponentów nie jest walidowana na etapie inicjalizacji – błąd może wystąpić dopiero przy konwersji na Date przez Calendar.

Inicjalizator DateComponents(calendar:timeZone:era:year:month:day:hour:minute:second:nanosecond:weekday:…) pozwala ustawić wszystkie pola w jednym wywołaniu. Ten inicjalizator jest wygodny do tworzenia pełnej daty z gotowych wartości, ale rzadko używany z więcej niż 5-6 argumentami ze względu na czytelność.

Calendar.dateComponents(_:from:) – podstawowy sposób uzyskiwania DateComponents z istniejącego Date. Drugi argument to zestaw składowych, które należy wyodrębnić. Calendar wykonuje obliczenia kalendarzowe z uwzględnieniem strefy czasowej i zwraca strukturę tylko z żądanymi polami, pozostałe pola pozostają nil.

swift
import Foundation

// Tworzenie przez inicjalizator pól
var components = DateComponents()
components.year = 2026
components.month = 7
components.day = 21

// Wyodrębnianie z Date
let now = Date()
let extracted = Calendar.current.dateComponents(
    [.year, .month, .day],
    from: now
)
print("Today: \(extracted.day!).\(extracted.month!).\(extracted.year!)")

// Tworzenie przez rozszerzony inicjalizator
let birthday = DateComponents(
    calendar: Calendar.current,
    year: 1990, month: 5, day: 15
)

Przy tworzeniu DateComponents przez ręczne pola zawsze sprawdzaj Calendar przed konwersją na Date. Calendar przy konwersji date(from:) może zwrócić nil, jeśli komponenty tworzą nieistniejącą datę – na przykład 31 lutego lub 30 lutego w roku nieprzestępnym. Walidacja daty jest odpowiedzialnością Calendar, nie DateComponents.

Konwersja DateComponents na Date

Calendar.date(from:) – podstawowa metoda konwersji DateComponents na Date. Calendar interpretuje komponenty zgodnie ze swoim kalendarzem i strefą czasową. Jeśli jakieś pola nie są ustawione (nil), Calendar używa wartości domyślnych: dzień = 1, godzina = 0, minuta = 0, sekunda = 0.

Metoda zwraca opcjonalny Date – nil występuje, gdy komponenty są ze sobą sprzeczne lub tworzą nieprawidłową datę. Typowe przyczyny nil: nieistniejąca data (32 stycznia, 29 lutego 2023), sprzeczne pola (weekday=1, day=5 w jednym zestawie), niemożliwy rok dla danego kalendarza (rok 0 w kalendarzu gregoriańskim).

DateComponents z timeZone – jeśli DateComponents zawiera timeZone, Calendar używa go przy konwersji. Jeśli timeZone nie jest określona, Calendar używa swojej bieżącej timeZone. Jeśli Calendar.timeZone nie odpowiada oczekiwanej strefie czasowej daty, wynik może różnić się o kilka godzin – upewnij się, że timeZone jest jawnie ustawiona w jednym z obiektów.

swift
let calendar = Calendar(identifier: .gregorian)

// Tworzenie Date z DateComponents
var comps = DateComponents()
comps.year = 2026
comps.month = 12
comps.day = 25
comps.hour = 10

if let date = calendar.date(from: comps) {
    print("Christmas: \(date)")
}

// Tworzenie z określeniem timeZone
calendar.timeZone = TimeZone(identifier: "UTC")!
let utcComps = DateComponents(
    calendar: calendar, year: 2026, month: 7, day: 21,
    hour: 12
)
let utcDate = calendar.date(from: utcComps)!

Calendar.dateComponents dla różnicy dat – kolejny scenariusz użycia DateComponents. Calendar.dateComponents([.year, .month, .day], from: Date(), to: futureDate) zwraca różnicę w latach, miesiącach i dniach między dwiema datami. Jest to prawidłowy sposób obliczania wieku zamiast dzielenia TimeInterval przez liczbę sekund w roku, ponieważ Calendar uwzględnia lata przestępne.

Calendar i DateComponents

Calendar – centralna klasa, która pracuje z DateComponents. Wszystkie operacje wyodrębniania, składania i porównywania dat przechodzą przez Calendar. Bez Calendar DateComponents to tylko zbiór liczb nie mający znaczenia czasowego. Calendar nadaje komponentom interpretację: określa, że miesiąc 2 to luty, a weekday 2 to poniedziałek.

Calendar.nextDate i Calendar.enumerateDates – dwie metody oparte na DateComponents. nextDate(after: Date(), matching: DateComponents) znajduje następną datę odpowiadającą określonym komponentom – na przykład następny poniedziałek po dzisiejszym. enumerateDates(startingAfter:matching:matchingPolicy:using:) iteruje wszystkie daty pasujące do wzorca do określonego limitu.

Calendar.dateInterval – metoda zwracająca DateInterval dla określonego komponentu. dateInterval(of: .month, for: Date()) zwraca początek i koniec bieżącego miesiąca. Wewnętrznie ta metoda używa DateComponents do znalezienia granic okresu: tworzy DateComponents z pierwszym i ostatnim dniem miesiąca, konwertuje je na Date przez Calendar.

swift
let calendar = Calendar.current

// Następny poniedziałek
let nextMonday = calendar.nextDate(
    after: Date(),
    matching: DateComponents(weekday: 2),
    matchingPolicy: .nextTime
)!

// Różnica między datami w dniach
let diff = calendar.dateComponents(
    [.day], from: Date(), to: nextMonday
)

// Zakres miesiąca
let monthInterval = calendar.dateInterval(
    of: .month, for: Date()
)!
let startOfMonth = monthInterval.start
let endOfMonth = monthInterval.end

MatchingPolicy – ważny parametr metod Calendar przy pracy z DateComponents. strictPolicy wymaga dokładnego dopasowania wszystkich komponentów, nextTimePolicy wybiera następne w czasie dopasowanie, nextTimePreservingSmallerComponents zachowuje mniejsze komponenty (minuty, sekundy) z oryginalnej daty. Wybór polityki wpływa na wynik wyszukiwania dat, szczególnie przy przesunięciu przez zmianę czasu na letni/zimowy.

Przykłady DateComponents

Rozważmy praktyczne scenariusze zastosowania DateComponents w aplikacji. Każdy przykład demonstruje typowe zadanie, z którym spotyka się programista iOS podczas pracy z datami kalendarzowymi.

Przypomnienie na pierwszy dzień każdego miesiąca

Calendar.nextDate z DateComponents(day: 1) znajduje pierwszy dzień następnego miesiąca. Calendar automatycznie określa liczbę dni w bieżącym miesiącu i przechodzi do następnego. Do powtarzających się powiadomień używaj enumerateDates lub Combine.Timer z kluczem Calendar.

swift
func firstDayOfNextMonth(from date: Date) -> Date {
    let calendar = Calendar.current
    let comps = DateComponents(day: 1)
    return calendar.nextDate(
        after: date,
        matching: comps,
        matchingPolicy: .nextTime
    )!
}

// Obliczanie wieku w latach
func ageInYears(from birthDate: Date) -> Int {
    let calendar = Calendar.current
    let ageComponents = calendar.dateComponents(
        [.year], from: birthDate, to: Date()
    )
    return ageComponents.year ?? 0
}

// Grupowanie zdarzeń według roku i miesiąca
func groupEventsByMonth(_ events: [Event]) -> [String: [Event]] {
    let calendar = Calendar.current
    return Dictionary(grouping: events) { event in
        let comps = calendar.dateComponents(
            [.year, .month], from: event.date
        )
        return "\(comps.year!)-\(comps.month!)"
    }
}

Obliczanie wieku przez Calendar.dateComponents([.year], from:to:) – jedyny prawidłowy sposób uwzględniający lata przestępne. Obliczenia oparte na TimeInterval (sekundy / 31536000) dają błąd dla osób urodzonych 29 lutego. Calendar poprawnie określa, czy urodziny były w bieżącym roku, i zwraca dokładny wiek.

Grupowanie według roku i miesiąca – częste zadanie dla ekranów z historią lub kalendarzem. DateComponents służy jako klucz grupowania: wyodrębniasz rok i miesiąc z daty zdarzenia, tworzysz klucz w postaci stringa i grupujesz przez Dictionary(grouping:). Do wyświetlania używaj DateFormatter z szablonem „LLLL yyyy” dla zlokalizowanej nazwy miesiąca.

ZadanieMetoda CalendarRola DateComponents
Pierwszy dzień miesiącanextDate(after:matching:)day: 1
Obliczanie wiekudateComponents(from:to:)[.year] z różnicy
Grupowanie datdateComponents(_:from:)year + month klucz
Wyszukiwanie dnia tygodnianextDate(after:matching:)weekday: N

Często zadawane pytania

Dlaczego Calendar.date(from:) zwraca nil dla DateComponents?

Przyczyny: nieistniejąca data (31 kwietnia), sprzeczne pola (weekday=1 z day=5), nieprawidłowa kombinacja pól dla wybranego kalendarza. Calendar próbuje zinterpretować komponenty w swoim systemie – jeśli kombinacja jest niemożliwa, wynik to nil. Zawsze używaj guard let lub if let przy konwersji.

Czy można porównywać DateComponents między sobą?

Tak, przez operator ==. DateComponents implementuje Equatable, porównując wszystkie pola. Dwie struktury są równe, jeśli wszystkie ich pola są równe (nil == nil jest uznawane za prawdę). Do porównania tylko części pól – wyodrębnij ten sam zestaw przez Calendar.dateComponents.

Czym DateComponents różni się od Date?

Date – absolutny moment w czasie bez powiązania z kalendarzem. DateComponents – zestaw czytelnych dla człowieka liczb (rok, miesiąc, dzień), które mają sens tylko w kontekście Calendar. Date można porównać, odjąć, serializować do ISO 8601. DateComponents – reprezentacja pośrednia do interakcji z kalendarzem.

Jak określić tylko rok i miesiąc w DateComponents?

Ustaw tylko pola year i month, pozostawiając pozostałe jako nil. Przy konwersji na Date przez Calendar.date(from:) Calendar automatycznie ustawi dzień = 1, godzina = 0, minuta = 0. Wynik – Date odpowiadający pierwszemu dniowi określonego miesiąca o północy.

Jak DateComponents obsługuje strefy czasowe?

DateComponents nie przechowuje informacji o strefie czasowej w polach – wartości pól (rok, miesiąc, dzień) same zależą od timeZone, w której zostały wyodrębnione. Komponenty „21 lipca 2026 14:00 MSK” i „21 lipca 2026 10:00 UTC” reprezentują tę samą datę Date, ale pola DateComponents są różne.

Podsumowanie

  • DateComponents – struktura Foundation do przechowywania komponentów kalendarzowych (rok, miesiąc, dzień, godzina) jako opcjonalnych pól Int?.
  • Calendar.dateComponents wyodrębnia komponenty z Date z uwzględnieniem strefy czasowej i systemu kalendarzowego.
  • Calendar.date(from:) składa Date z DateComponents, używając wartości domyślnych dla brakujących pól.
  • Opcjonalność pól pozwala na określanie niepełnych dat – Calendar uzupełnia brakujące wartości.
  • Calendar.nextDate wyszukuje następną datę odpowiadającą DateComponents – dla przypomnień i powtarzających się zdarzeń.
  • Obliczanie wieku przez Calendar.dateComponents([.year], from:to:) – jedyny prawidłowy sposób uwzględniający lata przestępne.
  • MatchingPolicy kontroluje zachowanie Calendar przy niedopasowaniu wszystkich komponentów – ważny parametr do wyszukiwania dat.

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ż