Calendar — co to jest, kalendarz Date i obliczenia w Swift

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

Calendar — to klasa Foundation, która definiuje system kalendarzowy i dostarcza metod do obliczeń kalendarzowych: wyodrębnianie składników daty, obliczanie różnicy między datami, znajdowanie granic okresów i przesuwanie dat. Kalendarz łączy czas absolutny (Date) z czytelnymi dla człowieka składnikami i uwzględnia regionalne różnice: początek tygodnia, strefę czasową i czas letni. Według Apple Developer Documentation (2025), Foundation obsługuje 17 systemów kalendarzowych — od gregoriańskiego po buddyjski i japoński, co czyni Calendar uniwersalnym narzędziem dla zinternacjonalizowanych aplikacji.

Najważniejsze

  • Calendar — klasa Foundation do obliczeń kalendarzowych: wyodrębniania składników, porównywania i przesuwania dat.
  • Calendar.current — systemowy kalendarz użytkownika, automatycznie uwzględniający ustawienia regionalne.
  • 17 systemów kalendarzowych — Foundation obsługuje gregoriański, buddyjski, japoński, hebrajski, islamski i inne.
  • Calendar.dateComponents wyodrębnia składniki (rok, miesiąc, dzień) z Date z uwzględnieniem timezone.
  • Calendar.dateInterval zwraca początkową i końcową datę określonego okresu (dzień, tydzień, miesiąc).

Czym jest Calendar w Foundation?

Calendar — to klasa Foundation implementująca obliczenia kalendarzowe w oparciu o ICU (International Components for Unicode). Kalendarz określa, jak czas absolutny (Date) jest mapowany na składniki kalendarzowe: rok, miesiąc, dzień, godzinę, minutę, sekundę. Bez Calendar nie można określić, jaki jest dzisiaj rok, miesiąc i dzień — Date sam w sobie nie zawiera tych informacji.

Kalendarz uwzględnia trzy grupy parametrów: system kalendarzowy (gregoriański, buddyjski, japoński), strefę czasową i lokalizację. Calendar.current łączy wszystkie trzy z ustawień systemowych użytkownika. Calendar.autoupdatingCurrent — specjalna wersja automatycznie aktualizowana przy zmianie ustawień bez restartu aplikacji przez NotificationCenter.

Kalendarz jest typem wartościowym (value type) w Foundation. Calendar(identifier:) tworzy nową instancję ze stałymi parametrami. Calendar można kopiować, porównywać przez == i używać jako klucza w słowniku. Pozwala to tworzyć kalendarze z konkretnymi ustawieniami timeZone i locale do testowania.

Calendar i NSCalendar

Calendar — to Swiftowa wersja Objective-C NSCalendar, z mostem as Calendar / as NSCalendar. We współczesnym Swifcie wszędzie używa się Calendar. NSCalendar pozostaje dla wstecznej kompatybilności z Objective-C API. Calendar ma pełny zestaw metod bez prefiksu NS, z type-safe argumentami i Swiftową opcjonalnością.

Thread Safety — Calendar jest bezpieczny wątkowo do odczytu. Utworzoną instancję można bezpiecznie czytać z wielu wątków. Modyfikacja właściwości (timeZone, locale) nie jest bezpieczna wątkowo — dla różnych konfiguracji twórz różne instancje Calendar.

Typy kalendarzy w Foundation

Foundation obsługuje 17 systemów kalendarzowych przez wyliczenie Calendar.Identifier. Każdy system ma własne zasady lat przestępnych, liczbę miesięcy i początek ery. Wybór kalendarza wpływa na wszystkie obliczenia: dateComponents, dateInterval, nextDate.

Podstawowe systemy kalendarzowe:

  • .gregorian — międzynarodowy standard, 12 miesięcy, 365/366 dni, era od narodzenia Chrystusa.
  • .buddhist — kalendarz buddyjski, używany w Tajlandii, Kambodży, Laosie, era wyprzedza gregoriańską o 543 lata.
  • .japanese — kalendarz japoński według er panowania cesarzy, 12 miesięcy jak w gregoriańskim.
  • .hebrew — kalendarz hebrajski, 12/13 miesięcy, miesiąc przestępny Adar II.
  • .islamic — kalendarz islamski, 12 miesięcy księżycowych, 354/355 dni.
  • .indian — indyjski kalendarz narodowy (Saka), 12 miesięcy.

Calendar(identifier: .gregorian) — najczęściej używany. Odpowiada międzynarodowemu standardowi ISO 8601 i jest domyślnym kalendarzem w większości krajów. Dla aplikacji z międzynarodową publicznością używaj Calendar.current — automatycznie odpowiada systemowemu kalendarzowi użytkownika.

IdentyfikatorTypRegion użycia
.gregorianSłonecznyMiędzynarodowy
.buddhistSłonecznyTajlandia, Kambodża
.japaneseSłonecznyJaponia
.hebrewKsiężycowo-słonecznyIzrael
.islamicKsiężycowyKraje islamskie
.chineseKsiężycowo-słonecznyChiny

Calendar i DateComponents

DateComponents i Calendar — nierozłączna para. Calendar.dateComponents(_:from:) wyodrębnia składniki z Date z uwzględnieniem strefy czasowej kalendarza. Calendar.date(from:) składa Date z DateComponents, wypełniając brakujące pola wartościami domyślnymi: dzień = 1, godzina = 0, minuta = 0, sekunda = 0.

Metoda Calendar.component wyodrębnia jeden składnik, co jest wygodne do szybkiego sprawdzenia. Calendar.dateComponents wyodrębnia zestaw składników w jednym wywołaniu — jest to bardziej wydajne, ponieważ Calendar wykonuje obliczenia kalendarzowe raz, a nie dla każdego składnika osobno. Dla listy 3+ składników zawsze używaj dateComponents.

Calendar.compare porównuje dwie Date z zadaną dokładnością. Parametr toGranularity określa, do którego składnika wykonać porównanie: .year porównuje tylko rok, .month — rok i miesiąc, .day — rok, miesiąc, dzień. Jest to wygodne do sprawdzenia, czy dwie daty należą do tego samego dnia, bez uwzględniania czasu.

swift
let calendar = Calendar.current
let now = Date()

// Wyodrębnianie pojedynczego komponentu
let year = calendar.component(.year, from: now)

// Wyodrębnianie zestawu komponentów
let comps = calendar.dateComponents(
    [.year, .month, .day], from: now
)

// Porównanie z dokładnością do dnia
let isSameDay = calendar.compare(date1, to: date2,
                                  toGranularity: .day) == .orderedSame

// Sprawdź, czy data jest dzisiaj
let isToday = calendar.isDateInToday(someDate)

Calendar.isDateInToday, isDateInTomorrow, isDateInYesterday — metody do sprawdzeń względnych. Calendar.isDate(_:inSameDayAs:) sprawdza, czy dwie daty przypadają na ten sam dzień kalendarzowy z uwzględnieniem strefy czasowej kalendarza. Te metody używają Calendar.compare wewnętrznie i są zoptymalizowane do częstego wywoływania.

Obliczenia Calendar

Calendar.dateInterval — jedna z najbardziej przydatnych metod do analityki i UI. Zwraca DateInterval dla określonego składnika: początek i koniec dnia, tygodnia, miesiąca, roku. DateInterval zawiera start (Date) i end (Date) — granice okresu. Na przykład dateInterval(of: .weekOfYear, for: Date()) zwraca początek poniedziałku i koniec niedzieli bieżącego tygodnia.

Calendar.date z byAdding — metoda do przesuwania daty. Calendar.date(byAdding: .day, value: 7, to: Date()) zwraca datę za tydzień. Calendar.date(byAdding: DateComponents) — bardziej elastyczna wersja, pozwalająca przesunąć kilka składników naraz: +1 miesiąc +3 dni. Calendar automatycznie uwzględnia różną długość miesięcy i lata przestępne.

Calendar.nextDate szuka następnej daty pasującej do zadanych DateComponents. Parametr matchingPolicy określa zachowanie przy braku dopasowania: .nextTime — następne dopasowanie czasu, .nextTimePreservingSmallerComponents — zachowuje minuty i sekundy z oryginalnej daty, .strict — wymaga dokładnego dopasowania.

swift
let calendar = Calendar.current
let today = Date()

// Początek i koniec tygodnia
let weekInterval = calendar.dateInterval(
    of: .weekOfYear, for: today
)!

// Przesunięcie o 1 miesiąc
let nextMonth = calendar.date(
    byAdding: .month, value: 1, to: today
)!

// Przesunięcie przez DateComponents
var delta = DateComponents()
delta.month = 1
delta.day = 3
let shifted = calendar.date(byAdding: delta, to: today)!

// Następny piątek 13-tego
let friday13Components = DateComponents(
    weekday: 6, day: 13
)
let nextFriday13 = calendar.nextDate(
    after: today, matching: friday13Components,
    matchingPolicy: .nextTime
)

EnumerateDates — potężna metoda do iteracji dat według wzorca. Calendar.enumerateDates(startingAfter:matching:matchingPolicy:using:) wywołuje blok dla każdego dopasowania, dopóki blok nie zwróci stop = true. Używana do generowania powtarzających się wydarzeń w kalendarzach i harmonogramach. Metoda jest bardziej wydajna niż ręczna pętla z nextDate, ponieważ jest zoptymalizowana przez ICU.

TimeZone i Locale

TimeZone — nieodłączna część Calendar. Strefa czasowa określa, któremu czasowi kalendarzowemu odpowiada absolutny Date. Ten sam Date w UTC i w Moskwie daje różne składniki: Date() w UTC może pokazywać 10:00, a w MSK — 13:00. Calendar.timeZone domyślnie równa się TimeZone.current.

Locale wpływa na pierwszy dzień tygodnia, minimalną liczbę dni w pierwszym tygodniu roku (minDaysInFirstWeek) i nazwy miesięcy/dni tygodnia (przy konwersji przez DateFormatter). Calendar.locale domyślnie równa się Locale.current. W polskiej lokalizacji tydzień zaczyna się od poniedziałku, w amerykańskiej — od niedzieli.

Calendar.availableIdentifiers zwraca listę wszystkich obsługiwanych identyfikatorów kalendarzowych. static property Calendar.availableCalendarIdentifiers — tablica ciągów z tymi samymi identyfikatorami. Używane do budowania UI wyboru kalendarza i do sprawdzania dostępności konkretnego systemu kalendarzowego na urządzeniu.

swift
// Kalendarz z konkretną strefą czasową
var utcCalendar = Calendar(identifier: .gregorian)
utcCalendar.timeZone = TimeZone(identifier: "UTC")!

// Kalendarz z rosyjską lokalizacją
var russianCalendar = Calendar(identifier: .gregorian)
russianCalendar.locale = Locale(identifier: "ru_RU")

// Pierwszy dzień tygodnia zależy od lokalizacji
let firstWeekday = russianCalendar.firstWeekday
// 2 = poniedziałek (w pl_PL)

// Lista dostępnych kalendarzy
for identifier in Calendar.availableIdentifiers {
    print(identifier)
}

firstWeekday — właściwość Calendar określająca, który dzień tygodnia jest uważany za pierwszy. W polskiej lokalizacji Sunday = 2 (poniedziałek pierwszy). W amerykańskiej Sunday = 1. Wpływa to na działanie weekOfMonth i weekOfYear: ta sama data może być przypisana do różnych numerów tygodnia w różnych lokalizacjach. Dla aplikacji z datami używaj Calendar.current lub jawnie ustawiaj firstWeekday.

Przykłady pracy z Calendar

Rozważmy praktyczne scenariusze demonstrujące możliwości Calendar. Każdy przykład rozwiązuje konkretne zadanie iOS developmentu i pokazuje prawidłowy sposób użycia obliczeń kalendarzowych.

Sprawdzenie: data w tym miesiącu?

Calendar.dateInterval(of: .month, for:) zwraca granice bieżącego miesiąca. Sprawdzenie Date pod kątem przynależności do tego przedziału — najszybszy sposób określenia, czy data należy do bieżącego miesiąca. Alternatywny sposób — Calendar.compare z granularity .month: jeśli wynik .orderedSame, to miesiąc się zgadza.

swift
func isInCurrentMonth(_ date: Date) -> Bool {
    let calendar = Calendar.current
    let monthInterval = calendar.dateInterval(
        of: .month, for: Date()
    )!
    return monthInterval.contains(date)
}

// Liczba dni w miesiącu
func daysInMonth(for date: Date) -> Int {
    let calendar = Calendar.current
    return calendar.range(
        of: .day, in: .month, for: date
    )?.count ?? 0
}

// Dodawanie miesięcy z poprawnym zakończeniem
func addMonths(_ months: Int, to date: Date) -> Date {
    let calendar = Calendar.current
    return calendar.date(
        byAdding: .month, value: months, to: date
    )!
}

Calendar.range(of:in:for:) zwraca zakres dozwolonych wartości dla określonego składnika w kontekście innego składnika. Na przykład range(of: .day, in: .month, for: date) zwraca 1..<32 dla miesięcy z 31 dniami lub 1..<29 dla lutego roku nieprzestępnego. To prawidłowy sposób na poznanie liczby dni w miesiącu, a nie używanie hardkodowanych wartości.

