Calendar — co to je, kalendář Date a výpočty ve Swift

Autor: IT Sectr Publikováno: 2026-07-12 Doba čtení: 7 min

Calendar — je třída Foundation, která definuje kalendářní systém a poskytuje metody pro kalendářní výpočty: extrahování součástí data, výpočet rozdílu mezi daty, hledání hranic období a posouvání dat. Kalendář propojuje absolutní čas (Date) s lidsky čitelnými součástmi a zohledňuje regionální specifika: začátek týdne, časové pásmo a letní čas. Podle Apple Developer Documentation (2025) Foundation podporuje 17 kalendářních systémů — od gregoriánského po buddhistický a japonský, což činí Calendar univerzálním nástrojem pro internacionalizované aplikace.

Hlavní body

  • Calendar — třída Foundation pro kalendářní výpočty: extrakce součástí, porovnávání a posouvání dat.
  • Calendar.current — systémový kalendář uživatele, automaticky zohledňující regionální nastavení.
  • 17 kalendářních systémů — Foundation podporuje gregoriánský, buddhistický, japonský, hebrejský, islámský a další.
  • Calendar.dateComponents extrahuje součásti (rok, měsíc, den) z Date s ohledem na časové pásmo.
  • Calendar.dateInterval vrací počáteční a koncové datum zadaného období (den, týden, měsíc).

Co je Calendar ve Foundation?

Calendar — je třída Foundation implementující kalendářní výpočty založené na ICU (International Components for Unicode). Kalendář určuje, jak je absolutní čas (Date) mapován na kalendářní součásti: rok, měsíc, den, hodina, minuta, sekunda. Bez Calendar nelze zjistit, jaký je dnes rok, měsíc a den — Date sám o sobě tyto informace neobsahuje.

Kalendář zohledňuje tři skupiny parametrů: kalendářní systém (gregoriánský, buddhistický, japonský), časové pásmo a locale. Calendar.current kombinuje všechny tři ze systémových nastavení uživatele. Calendar.autoupdatingCurrent — speciální verze, která se automaticky aktualizuje při změně nastavení bez restartu aplikace prostřednictvím NotificationCenter.

Kalendář je hodnotový typ (value type) ve Foundation. Calendar(identifier:) vytváří novou instanci s pevnými parametry. Kalendář lze kopírovat, porovnávat pomocí == a používat jako klíč ve slovníku. To umožňuje vytvářet kalendáře s konkrétním nastavením timeZone a locale pro testování.

Calendar a NSCalendar

Calendar — Swift verze Objective-C NSCalendar, s můstkem as Calendar / as NSCalendar. V moderním Swiftu se všude používá Calendar. NSCalendar zůstává pro zpětnou kompatibilitu s Objective-C API. Calendar má plnou sadu metod bez předpony NS, s type-safe argumenty a Swift opcionalitou.

Thread Safety — Calendar je thread-safe pro čtení. Vytvořenou instanci lze bezpečně číst z více vláken. Úprava vlastností (timeZone, locale) není thread-safe — pro různé konfigurace vytvářejte samostatné instance Calendar.

Typy kalendářů ve Foundation

Foundation podporuje 17 kalendářních systémů prostřednictvím výčtu Calendar.Identifier. Každý systém má svá vlastní pravidla pro přestupné roky, počet měsíců a začátek éry. Volba kalendáře ovlivňuje všechny výpočty: dateComponents, dateInterval, nextDate.

Hlavní kalendářní systémy:

  • .gregorian — mezinárodní standard, 12 měsíců, 365/366 dní, éra od narození Krista.
  • .buddhist — buddhistický kalendář, používaný v Thajsku, Kambodži, Laosu, éra předbíhá gregoriánskou o 543 let.
  • .japanese — japonský kalendář podle éry vlády císařů, 12 měsíců jako v gregoriánském.
  • .hebrew — hebrejský kalendář, 12/13 měsíců, přestupný měsíc Adar II.
  • .islamic — islámský kalendář, 12 lunárních měsíců, 354/355 dní.
  • .indian — indický národní kalendář (Saka), 12 měsíců.

Calendar(identifier: .gregorian) — nejčastěji používaný. Odpovídá mezinárodnímu standardu ISO 8601 a je výchozím kalendářem ve většině zemí. Pro aplikace s mezinárodním publikem používejte Calendar.current — automaticky odpovídá systémovému kalendáři uživatele.

IdentifikátorTypOblast použití
.gregorianSlunečníMezinárodní
.buddhistSlunečníThajsko, Kambodža
.japaneseSlunečníJaponsko
.hebrewLunárně-slunečníIzrael
.islamicLunárníIslámské země
.chineseLunárně-slunečníČína

Calendar a DateComponents

DateComponents a Calendar — nerozlučná dvojice. Calendar.dateComponents(_:from:) extrahuje součásti z Date s ohledem na časové pásmo kalendáře. Calendar.date(from:) sestavuje Date z DateComponents a vyplňuje chybějící pole výchozími hodnotami: den = 1, hodina = 0, minuta = 0, sekunda = 0.

Metoda Calendar.component extrahuje jednu součást, což je vhodné pro rychlou kontrolu. Calendar.dateComponents extrahuje sadu součástí v jednom volání — je to efektivnější, protože Calendar provádí kalendářní výpočty jednou, ne pro každou součást zvlášť. Pro seznam 3+ součástí vždy používejte dateComponents.

Calendar.compare porovnává dvě Date se zadanou přesností. Parametr toGranularity určuje, do které součásti se porovnání provádí: .year porovnává pouze rok, .month — rok a měsíc, .day — rok, měsíc, den. To je vhodné pro kontrolu, zda dvě data patří ke stejnému dni, bez ohledu na čas.

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

// Extrahování jedné součásti
let year = calendar.component(.year, from: now)

// Extrahování sady součástí
let comps = calendar.dateComponents(
    [.year, .month, .day], from: now
)

// Porovnání s přesností na den
let isSameDay = calendar.compare(date1, to: date2,
                                  toGranularity: .day) == .orderedSame

// Zkontrolujte, zda je datum dnes
let isToday = calendar.isDateInToday(someDate)

Calendar.isDateInToday, isDateInTomorrow, isDateInYesterday — metody pro relativní kontroly. Calendar.isDate(_:inSameDayAs:) kontroluje, zda dvě data připadají na stejný kalendářní den s ohledem na časové pásmo kalendáře. Tyto metody interně používají Calendar.compare a jsou optimalizovány pro časté volání.

Calendar výpočty

Calendar.dateInterval — jedna z nejužitečnějších metod pro analytiku a UI. Vrací DateInterval pro zadanou součást: začátek a konec dne, týdne, měsíce, roku. DateInterval obsahuje start (Date) a end (Date) — hranice období. Například dateInterval(of: .weekOfYear, for: Date()) vrací začátek pondělí a konec neděle aktuálního týdne.

Calendar.date s byAdding — metoda pro posouvání data. Calendar.date(byAdding: .day, value: 7, to: Date()) vrací datum za týden. Calendar.date(byAdding: DateComponents) — flexibilnější verze umožňující posunout několik součástí najednou: +1 měsíc +3 dny. Calendar automaticky zohledňuje různou délku měsíců a přestupné roky.

Calendar.nextDate hledá další datum odpovídající zadaným DateComponents. Parametr matchingPolicy určuje chování při neshodě: .nextTime — další shoda v čase, .nextTimePreservingSmallerComponents — zachovává minuty a sekundy z původního data, .strict — vyžaduje přesnou shodu.

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

// Začátek a konec týdne
let weekInterval = calendar.dateInterval(
    of: .weekOfYear, for: today
)!

// Posun o 1 měsíc
let nextMonth = calendar.date(
    byAdding: .month, value: 1, to: today
)!

// Posun přes DateComponents
var delta = DateComponents()
delta.month = 1
delta.day = 3
let shifted = calendar.date(byAdding: delta, to: today)!

// Příští pátek 13.
let friday13Components = DateComponents(
    weekday: 6, day: 13
)
let nextFriday13 = calendar.nextDate(
    after: today, matching: friday13Components,
    matchingPolicy: .nextTime
)

EnumerateDates — výkonná metoda pro iteraci dat podle vzoru. Calendar.enumerateDates(startingAfter:matching:matchingPolicy:using:) volá blok pro každou shodu, dokud blok nevrátí stop = true. Používá se pro generování opakujících se událostí v kalendářích a rozvrzích. Metoda je efektivnější než ruční cyklus s nextDate, protože je optimalizována ICU.

TimeZone a Locale

TimeZone — neoddělitelná součást Calendar. Časové pásmo určuje, kterému kalendářnímu času odpovídá absolutní Date. Stejný Date v UTC a v Moskvě dává různé součásti: Date() v UTC může ukazovat 10:00, a v MSK — 13:00. Calendar.timeZone je ve výchozím nastavení roven TimeZone.current.

Locale ovlivňuje první den týdne, minimální počet dní v prvním týdnu roku (minDaysInFirstWeek) a názvy měsíců/dnů v týdnu (při převodu přes DateFormatter). Calendar.locale je ve výchozím nastavení roven Locale.current. V české locale začíná týden pondělím, v americké — nedělí.

Calendar.availableIdentifiers vrací seznam všech podporovaných kalendářních identifikátorů. static property Calendar.availableCalendarIdentifiers — pole řetězců se stejnými identifikátory. Používá se pro vytvoření UI výběru kalendáře a pro kontrolu dostupnosti konkrétního kalendářního systému na zařízení.

swift
// Kalendář s konkrétním časovým pásmem
var utcCalendar = Calendar(identifier: .gregorian)
utcCalendar.timeZone = TimeZone(identifier: "UTC")!

// Kalendář s ruskou locale
var russianCalendar = Calendar(identifier: .gregorian)
russianCalendar.locale = Locale(identifier: "ru_RU")

// První den týdne závisí na locale
let firstWeekday = russianCalendar.firstWeekday
// 2 = pondělí (v cs_CZ)

// Seznam dostupných kalendářů
for identifier in Calendar.availableIdentifiers {
    print(identifier)
}

firstWeekday — vlastnost Calendar určující, který den v týdnu je považován za první. V české locale je Sunday = 2 (pondělí první). V americké je Sunday = 1. To ovlivňuje fungování weekOfMonth a weekOfYear: stejné datum může být v různých locale přiřazeno k různým číslům týdne. Pro aplikace s daty používejte Calendar.current nebo explicitně nastavte firstWeekday.

Příklady práce s Calendar

Podívejme se na praktické scénáře demonstrující možnosti Calendar. Každý příklad řeší konkrétní úlohu iOS vývoje a ukazuje správný způsob použití kalendářních výpočtů.

Kontrola: je datum v tomto měsíci?

Calendar.dateInterval(of: .month, for:) vrací hranice aktuálního měsíce. Kontrola, zda Date spadá do tohoto intervalu — nejrychlejší způsob, jak zjistit, zda datum patří do aktuálního měsíce. Alternativní způsob — Calendar.compare s granularity .month: pokud je výsledek .orderedSame, měsíc souhlasí.

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

// Počet dní v měsíci
func daysInMonth(for date: Date) -> Int {
    let calendar = Calendar.current
    return calendar.range(
        of: .day, in: .month, for: date
    )?.count ?? 0
}

// Přidávání měsíců se správným ukončením
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:) vrací rozsah povolených hodnot pro zadanou součást v kontextu jiné součásti. Například range(of: .day, in: .month, for: date) vrací 1..<32 pro měsíce s 31 dny nebo 1..<29 pro únor nepřestupného roku. Toto je správný způsob, jak zjistit počet dní v měsíci, ne pomocí pevně zadaných hodnot.

Přidávání měsíců přes Calendar.date(byAdding:value:to:) správně zpracovává hraniční data. Pokud k 31. lednu přidáme 1 měsíc, Calendar vrátí 28. února (nebo 29. v přestupném roce), ne 3. března, jak by se stalo při jednoduchém přidání 30 dnů přes TimeInterval. To je další důvod nepoužívat TimeInterval pro kalendářní výpočty.

Metoda CalendarÚčelPříklad
dateIntervalHranice obdobíZačátek a konec měsíce
range(of:in:for:)Rozsah součástiDny v aktuálním měsíci
date(byAdding:)Posun data+1 měsíc od dneška
isDateInTodayKontrola, zda je dnesJe datum dnešní?
compare(toGranularity:)Porovnání s přesnostíStejný den bez času

Často kladené otázky

Jaký je rozdíl mezi Calendar.current a Calendar(identifier: .gregorian)?

Calendar.current vrací kalendář ze systémových nastavení uživatele — může být negregoriánský (například buddhistický v Thajsku). Calendar(identifier: .gregorian) vždy vytváří gregoriánský kalendář bez ohledu na nastavení. Pro zobrazení dat používejte Calendar.current, pro obchodní logiku — explicitně zvolený identifikátor.

Proč Calendar.date(byAdding: .month, value: 1) někdy vrací stejné datum?

To souvisí s různou délkou měsíců. Pokud je aktuální datum 31. ledna, přidání 1 měsíce dává 28. února, protože únor nemá 31 dní. Kalendář automaticky dokončí datum do posledního povoleného dne měsíce. Pro přesnou kontrolu použijte DateComponents s day: 1 pro přechod na první den měsíce.

Který kalendář používá DateFormatter ve výchozím nastavení?

DateFormatter používá Calendar.current — systémový kalendář uživatele. Pokud má aplikace vždy zobrazovat data v gregoriánském kalendáři bez ohledu na nastavení, nastavte formatter.calendar = Calendar(identifier: .gregorian). To zaručuje jednotné zobrazení pro všechny uživatele.

Jak zkontrolovat, zda je rok přestupný pomocí Calendar?

Calendar.range(of: .day, in: .year, for: date) vrací 365 nebo 366 dní. Jednodušší: Calendar.date(from: DateComponents(year: rok, month: 2, day: 29)) != nil — pokud 29. únor existuje, rok je přestupný. Calendar sám zohledňuje pravidla pro konkrétní kalendářní systém.

Lze změnit firstWeekday po vytvoření Calendar?

Ano, vlastnost firstWeekday je zapisovatelná. Změna ovlivňuje weekOfMonth, weekOfYear a všechny výpočty související s čísly týdnů. Při nastavení locale = Locale(identifier: "cs_CZ") se firstWeekday automaticky stane 2 (pondělí). Ruční nastavení přepíše hodnotu z locale.

Shrnutí

  • Calendar — třída Foundation pro kalendářní výpočty, propojující Date s lidsky čitelnými součástmi prostřednictvím DateComponents.
  • 17 kalendářních systémů podporuje Foundation — od gregoriánského po buddhistický a japonský, s automatickým zohledněním regionálních pravidel.
  • Calendar.current odpovídá systémovému kalendáři uživatele — používejte jej pro UI a DateFormatter.
  • Calendar.dateInterval a Calendar.range — klíčové metody pro práci s hranicemi období a rozsahy součástí.
  • Calendar.date(byAdding:) správně zpracovává různou délku měsíců a přestupné roky — nenahrazujte jej aritmetikou TimeInterval.
  • TimeZone a Locale jako vlastnosti Calendar ovlivňují všechny kalendářní výpočty — nastavujte je explicitně pro předvídatelné chování.
  • firstWeekday je určen locale a ovlivňuje čísla týdnů — pro globální aplikace jej nastavte explicitně nebo použijte Calendar.current.

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také