Calendar — what it is, Date calendar and computations in Swift

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

Calendar is a Foundation class that defines a calendar system and provides methods for calendar calculations: extracting date components, calculating differences between dates, finding period boundaries, and shifting dates. The Calendar connects absolute time (Date) with human-readable components and takes regional features into account: the start of the week, time zone, and daylight saving time. According to Apple Developer Documentation (2025), Foundation supports 17 calendar systems — from Gregorian to Buddhist and Japanese — making Calendar a universal tool for internationalized applications.

Key Takeaways

  • Calendar — a Foundation class for calendar calculations: extracting components, comparing, and shifting dates.
  • Calendar.current — the user’s system calendar, automatically incorporating regional settings.
  • 17 calendar systems — Foundation supports Gregorian, Buddhist, Japanese, Hebrew, Islamic, and others.
  • Calendar.dateComponents extracts components (year, month, day) from Date considering timezone.
  • Calendar.dateInterval returns the start and end date of a specified period (day, week, month).

What is Calendar in Foundation?

Calendar is a Foundation class that implements calendar calculations based on ICU (International Components for Unicode). The Calendar defines how absolute time (Date) maps to calendar components: year, month, day, hour, minute, second. Without Calendar it is impossible to determine what year, month, and day it is — Date itself does not contain this information.

The Calendar takes three groups of parameters into account: the calendar system (Gregorian, Buddhist, Japanese), the time zone, and the locale. Calendar.current combines all three from the user’s system settings. Calendar.autoupdatingCurrent is a special version that automatically updates when settings change without restarting the app via NotificationCenter.

The Calendar is a value type in Foundation. Calendar(identifier:) creates a new instance with fixed parameters. Calendar can be copied, compared via ==, and used as a dictionary key. This allows creating calendars with specific timeZone and locale settings for testing.

Calendar and NSCalendar

Calendar is the Swift version of Objective-C NSCalendar, bridged via as Calendar / as NSCalendar. In modern Swift, Calendar is used everywhere. NSCalendar remains for backward compatibility with Objective-C APIs. Calendar has a complete set of methods without the NS prefix, with type-safe arguments and Swift optionals.

Thread Safety — Calendar is thread-safe for reading. A created instance can be safely read from multiple threads. Modifying properties (timeZone, locale) is not thread-safe — create separate Calendar instances for different configurations.

Types of Calendars in Foundation

Foundation supports 17 calendar systems via the Calendar.Identifier enumeration. Each system has its own rules for leap years, number of months, and the start of the era. The choice of calendar affects all calculations: dateComponents, dateInterval, nextDate.

Main calendar systems:

  • .gregorian — international standard, 12 months, 365/366 days, era from the birth of Christ.
  • .buddhist — Buddhist calendar, used in Thailand, Cambodia, Laos, era is 543 years ahead of the Gregorian.
  • .japanese — Japanese calendar by imperial reign eras, 12 months as in the Gregorian.
  • .hebrew — Hebrew calendar, 12/13 months, leap month Adar II.
  • .islamic — Islamic calendar, 12 lunar months, 354/355 days.
  • .indian — Indian national calendar (Saka), 12 months.

Calendar(identifier: .gregorian) — the most commonly used. It conforms to the international standard ISO 8601 and is the default calendar in most countries. For applications with an international audience, use Calendar.current — it automatically matches the user’s system calendar.

IdentifierTypeRegion of Use
.gregorianSolarInternational
.buddhistSolarThailand, Cambodia
.japaneseSolarJapan
.hebrewLunisolarIsrael
.islamicLunarIslamic countries
.chineseLunisolarChina

Calendar and DateComponents

DateComponents and Calendar are an inseparable pair. Calendar.dateComponents(_:from:) extracts components from Date respecting the calendar’s time zone. Calendar.date(from:) assembles a Date from DateComponents, filling missing fields with default values: day = 1, hour = 0, minute = 0, second = 0.

The Calendar.component method extracts a single component, convenient for quick checks. Calendar.dateComponents extracts multiple components in one call — this is more performant since Calendar performs calendar calculations once rather than for each component separately. For a list of 3+ components, always use dateComponents.

