DateComponents је структура Foundation-а која чува компоненте календарског датума као засебна поља: година, месец, дан, сат, минут, секунд и друга. За разлику од Date-а, који представља апсолутни тренутак у времену, DateComponents садржи људима читљиве вредности, зависне од календара и временске зоне. Према Apple Developer Documentation (2025), DateComponents се користи као посредна веза између Date-а и Calendar-а – преко ње се извлаче и конструишу календарски датуми, врше израчунавања и померања датума без ручне аритметике.
Главно
DateComponents је вредносни тип Foundation-а намењен чувању календарских компоненти времена. Свака компонента је представљена као опционо Int поље: year, month, day, hour, minute, second, nanosecond, weekday, weekOfMonth, weekOfYear, quarter, yearForWeekOfYear и друга.
Главна разлика у односу на Date је повезаност са календаром. Date чува апсолутно време (број секунди од reference date-а), а DateComponents – људима читљиву репрезентацију која има смисла само у контексту одређеног Calendar-а. Исти Date може бити представљен различитим DateComponents-има у различитим календарима и временским зонама.
DateComponents није самосталан тип времена, већ контејнер података. За интерпретацију DateComponents-а као датума потребан је Calendar који разуме како се компоненте односе на календарски систем. Calendar.dateComponents(from: Date) врши извлачење компоненти, Calendar.date(from: DateComponents) – обрнуто састављање.
Свако поље DateComponents-а је опционо (Int?), што је принципијелно за рад са непотпуним датумима. Ако наведете само годину и месец, Calendar ће допунити недостајућа поља подразумеваним вредностима: дан = 1, сат = 0, минут = 0. Ово је згодно за креирање датума почетка периода – потребно је задати само интересантне компоненте.
При поређењу DateComponents-а оператором == пореде се само задата (не nil) поља. Две структуре DateComponents са годином 2026, али различитим месецима, сматрају се различитим. isEqual из NSObjectProtocol-а се не примењује за DateComponents – DateComponents не наслеђује NSObject.
Основна поља DateComponents-а укључују year, month, day, hour, minute, second, nanosecond. Свако поље чува нумеричку вредност у одговарајућој јединици: година – 2026, месец – 1..12, дан – 1..31, сат – 0..23, минут – 0..59, секунд – 0..59. Наносекунде могу узимати вредности 0..999999999.
Поља недеље – weekday (1..7, где 1 = недеља у грегоријанском календару), weekOfMonth, weekOfYear. Ова поља зависе од Calendar-а и немају смисла ван његовог контекста. weekday зависи од подешавања firstWeekday календара: у српској локализацији недеља почиње понедељком (weekday = 2 у грегоријанском систему), а у америчкој – недељом (weekday = 1).
Специјализована поља – quarter (1..4), yearForWeekOfYear (година којој припада недеља), isLeapMonth (логичка заставица за преступне месеце у хебрејском или кинеском календару). Поља calendar и timeZone чувају референце на одговарајуће објекте са којима је структура креирана.
| Категорија | Поља | Опсег |
|---|---|---|
| Календарска | year, month, day | 1..∞, 1..12, 1..31 |
| Временска | hour, minute, second, nanosecond | 0..23, 0..59, 0..59, 0..999999999 |
| Недељна | weekday, weekOfMonth, weekOfYear | 1..7, 1..5, 1..53 |
| Специјална | quarter, yearForWeekOfYear | 1..4, зависно |
При извлачењу компоненти путем Calendar.dateComponents важно је захтевати само потребна поља ради перформанси. Calendar извлачи сва захтевана поља у једном пролазу – то је значајно брже од позивања Calendar.component за свако поље појединачно.
Иницијализација DateComponents-а – најједноставнији начин: креирате празну структуру и попуните потребна поља. Сва ненаведена поља аутоматски добијају nil. Датум креиран из делимичних компоненти се не валидира у фази иницијализације – грешка може настати само при конверзији у Date путем Calendar-а.
Иницијализатор DateComponents(calendar:timeZone:era:year:month:day:hour:minute:second:nanosecond:weekday:…) омогућава постављање свих поља у једном позиву. Овај иницијализатор је згодан за креирање пуног датума из готових вредности, али се ретко користи са више од 5-6 аргумената због читљивости.
Calendar.dateComponents(_:from:) – основни начин добијања DateComponents-а из постојећег Date-а. Други аргумент је скуп компоненти које треба извући. Calendar врши календарска израчунавања узимајући у обзир временску зону и враћа структуру само са захтеваним пољима, остала поља остају nil.
import Foundation
// Креирање преко иницијализатора поља
var components = DateComponents()
components.year = 2026
components.month = 7
components.day = 21
// Извлачење из Date-а
let now = Date()
let extracted = Calendar.current.dateComponents(
[.year, .month, .day],
from: now
)
print("Today: \(extracted.day!).\(extracted.month!).\(extracted.year!)")
// Креирање преко проширеног иницијализатора
let birthday = DateComponents(
calendar: Calendar.current,
year: 1990, month: 5, day: 15
)
При креирању DateComponents-а кроз ручно постављање поља, увек проверите Calendar пре конверзије у Date. Calendar при конверзији date(from:) може вратити nil ако компоненте чине непостојећи датум – на пример, 31. фебруар или 30. фебруар у непреступној години. Валидација датума је одговорност Calendar-а, не DateComponents-а.
Calendar.date(from:) – основни метод конверзије DateComponents-а у Date. Calendar интерпретира компоненте према свом календару и временској зони. Ако нека поља нису постављена (nil), Calendar користи подразумеване вредности: дан = 1, сат = 0, минут = 0, секунд = 0.
Метод враћа опциони Date – nil настаје када су компоненте у међусобној противречности или чине неважећи датум. Типични узроци nil-а: непостојећи датум (32. јануар, 29. фебруар 2023), противречна поља (weekday=1, day=5 у истом скупу), немогућа година за дати календар (година 0 у грегоријанском календару).
DateComponents са timeZone – ако DateComponents садржи timeZone, Calendar га користи при конверзији. Ако timeZone није наведена, Calendar користи своју тренутну timeZone. Ако Calendar.timeZone не одговара очекиваној временској зони датума, резултат се може разликовати неколико сати – уверите се да је timeZone експлицитно постављена у једном од објеката.
let calendar = Calendar(identifier: .gregorian)
// Креирање Date-а из 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)")
}
// Креирање са навођењем 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 за разлику датума – још један сценариј употребе DateComponents-а. Calendar.dateComponents([.year, .month, .day], from: Date(), to: futureDate) враћа разлику у годинама, месецима и данима између два датума. Ово је исправан начин израчунавања старости уместо дељења TimeInterval-а бројем секунди у години, јер Calendar узима у обзир преступне године.
Calendar – централна класа која ради са DateComponents-има. Све операције извлачења, састављања и поређења датума пролазе кроз Calendar. Без Calendar-а, DateComponents је само скуп бројева без временског смисла. Calendar даје интерпретацију компонентама: одређује да је месец 2 – фебруар, а weekday 2 – понедељак.
Calendar.nextDate и Calendar.enumerateDates – две методе засноване на DateComponents-има. nextDate(after: Date(), matching: DateComponents) проналази следећи датум који одговара задатим компонентама – на пример, следећи понедељак после данас. enumerateDates(startingAfter:matching:matchingPolicy:using:) прегледа све датуме који одговарају шаблону до наведене границе.
Calendar.dateInterval – метода која враћа DateInterval за наведену компоненту. dateInterval(of: .month, for: Date()) враћа почетак и крај текућег месеца. Унутар себе, ова метода користи DateComponents за проналажење граница периода: креира DateComponents са првим и последњим даном месеца, претвара их у Date путем Calendar-а.
let calendar = Calendar.current
// Следећи понедељак
let nextMonday = calendar.nextDate(
after: Date(),
matching: DateComponents(weekday: 2),
matchingPolicy: .nextTime
)!
// Разлика између датума у данима
let diff = calendar.dateComponents(
[.day], from: Date(), to: nextMonday
)
// Опсег месеца
let monthInterval = calendar.dateInterval(
of: .month, for: Date()
)!
let startOfMonth = monthInterval.start
let endOfMonth = monthInterval.end
MatchingPolicy – важан параметар Calendar метода при раду са DateComponents-има. strictPolicy захтева тачно поклапање свих компоненти, nextTimePolicy бира следеће поклапање у времену, nextTimePreservingSmallerComponents чува мање компоненте (минуте, секунде) из оригиналног датума. Избор политике утиче на резултат претраге датума, посебно при померању кроз прелазак на летње/зимско време.
Размотрићемо практичне сценаријe примене DateComponents-а у апликацији. Сваки пример демонстрира типичан задатак са којим се суочава iOS програмер при раду са календарским датумима.
Calendar.nextDate са DateComponents(day: 1) проналази први дан следећег месеца. Calendar аутоматски одређује број дана у текућем месецу и прелази на следећи. За понављајућа обавештења користите enumerateDates или Combine.Timer са Calendar кључем.
func firstDayOfNextMonth(from date: Date) -> Date {
let calendar = Calendar.current
let comps = DateComponents(day: 1)
return calendar.nextDate(
after: date,
matching: comps,
matchingPolicy: .nextTime
)!
}
// Израчунавање старости у годинама
func ageInYears(from birthDate: Date) -> Int {
let calendar = Calendar.current
let ageComponents = calendar.dateComponents(
[.year], from: birthDate, to: Date()
)
return ageComponents.year ?? 0
}
// Груписање догађаја по години и месецу
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!)"
}
}
Израчунавање старости путем Calendar.dateComponents([.year], from:to:) – једини исправан начин који узима у обзир преступне године. TimeInterval-базирано израчунавање (секунди / 31536000) даје грешку за особе рођене 29. фебруара. Calendar исправно одређује да ли је рођендан био у текућој години и враћа тачну старост.
Груписање по години и месецу – уобичајен задатак за екране са историјом или календаром. DateComponents служи као кључ за груписање: извлачите годину и месец из датума догађаја, формирате кључ у виду стринга и групишете путем Dictionary(grouping:). За приказ користите DateFormatter са шаблоном „LLLL yyyy” за локализовано име месеца.
| Задатак | Calendar метода | Улога DateComponents |
|---|---|---|
| Први дан месеца | nextDate(after:matching:) | day: 1 |
| Израчунавање старости | dateComponents(from:to:) | [.year] из разлике |
| Груписање датума | dateComponents(_:from:) | year + month кључ |
| Проналажење дана у недељи | nextDate(after:matching:) | weekday: N |
Често постављана питања
Узроци: непостојећи датум (31. април), противречна поља (weekday=1 са day=5), неважећа комбинација поља за изабрани календар. Calendar покушава да интерпретира компоненте у свом систему – ако је комбинација немогућа, резултат је nil. Увек користите guard let или if let при конверзији.
Да, путем оператора ==. DateComponents имплементира Equatable, поредећи сва поља. Две структуре су једнаке ако су сва њихова поља једнака (nil == nil се сматра тачним). За поређење само дела поља – извуците исти скуп путем Calendar.dateComponents.
Date – апсолутни тренутак у времену без везе са календаром. DateComponents – скуп људима читљивих бројева (година, месец, дан) који имају смисла само у контексту Calendar-а. Date се може поредити, одузети, серијализовати у ISO 8601. DateComponents – посредна репрезентација за интеракцију са календаром.
Поставите само поља year и month, остављајући остала nil. При конверзији у Date путем Calendar.date(from:), Calendar ће аутоматски поставити дан = 1, сат = 0, минут = 0. Резултат – Date који одговара првом дану наведеног месеца у поноћ.
DateComponents не чува информацију о временској зони у пољима – вредности поља (година, месец, дан) саме зависе од timeZone у којој су извучене. Компоненте „21. јул 2026. 14:00 MSK” и „21. јул 2026. 10:00 UTC” представљају исти Date, али су поља DateComponents-а различита.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође