Calendar — је класа Foundation која дефинише календарски систем и пружа методе за календарска израчунавања: издвајање компоненти датума, израчунавање разлике између датума, проналажење граница периода и померање датума. Календар повезује апсолутно време (Date) са људски читљивим компонентама и узима у обзир регионалне особености: почетак недеље, временску зону и летње време. Према Apple Developer Documentation (2025), Foundation подржава 17 календарских система — од грегоријанског до будистичког и јапанског, што чини Calendar универзалним алатом за интернационализоване апликације.
Главно
Calendar — је класа Foundation која имплементира календарска израчунавања на основу ICU (International Components for Unicode). Календар одређује како се апсолутно време (Date) мапира на календарске компоненте: година, месец, дан, сат, минут, секунд. Без Calendar немогуће је одредити која је данас година, месец и дан — Date сам по себи не садржи ове информације.
Календар узима у обзир три групе параметара: календарски систем (грегоријански, будистички, јапански), временску зону и локал. Calendar.current их комбинује све три из системских подешавања корисника. Calendar.autoupdatingCurrent — специјална верзија која се аутоматски ажурира при промени подешавања без поновног покретања апликације преко NotificationCenter.
Календар је вредносни тип (value type) у Foundation. Calendar(identifier:) креира нову инстанцу са фиксним параметрима. Календар се може копирати, упоређивати путем == и користити као кључ у речнику. Ово омогућава креирање календара са одређеним timeZone и locale подешавањима за тестирање.
Calendar — Swift верзија Objective-C NSCalendar, са мостом as Calendar / as NSCalendar. У модерном Swift-у свуда се користи Calendar. NSCalendar остаје за повратну компатибилност са Objective-C API-јем. Calendar има пун скуп метода без NS префикса, са type-safe аргументима и Swift опционалношћу.
Thread Safety — Calendar је thread-safe за читање. Креирана инстанца се може безбедно читати из више нити. Модификација својстава (timeZone, locale) није thread-safe — за различите конфигурације креирајте различите инстанце Calendar.
Foundation подржава 17 календарских система кроз енумерацију Calendar.Identifier. Сваки систем има своја правила за преступне године, број месеци и почетак ере. Избор календара утиче на сва израчунавања: dateComponents, dateInterval, nextDate.
Основни календарски системи:
Calendar(identifier: .gregorian) — најчешће коришћен. Одговара међународном стандарду ISO 8601 и подразумевани је календар у већини земаља. За апликације са међународном публиком користите Calendar.current — аутоматски одговара системском календару корисника.
| Идентификатор | Тип | Регион употребе |
|---|---|---|
| .gregorian | Соларни | Међународни |
| .buddhist | Соларни | Тајланд, Камбоџа |
| .japanese | Соларни | Јапан |
| .hebrew | Лунарно-соларни | Израел |
| .islamic | Лунарни | Исламске земље |
| .chinese | Лунарно-соларни | Кина |
DateComponents и Calendar — нераздвојни пар. Calendar.dateComponents(_:from:) издваја компоненте из Date узимајући у обзир временску зону календара. Calendar.date(from:) саставља Date из DateComponents, попуњавајући недостајућа поља подразумеваним вредностима: дан = 1, сат = 0, минут = 0, секунд = 0.
Метода Calendar.component издваја једну компоненту, што је згодно за брзу проверу. Calendar.dateComponents издваја скуп компоненти у једном позиву — ово је ефикасније јер Calendar врши календарска израчунавања једном, а не за сваку компоненту посебно. За листу од 3+ компоненти увек користите dateComponents.
Calendar.compare упоређује два Date са задатом прецизношћу. Параметар toGranularity одређује до које компоненте се врши поређење: .year упоређује само годину, .month — годину и месец, .day — годину, месец, дан. Ово је згодно за проверу да ли два датума припадају истом дану, без узимања у обзир времена.
let calendar = Calendar.current
let now = Date()
// Издвајање једне компоненте
let year = calendar.component(.year, from: now)
// Издвајање скупа компоненти
let comps = calendar.dateComponents(
[.year, .month, .day], from: now
)
// Поређење са прецизношћу дана
let isSameDay = calendar.compare(date1, to: date2,
toGranularity: .day) == .orderedSame
// Провери да ли је датум данас
let isToday = calendar.isDateInToday(someDate)
Calendar.isDateInToday, isDateInTomorrow, isDateInYesterday — методе за релативне провере. Calendar.isDate(_:inSameDayAs:) проверава да ли два датума падају на исти календарски дан узимајући у обзир временску зону календара. Ове методе користе Calendar.compare интерно и оптимизоване су за честе позиве.
Calendar.dateInterval — једна од најкориснијих метода за аналитику и UI. Враћа DateInterval за наведену компоненту: почетак и крај дана, недеље, месеца, године. DateInterval садржи start (Date) и end (Date) — границе периода. На пример, dateInterval(of: .weekOfYear, for: Date()) враћа почетак понедељка и крај недеље текуће недеље.
Calendar.date са byAdding — метода за померање датума. Calendar.date(byAdding: .day, value: 7, to: Date()) враћа датум за недељу дана. Calendar.date(byAdding: DateComponents) — флексибилнија верзија која омогућава померање више компоненти одједном: +1 месец +3 дана. Calendar аутоматски узима у обзир различиту дужину месеци и преступне године.
Calendar.nextDate тражи следећи датум који се поклапа са задатим DateComponents. Параметар matchingPolicy одређује понашање при непоклапању: .nextTime — следеће поклапање по времену, .nextTimePreservingSmallerComponents — задржава минуте и секунде из оригиналног датума, .strict — захтева тачно поклапање.
let calendar = Calendar.current
let today = Date()
// Почетак и крај недеље
let weekInterval = calendar.dateInterval(
of: .weekOfYear, for: today
)!
// Померање за 1 месец
let nextMonth = calendar.date(
byAdding: .month, value: 1, to: today
)!
// Померање кроз DateComponents
var delta = DateComponents()
delta.month = 1
delta.day = 3
let shifted = calendar.date(byAdding: delta, to: today)!
// Следећи петак 13.
let friday13Components = DateComponents(
weekday: 6, day: 13
)
let nextFriday13 = calendar.nextDate(
after: today, matching: friday13Components,
matchingPolicy: .nextTime
)
EnumerateDates — моћна метода за пребирање датума по шаблону. Calendar.enumerateDates(startingAfter:matching:matchingPolicy:using:) позива блок за свако поклапање док блок не врати stop = true. Користи се за генерисање понављајућих догађаја у календарима и распоредима. Метода је ефикаснија од ручне петље са nextDate јер је оптимизована од стране ICU.
TimeZone — неодвојиви део Calendar-а. Временска зона одређује ком календарском времену одговара апсолутни Date. Исти Date у UTC и у Москви даје различите компоненте: Date() у UTC може показивати 10:00, а у MSK — 13:00. Calendar.timeZone подразумевано је једнак TimeZone.current.
Locale утиче на први дан недеље, минималан број дана у првој недељи године (minDaysInFirstWeek) и називе месеци/дана недеље (при конверзији кроз DateFormatter). Calendar.locale подразумевано је једнак Locale.current. У српској локализацији недеља почиње понедељком, у америчкој — недељом.
Calendar.availableIdentifiers враћа листу свих подржаних календарских идентификатора. static property Calendar.availableCalendarIdentifiers — низ стрингова са истим идентификаторима. Користи се за изградњу UI-ја за избор календара и за проверу доступности одређеног календарског система на уређају.
// Календар са одређеном временском зоном
var utcCalendar = Calendar(identifier: .gregorian)
utcCalendar.timeZone = TimeZone(identifier: "UTC")!
// Календар са руским локалом
var russianCalendar = Calendar(identifier: .gregorian)
russianCalendar.locale = Locale(identifier: "ru_RU")
// Први дан недеље зависи од локала
let firstWeekday = russianCalendar.firstWeekday
// 2 = понедељак (у sr_RS)
// Листа доступних календара
for identifier in Calendar.availableIdentifiers {
print(identifier)
}
firstWeekday — својство Calendar-а које одређује који се дан недеље сматра првим. У српској локализацији Sunday = 2 (понедељак први). У америчкој Sunday = 1. Ово утиче на рад weekOfMonth и weekOfYear: исти датум може бити придружен различитим бројевима недеље у различитим локализацијама. За апликације са датумима користите Calendar.current или експлицитно подесите firstWeekday.
Размотримо практичне сценарије који демонстрирају могућности Calendar. Сваки пример решава конкретан задатак iOS развоја и показује правилан начин коришћења календарских израчунавања.
Calendar.dateInterval(of: .month, for:) враћа границе текућег месеца. Провера припадања Date овом интервалу — најбржи начин да се утврди да ли датум припада текућем месецу. Алтернативни начин — Calendar.compare са granularity .month: ако је резултат .orderedSame, месец се поклапа.
func isInCurrentMonth(_ date: Date) -> Bool {
let calendar = Calendar.current
let monthInterval = calendar.dateInterval(
of: .month, for: Date()
)!
return monthInterval.contains(date)
}
// Број дана у месецу
func daysInMonth(for date: Date) -> Int {
let calendar = Calendar.current
return calendar.range(
of: .day, in: .month, for: date
)?.count ?? 0
}
// Додавање месеци са коректним завршетком
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:) враћа опсег дозвољених вредности за наведену компоненту у контексту друге компоненте. На пример, range(of: .day, in: .month, for: date) враћа 1..<32 за месеце са 31 даном или 1..<29 за фебруар непреступне године. Ово је прави начин да се сазна број дана у месецу, а не коришћење хардкодованих вредности.
Додавање месеци кроз Calendar.date(byAdding:value:to:) коректно обрађује граничне датуме. Ако се 31. јануара дода 1 месец, Calendar враћа 28. фебруар (или 29. у преступној години), а не 3. март, како би се добило једноставним додавањем 30 дана кроз TimeInterval. Ово је још један разлог да се не користи TimeInterval за календарска израчунавања.
| Метода Calendar | Намена | Пример |
|---|---|---|
| dateInterval | Границе периода | Почетак и крај месеца |
| range(of:in:for:) | Опсег компоненте | Дани у текућем месецу |
| date(byAdding:) | Померање датума | +1 месец од данас |
| isDateInToday | Провера да ли је данас | Да ли је датум данашњи |
| compare(toGranularity:) | Поређење са прецизношћу | Исти дан без времена |
Често постављана питања
Calendar.current враћа календар из системских подешавања корисника — може бити негрегоријански (на пример, будистички на Тајланду). Calendar(identifier: .gregorian) увек креира грегоријански календар независно од подешавања. За приказ датума користите Calendar.current, за пословну логику — експлицитно изабрани идентификатор.
Ово је повезано са различитом дужином месеци. Ако је текући датум 31. јануар, додавање 1 месеца даје 28. фебруар, јер фебруар нема 31 дан. Calendar аутоматски завршава датум до последњег дозвољеног дана месеца. За прецизну контролу користите DateComponents са day: 1 за прелазак на први дан месеца.
DateFormatter користи Calendar.current — системски календар корисника. Ако апликација увек треба да приказује датуме у грегоријанском календару без обзира на подешавања, подесите formatter.calendar = Calendar(identifier: .gregorian). Ово гарантује једнообразни приказ за све кориснике.
Calendar.range(of: .day, in: .year, for: date) враћа 365 или 366 дана. Једноставније: Calendar.date(from: DateComponents(year: година, month: 2, day: 29)) != nil — ако 29. фебруар постоји, година је преступна. Calendar сам узима у обзир правила за одређени календарски систем.
Да, својство firstWeekday је доступно за писање. Промена утиче на weekOfMonth, weekOfYear и сва израчунавања повезана са бројевима недеља. При постављању locale = Locale(identifier: "sr_RS") firstWeekday аутоматски постаје 2 (понедељак). Ручно подешавање замењује вредност из locale.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође