DateComponents — what it is, calendar components and NSCalendar

Author: IT Sectr Published: 2026-07-12 Reading time: 7 min

DateComponents is a Foundation structure that stores calendar date components as separate fields: year, month, day, hour, minute, second, and others. Unlike Date, which represents an absolute moment in time, DateComponents contains human-readable values that depend on the calendar and time zone. According to Apple Developer Documentation (2025), DateComponents is used as an intermediate link between Date and Calendar — through it, calendar dates are extracted and constructed, calculations and date shifts are performed without manual arithmetic.

Key takeaways

  • DateComponents — a structure for storing date components (year, month, day) as optional integer fields.
  • Calendar.dateComponents — a method that extracts specified components from Date taking time zone into account.
  • Calendar.date(from:) — reverse conversion of DateComponents to Date with auto-filling missing fields.
  • Optional fields — each DateComponents field can be nil, allowing partial dates to be specified.
  • Range and components — DateComponents is used in Calendar to calculate the difference between dates and find dates within a range.

What is DateComponents?

DateComponents is a Foundation value type designed for storing calendar time components. Each component is represented by an optional Int field: year, month, day, hour, minute, second, nanosecond, weekday, weekOfMonth, weekOfYear, quarter, yearForWeekOfYear, and others.

The main difference from Date is calendar binding. Date stores absolute time (number of seconds from reference date), while DateComponents is a human-readable representation that only makes sense in the context of a specific Calendar. The same Date can be represented by different DateComponents in different calendars and time zones.

DateComponents is not a standalone time type, but a data container. To interpret DateComponents as a date, a Calendar is required that understands how components relate to the calendar system. Calendar.dateComponents(from: Date) performs component extraction, Calendar.date(from: DateComponents) performs reverse assembly.

Optionality of fields

Each DateComponents field is optional (Int?), which is fundamental for working with partial dates. If only year and month are specified, Calendar fills in missing fields with default values: day = 1, hour = 0, minute = 0. This is convenient for creating period start dates — you only need to specify the relevant components.

When comparing DateComponents with the == operator, only specified (non-nil) fields are compared. Two DateComponents structures both with year 2026 but different months are considered distinct. isEqual from NSObjectProtocol does not apply to DateComponents — DateComponents does not inherit NSObject.

Date components: year, month, day

Main fields of DateComponents include year, month, day, hour, minute, second, nanosecond. Each field stores a numeric value in the corresponding unit: year — 2026, month — 1..12, day — 1..31, hour — 0..23, minute — 0..59, second — 0..59. Nanoseconds can range from 0 to 999999999.

Week fields — weekday (1..7, where 1 = Sunday in the Gregorian calendar), weekOfMonth, weekOfYear. These fields depend on the Calendar and have no meaning outside its context. weekday depends on the calendar's firstWeekday setting: in Russian locale the week starts on Monday (weekday = 2 in the Gregorian system), while in American locale it starts on Sunday (weekday = 1).

Specialized fields — quarter (1..4), yearForWeekOfYear (the year to which the week belongs), isLeapMonth (a boolean flag for leap months in the Hebrew or Chinese calendars). The calendar and timeZone fields store references to the corresponding objects with which the structure was created.

CategoryFieldsRange
Calendaryear, month, day1..∞, 1..12, 1..31
Timehour, minute, second, nanosecond0..23, 0..59, 0..59, 0..999999999
Weekweekday, weekOfMonth, weekOfYear1..7, 1..5, 1..53
Specialquarter, yearForWeekOfYear1..4, dependent

When extracting components via Calendar.dateComponents, it is important to request only the needed fields for performance. Calendar extracts all requested fields in a single pass — this is significantly faster than calling Calendar.component for each field individually.

Creating DateComponents

Initializing DateComponents — the simplest way: create an empty structure and fill in the needed fields. All unspecified fields automatically get nil. A date created from partial components is not validated at initialization — an error may only occur when converting to Date via Calendar.

The initializer DateComponents(calendar:timeZone:era:year:month:day:hour:minute:second:nanosecond:weekday:…) allows setting all fields in a single call. This initializer is convenient for creating a complete date from ready-made values, but is rarely used with more than 5-6 arguments due to readability.

Calendar.dateComponents(_:from:) — the primary way to obtain DateComponents from an existing Date. The second argument is the set of components to extract. Calendar performs calendar calculations taking time zone into account and returns a structure with only the requested fields; the remaining fields stay nil.

swift
import Foundation

// Creating via field initializer
var components = DateComponents()
components.year = 2026
components.month = 7
components.day = 21

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

// Creating via extended initializer
let birthday = DateComponents(
    calendar: Calendar.current,
    year: 1990, month: 5, day: 15
)

When creating DateComponents by setting fields manually, always check the Calendar before converting to Date. When converting date(from:), Calendar may return nil if the components form a non-existent date — for example, February 31 or February 30 in a non-leap year. Date validation is the responsibility of Calendar, not DateComponents.

Converting DateComponents to Date

Calendar.date(from:) — the primary method for converting DateComponents to Date. Calendar interprets the components according to its own calendar and time zone. If some fields are not set (nil), Calendar uses default values: day = 1, hour = 0, minute = 0, second = 0.

The method returns an optional Date — nil occurs if the components contradict each other or form an invalid date. Typical causes of nil: non-existent date (January 32, February 29 2023), contradictory fields (weekday=1, day=5 in the same set), impossible year for the given calendar (year 0 in the Gregorian calendar).

DateComponents with timeZone — if DateComponents contains a timeZone, Calendar uses it during conversion. If timeZone is not specified, Calendar uses its own current timeZone. If Calendar.timeZone does not match the expected time zone of the date, the result may differ by several hours — make sure timeZone is explicitly set in one of the objects.

swift
let calendar = Calendar(identifier: .gregorian)

// Creating Date from 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)")
}

// Creating with timeZone specified
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 for date difference — another use case for DateComponents. Calendar.dateComponents([.year, .month, .day], from: Date(), to: futureDate) returns the difference in years, months, and days between two dates. This is the correct way to calculate age instead of dividing TimeInterval by the number of seconds in a year, since Calendar accounts for leap years.

Calendar and DateComponents

Calendar — the central class that works with DateComponents. All operations for extracting, assembling, and comparing dates go through Calendar. Without Calendar, DateComponents is just a set of numbers with no temporal meaning. Calendar gives components their interpretation: it determines that month 2 is February, and weekday 2 is Monday.

Calendar.nextDate and Calendar.enumerateDates — two methods based on DateComponents. nextDate(after: Date(), matching: DateComponents) finds the next date matching the specified components — for example, the next Monday after today. enumerateDates(startingAfter:matching:matchingPolicy:using:) iterates over all dates matching the pattern up to the specified limit.

Calendar.dateInterval — a method that returns a DateInterval for the specified component. dateInterval(of: .month, for: Date()) returns the start and end of the current month. Internally, this method uses DateComponents to find period boundaries: it creates DateComponents with the first and last day of the month and converts them to Date via Calendar.

swift
let calendar = Calendar.current

// Next Monday
let nextMonday = calendar.nextDate(
    after: Date(),
    matching: DateComponents(weekday: 2),
    matchingPolicy: .nextTime
)!

// Difference between dates in days
let diff = calendar.dateComponents(
    [.day], from: Date(), to: nextMonday
)

// Month range
let monthInterval = calendar.dateInterval(
    of: .month, for: Date()
)!
let startOfMonth = monthInterval.start
let endOfMonth = monthInterval.end

MatchingPolicy — an important parameter of Calendar methods when working with DateComponents. strictPolicy requires exact matching of all components, nextTimePolicy selects the next time-wise match, nextTimePreservingSmallerComponents preserves smaller components (minutes, seconds) from the source date. The choice of policy affects the result of date searching, especially when shifting through daylight saving time transitions.

DateComponents examples

Let's explore practical use cases for DateComponents in an application. Each example demonstrates a typical task an iOS developer faces when working with calendar dates.

Reminder on the first day of each month

Calendar.nextDate with DateComponents(day: 1) finds the first day of the next month. Calendar automatically determines the number of days in the current month and moves to the next one. For recurring notifications, use enumerateDates or Combine.Timer with a Calendar key.

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
    )!
}

// Calculating age in years
func ageInYears(from birthDate: Date) -> Int {
    let calendar = Calendar.current
    let ageComponents = calendar.dateComponents(
        [.year], from: birthDate, to: Date()
    )
    return ageComponents.year ?? 0
}

// Grouping events by year and month
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!)"
    }
}

Calculating age via Calendar.dateComponents([.year], from:to:) — the only correct way that accounts for leap years. TimeInterval-based calculation (seconds / 31536000) gives an error for people born on February 29. Calendar correctly determines whether a birthday has occurred in the current year and returns the exact age.

Grouping by year and month — a common task for history or calendar screens. DateComponents serves as the grouping key: extract the year and month from the event date, form a string key, and group via Dictionary(grouping:). For display, use DateFormatter with the "LLLL yyyy" template for a localized month name.

TaskCalendar methodDateComponents role
First day of monthnextDate(after:matching:)day: 1
Age calculationdateComponents(from:to:)[.year] from difference
Date groupingdateComponents(_:from:)year + month key
Weekday searchnextDate(after:matching:)weekday: N

Frequently asked questions

Why does Calendar.date(from:) return nil for DateComponents?

Causes: non-existent date (April 31), contradictory fields (weekday=1 with day=5), invalid combination of fields for the selected calendar. Calendar tries to interpret the components in its system — if the combination is impossible, the result is nil. Always use guard let or if let when converting.

Can DateComponents be compared with each other?

Yes, via the == operator. DateComponents conforms to Equatable, comparing all fields. Two structures are equal if all their fields are equal (nil == nil is considered true). To compare only a subset of fields — extract the same set via Calendar.dateComponents.

How is DateComponents different from Date?

Date is an absolute moment in time without calendar binding. DateComponents is a set of human-readable numbers (year, month, day) that only make sense in the context of a Calendar. Date can be compared, subtracted, serialized to ISO 8601. DateComponents is an intermediate representation for interacting with the calendar.

How to specify only year and month in DateComponents?

Set only the year and month fields, leaving the rest as nil. When converting to Date via Calendar.date(from:), Calendar will automatically set day = 1, hour = 0, minute = 0. The result is a Date corresponding to the first day of the specified month at midnight.

How does DateComponents handle time zones?

DateComponents does not store time zone information in its fields — the field values (year, month, day) themselves depend on the timeZone in which they were extracted. The components "July 21, 2026 14:00 MSK" and "July 21, 2026 10:00 UTC" represent the same Date, but the DateComponents fields are different.

Summary

  • DateComponents — a Foundation structure for storing calendar components (year, month, day, hour) as optional Int? fields.
  • Calendar.dateComponents extracts components from Date taking time zone and calendar system into account.
  • Calendar.date(from:) assembles a Date from DateComponents, using default values for missing fields.
  • Optional fields allow specifying partial dates — Calendar fills in missing values.
  • Calendar.nextDate finds the next date matching DateComponents — for reminders and recurring events.
  • Age calculation via Calendar.dateComponents([.year], from:to:) — the only correct way that accounts for leap years.
  • MatchingPolicy controls Calendar behavior when not all components match — an important parameter for date searching.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also