Calendar.compare compares two Dates with a specified granularity. The toGranularity parameter determines the component precision: .year compares only the year, .month — year and month, .day — year, month, day. This is useful for checking whether two dates fall on the same day, ignoring time.

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

// Extracting a single component
let year = calendar.component(.year, from: now)

// Extracting a set of components
let comps = calendar.dateComponents(
    [.year, .month, .day], from: now
)

// Compare with day granularity
let isSameDay = calendar.compare(date1, to: date2,
                                  toGranularity: .day) == .orderedSame

// Check if date is today
let isToday = calendar.isDateInToday(someDate)

Calendar.isDateInToday, isDateInTomorrow, isDateInYesterday — methods for relative checks. Calendar.isDate(_:inSameDayAs:) checks whether two dates fall on the same calendar day considering the calendar’s time zone. These methods use Calendar.compare internally and are optimized for frequent calls.

Calendar Computations

Calendar.dateInterval is one of the most useful methods for analytics and UI. It returns a DateInterval for the specified component: the start and end of a day, week, month, year. DateInterval contains start (Date) and end (Date) — the period boundaries. For example, dateInterval(of: .weekOfYear, for: Date()) returns the start of Monday and the end of Sunday of the current week.

Calendar.date with byAdding — a method for shifting dates. Calendar.date(byAdding: .day, value: 7, to: Date()) returns the date one week later. Calendar.date(byAdding: DateComponents) is a more flexible version that allows shifting multiple components at once: +1 month +3 days. Calendar automatically accounts for varying month lengths and leap years.

Calendar.nextDate searches for the next date matching the specified DateComponents. The matchingPolicy parameter defines the behavior on mismatch: .nextTime — the next matching time, .nextTimePreservingSmallerComponents — preserves minutes and seconds from the original date, .strict — requires an exact match.

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

// Start and end of the week
let weekInterval = calendar.dateInterval(
    of: .weekOfYear, for: today
)!

// Shift by 1 month
let nextMonth = calendar.date(
    byAdding: .month, value: 1, to: today
)!

// Shift via DateComponents
var delta = DateComponents()
delta.month = 1
delta.day = 3
let shifted = calendar.date(byAdding: delta, to: today)!

// Next Friday the 13th
let friday13Components = DateComponents(
    weekday: 6, day: 13
)
let nextFriday13 = calendar.nextDate(
    after: today, matching: friday13Components,
    matchingPolicy: .nextTime
)

EnumerateDates — a powerful method for iterating over dates by pattern. Calendar.enumerateDates(startingAfter:matching:matchingPolicy:using:) calls a block for each match until the block returns stop = true. Used for generating recurring events in calendars and schedules. This method is more efficient than a manual loop with nextDate, as it is optimized by ICU.

TimeZone and Locale

TimeZone is an integral part of Calendar. The time zone determines which calendar time corresponds to an absolute Date. The same Date in UTC and in Moscow yields different components: Date() in UTC might show 10:00, while in MSK — 13:00. Calendar.timeZone defaults to TimeZone.current.

Locale affects the first day of the week, the minimum number of days in the first week of the year (minDaysInFirstWeek), and month/weekday names (when converting via DateFormatter). Calendar.locale defaults to Locale.current. In the Russian locale the week starts on Monday, in the American locale — on Sunday.

Calendar.availableIdentifiers returns a list of all supported calendar identifiers. The static property Calendar.availableCalendarIdentifiers is an array of strings with the same identifiers. Used for building a calendar selection UI and for checking availability of a specific calendar system on the device.

swift
// Calendar with specific time zone
var utcCalendar = Calendar(identifier: .gregorian)
utcCalendar.timeZone = TimeZone(identifier: "UTC")!

// Calendar with Russian locale
var russianCalendar = Calendar(identifier: .gregorian)
russianCalendar.locale = Locale(identifier: "ru_RU")

// First weekday depends on locale
let firstWeekday = russianCalendar.firstWeekday
// 2 = Monday (in ru_RU)

// List of available calendars
for identifier in Calendar.availableIdentifiers {
    print(identifier)
}

firstWeekday — a Calendar property that determines which day of the week is considered the first. In the Russian locale Sunday = 2 (Monday is first). In the American locale Sunday = 1. This affects weekOfMonth and weekOfYear: the same date can belong to different week numbers in different locales. For applications dealing with dates, use Calendar.current or explicitly set firstWeekday.

Examples of Working with Calendar

Let us consider practical scenarios demonstrating the capabilities of Calendar. Each example solves a specific iOS development task and shows the correct way to use calendar calculations.

Check: is the date in this month?

Calendar.dateInterval(of: .month, for:) returns the boundaries of the current month. Checking whether a Date falls within this interval is the fastest way to determine if a date belongs to the current month. An alternative approach is Calendar.compare with granularity .month: if the result is .orderedSame, the month matches.

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

// Number of days in a month
func daysInMonth(for date: Date) -> Int {
    let calendar = Calendar.current
    return calendar.range(
        of: .day, in: .month, for: date
    )?.count ?? 0
}

// Adding months with correct wrapping
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:) returns the range of valid values for a specified component in the context of another component. For example, range(of: .day, in: .month, for: date) returns 1..<32 for months with 31 days or 1..<29 for February in a non-leap year. This is the correct way to determine the number of days in a month, rather than using hardcoded values.

Adding months via Calendar.date(byAdding:value:to:) correctly handles edge dates. If you add 1 month to January 31, Calendar returns February 28 (or 29 in a leap year), rather than March 3, which would result from simply adding 30 days via TimeInterval. This is another reason not to use TimeInterval for calendar calculations.

Calendar MethodPurposeExample
dateIntervalPeriod boundariesStart and end of a month
range(of:in:for:)Component rangeDays in the current month
date(byAdding:)Date shifting+1 month from today
isDateInTodayCheck for todayDoes the date belong to today
compare(toGranularity:)Comparison with precisionSame day ignoring time

Frequently Asked Questions

What is the difference between Calendar.current and Calendar(identifier: .gregorian)?

Calendar.current returns the calendar from the user’s system settings — it may not be Gregorian (e.g., Buddhist in Thailand). Calendar(identifier: .gregorian) always creates a Gregorian calendar regardless of settings. Use Calendar.current for displaying dates, and an explicitly chosen identifier for business logic.

Why does Calendar.date(byAdding: .month, value: 1) sometimes return the same date?

This is due to varying month lengths. If the current date is January 31, adding 1 month yields February 28, since February has no 31st day. Calendar automatically caps the date to the last valid day of the month. For precise control, use DateComponents with day: 1 to move to the first day of the month.

Which calendar does DateFormatter use by default?

DateFormatter uses Calendar.current — the user’s system calendar. If an application should always display dates in the Gregorian calendar regardless of settings, set formatter.calendar = Calendar(identifier: .gregorian). This ensures uniform display for all users.

How to check if a year is a leap year via Calendar?

Calendar.range(of: .day, in: .year, for: date) returns 365 or 366 days. Simpler: Calendar.date(from: DateComponents(year: year, month: 2, day: 29)) != nil — if February 29 exists, the year is a leap year. Calendar automatically handles the rules for the specific calendar system.

Can I change firstWeekday after creating a Calendar?

Yes, the firstWeekday property is writable. Changing it affects weekOfMonth, weekOfYear, and all calculations related to week numbers. When setting locale = Locale(identifier: “ru_RU”), firstWeekday automatically becomes 2 (Monday). Manual setting overrides the value from locale.

Summary

  • Calendar — a Foundation class for calendar calculations, linking Date to human-readable components via DateComponents.
  • 17 calendar systems are supported by Foundation — from Gregorian to Buddhist and Japanese, with automatic regional rule handling.
  • Calendar.current matches the user’s system calendar — use it for UI and DateFormatter.
  • Calendar.dateInterval and Calendar.range are key methods for working with period boundaries and component ranges.
  • Calendar.date(byAdding:) correctly handles varying month lengths and leap years — do not replace it with TimeInterval arithmetic.
  • TimeZone and Locale as Calendar properties affect all calendar calculations — set them explicitly for predictable behavior.
  • firstWeekday is determined by locale and affects week numbers — for global applications set it explicitly or use Calendar.current.

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