Dodawanie miesięcy przez Calendar.date(byAdding:value:to:) poprawnie obsługuje daty graniczne. Jeśli 31 stycznia dodać 1 miesiąc, Calendar zwróci 28 lutego (lub 29 w roku przestępnym), a nie 3 marca, jak by się stało przy prostym dodaniu 30 dni przez TimeInterval. To kolejny powód, by nie używać TimeInterval do obliczeń kalendarzowych.

Metoda CalendarPrzeznaczeniePrzykład
dateIntervalGranice okresuPoczątek i koniec miesiąca
range(of:in:for:)Zakres składnikaDni w bieżącym miesiącu
date(byAdding:)Przesunięcie daty+1 miesiąc od dzisiaj
isDateInTodaySprawdzenie czy dzisiajCzy data jest dzisiejsza
compare(toGranularity:)Porównanie z dokładnościąCzy ten sam dzień bez czasu

Często zadawane pytania

Jaka jest różnica między Calendar.current a Calendar(identifier: .gregorian)?

Calendar.current zwraca kalendarz z ustawień systemowych użytkownika — może nie być gregoriański (na przykład buddyjski w Tajlandii). Calendar(identifier: .gregorian) zawsze tworzy kalendarz gregoriański niezależnie od ustawień. Do wyświetlania dat używaj Calendar.current, do logiki biznesowej — jawnie wybranego identyfikatora.

Dlaczego Calendar.date(byAdding: .month, value: 1) czasami zwraca tę samą datę?

To wynika z różnej długości miesięcy. Jeśli bieżąca data to 31 stycznia, dodanie 1 miesiąca daje 28 lutego, ponieważ w lutym nie ma 31 dni. Calendar automatycznie kończy datę na ostatnim dozwolonym dniu miesiąca. Do precyzyjnej kontroli używaj DateComponents z day: 1 do przejścia na pierwszy dzień miesiąca.

Jakiego kalendarza domyślnie używa DateFormatter?

DateFormatter używa Calendar.current — systemowego kalendarza użytkownika. Jeśli aplikacja zawsze ma pokazywać daty w kalendarzu gregoriańskim niezależnie od ustawień, ustaw formatter.calendar = Calendar(identifier: .gregorian). Gwarantuje to jednolite wyświetlanie dla wszystkich użytkowników.

Jak sprawdzić przez Calendar, czy rok jest przestępny?

Calendar.range(of: .day, in: .year, for: date) zwraca 365 lub 366 dni. Prościej: Calendar.date(from: DateComponents(year: rok, month: 2, day: 29)) != nil — jeśli 29 lutego istnieje, rok jest przestępny. Calendar sam uwzględnia zasady dla konkretnego systemu kalendarzowego.

Czy można zmienić firstWeekday po utworzeniu Calendar?

Tak, właściwość firstWeekday jest dostępna do zapisu. Zmiana wpływa na weekOfMonth, weekOfYear i wszystkie obliczenia związane z numerami tygodni. Przy ustawieniu locale = Locale(identifier: "pl_PL") firstWeekday automatycznie staje się 2 (poniedziałek). Ręczne ustawienie nadpisuje wartość z locale.

Podsumowanie

  • Calendar — klasa Foundation do obliczeń kalendarzowych, łącząca Date z czytelnymi dla człowieka składnikami przez DateComponents.
  • 17 systemów kalendarzowych obsługiwanych przez Foundation — od gregoriańskiego po buddyjski i japoński, z automatycznym uwzględnieniem regionalnych zasad.
  • Calendar.current odpowiada systemowemu kalendarzowi użytkownika — używaj go do UI i DateFormatter.
  • Calendar.dateInterval i Calendar.range — kluczowe metody do pracy z granicami okresów i zakresami składników.
  • Calendar.date(byAdding:) poprawnie obsługuje różną długość miesięcy i lata przestępne — nie zastępuj go arytmetyką TimeInterval.
  • TimeZone i Locale jako właściwości Calendar wpływają na wszystkie obliczenia kalendarzowe — ustawiaj je jawnie dla przewidywalnego zachowania.
  • firstWeekday jest określany przez lokalizację i wpływa na numery tygodni — dla globalnych aplikacji ustawiaj go jawnie lub używaj Calendar.current.

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ż