Calendar — що це таке, календар Date та обчислення в Swift

Автор: IT Sectr Опубліковано: 2026-07-12 Час читання: 7 хв

Calendar — це клас Foundation, який визначає календарну систему та надає методи для календарних розрахунків: вилучення компонентів дати, обчислення різниці між датами, пошук меж періодів та зсув дат. Календар пов’язує абсолютний час (Date) з людськочитаними компонентами та враховує регіональні особливості: початок тижня, часовий пояс та літній час. Згідно з Apple Developer Documentation (2025), Foundation підтримує 17 календарних систем — від григоріанської до буддійської та японської, що робить Calendar універсальним інструментом для інтернаціоналізованих додатків.

Головне

  • Calendar — клас Foundation для календарних розрахунків: вилучення компонентів, порівняння та зсуву дат.
  • Calendar.current — системний календар користувача, який автоматично враховує регіональні налаштування.
  • 17 календарних систем — Foundation підтримує григоріанську, буддійську, японську, єврейську, ісламську та інші.
  • Calendar.dateComponents вилучає компоненти (рік, місяць, день) з Date з урахуванням часового поясу.
  • Calendar.dateInterval повертає початкову та кінцеву дату вказаного періоду (день, тиждень, місяць).

Що таке Calendar в Foundation?

Calendar — це клас Foundation, який реалізує календарні розрахунки на основі ICU (International Components for Unicode). Календар визначає, як абсолютний час (Date) відображається на календарні компоненти: рік, місяць, день, година, хвилина, секунда. Без Calendar неможливо дізнатися, який зараз рік, місяць та день — Date сам по собі не містить цієї інформації.

Календар враховує три групи параметрів: календарну систему (григоріанська, буддійська, японська), часовий пояс та локаль. Calendar.current об’єднує всі три з системних налаштувань користувача. Calendar.autoupdatingCurrent — спеціальна версія, яка автоматично оновлюється при зміні налаштувань без перезапуску додатка через NotificationCenter.

Calendar — це тип значення (value type) в Foundation. Calendar(identifier:) створює новий екземпляр з фіксованими параметрами. Calendar можна копіювати, порівнювати через == та використовувати як ключ у словнику. Це дозволяє створювати календарі з конкретними налаштуваннями timeZone та locale для тестування.

Calendar та NSCalendar

Calendar — це Swift-версія Objective-C NSCalendar, мостом до якої є as Calendar / as NSCalendar. У сучасному Swift Calendar використовується скрізь. NSCalendar залишається для зворотної сумісності з Objective-C API. Calendar має повний набір методів без префіксу NS, з type-safe аргументами та Swift-опціональністю.

Потокобезпека — Calendar є потокобезпечним для читання. Створений екземпляр можна безпечно читати з декількох потоків. Модифікація властивостей (timeZone, locale) не є потокобезпечною — створюйте окремі екземпляри Calendar для різних конфігурацій.

Типи календарів в Foundation

Foundation підтримує 17 календарних систем через перерахування Calendar.Identifier. Кожна система має свої правила для високосних років, кількості місяців та початку ери. Вибір календаря впливає на всі розрахунки: dateComponents, dateInterval, nextDate.

Основні календарні системи:

  • .gregorian — міжнародний стандарт, 12 місяців, 365/366 днів, ера від Різдва Христового.
  • .buddhist — буддійський календар, використовується в Таєланді, Камбоджі, Лаосі, ера на 543 роки випереджує григоріанську.
  • .japanese — японський календар за ерами правління імператорів, 12 місяців як у григоріанському.
  • .hebrew — єврейський календар, 12/13 місяців, високосний місяць Адар II.
  • .islamic — ісламський календар, 12 місяців, 354/355 днів.
  • .indian — індійський національний календар (Saka), 12 місяців.

Calendar(identifier: .gregorian) — найчастіше використовуваний. Він відповідає міжнародному стандарту ISO 8601 і є календарем за замовчуванням у більшості країн. Для додатків з міжнародною аудиторією використовуйте Calendar.current — він автоматично відповідає системному календарю користувача.

ІдентифікаторТипРегіон використання
.gregorianСонячнийМіжнародний
.buddhistСонячнийТаєланд, Камбоджа
.japaneseСонячнийЯпонія
.hebrewМісячно-сонячнийІзраїль
.islamicМісячнийІсламські країни
.chineseМісячно-сонячнийКитай

Calendar та DateComponents

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 — рік, місяць, день. Це зручно для перевірки, чи належать дві дати до одного дня, без врахування часу.

swift
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

Calendar.dateInterval — один з найкорисніших методів для аналітики та інтерфейсу користувача. Він повертає 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 — вимагає точного збігу.

swift
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 та Locale

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 повертає список усіх підтримуваних ідентифікаторів календаря. Статична властивість Calendar.availableCalendarIdentifiers — масив рядків з тими самими ідентифікаторами. Використовується для побудови інтерфейсу вибору календаря та перевірки доступності певної календарної системи на пристрої.

swift
// Calendar з певним часовим поясом
var utcCalendar = Calendar(identifier: .gregorian)
utcCalendar.timeZone = TimeZone(identifier: "UTC")!

// Calendar з російською локаллю
var russianCalendar = Calendar(identifier: .gregorian)
russianCalendar.locale = Locale(identifier: "ru_RU")

// Перший робочий день залежить від локалі
let firstWeekday = russianCalendar.firstWeekday
// 2 = понеділок (в ru_RU)

// Список доступних календарів
for identifier in Calendar.availableIdentifiers {
    print(identifier)
}

firstWeekday — властивість Calendar, яка визначає, який день тижня вважається першим. У російській локалі Sunday = 2 (понеділок перший). У американській локалі Sunday = 1. Це впливає на weekOfMonth та weekOfYear: одна й та сама дата може належати до різних номерів тижня в різних локалях. Для додатків, що працюють з датами, використовуйте Calendar.current або явно задавайте firstWeekday.

Приклади роботи з Calendar

Розглянемо практичні сценарії, що демонструють можливості Calendar. Кожен приклад вирішує конкретне завдання iOS-розробки та показує правильний спосіб використання календарних розрахунків.

Перевірка: чи в цьому місяці дата?

Calendar.dateInterval(of: .month, for:) повертає межі поточного місяця. Перевірка, чи потрапляє Date в цей інтервал, — найшвидший спосіб визначити, чи належить дата до поточного місяця. Альтернативний спосіб — Calendar.compare з гранулярністю .month: якщо результат .orderedSame, то місяць збігається.

swift
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 повертає календар з системних налаштувань користувача — він може бути не григоріанським (наприклад, буддійським в Таєланді). Calendar(identifier: .gregorian) завжди створює григоріанський календар незалежно від налаштувань. Для відображення дат використовуйте Calendar.current, для бізнес-логіки — явно вибраний ідентифікатор.

Чому Calendar.date(byAdding: .month, value: 1) іноді повертає ту ж дату?

Це пов’язано з різною довжиною місяців. Якщо поточна дата — 31 січня, додавання 1 місяця дає 28 лютого, оскільки в лютому немає 31 дня. Calendar автоматично завершує дату до останнього допустимого дня місяця. Для точного контролю використовуйте DateComponents з day: 1 для переходу до першого дня місяця.

Який календар використовує DateFormatter за замовчуванням?

DateFormatter використовує Calendar.current — системний календар користувача. Якщо додаток повинен завжди показувати дати в григоріанському календарі незалежно від налаштувань, встановіть formatter.calendar = Calendar(identifier: .gregorian). Це гарантує єдинообразне відображення для всіх користувачів.

Як перевірити, чи є рік високосним, через Calendar?

Calendar.range(of: .day, in: .year, for: date) повертає 365 або 366 днів. Простіше: Calendar.date(from: DateComponents(year: year, month: 2, day: 29)) != nil — якщо 29 лютого існує, рік високосний. Calendar сам враховує правила для конкретної календарної системи.

Чи можна змінити firstWeekday після створення Calendar?

Так, властивість firstWeekday доступна для запису. Зміна впливає на weekOfMonth, weekOfYear та всі розрахунки, пов’язані з номерами тижнів. При встановленні locale = Locale(identifier: “ru_RU”) firstWeekday автоматично стає 2 (понеділок). Ручне встановлення перевизначає значення з locale.

Підсумки

  • Calendar — клас Foundation для календарних розрахунків, що пов’язує Date з людськочитаними компонентами через DateComponents.
  • 17 календарних систем підтримуються Foundation — від григоріанської до буддійської та японської, з автоматичним врахуванням регіональних правил.
  • Calendar.current відповідає системному календарю користувача — використовуйте його для UI та DateFormatter.
  • Calendar.dateInterval та Calendar.range — ключові методи для роботи з межами періодів та діапазонами компонентів.
  • Calendar.date(byAdding:) коректно обробляє різну довжину місяців та високосні роки — не замінюйте його арифметикою TimeInterval.
  • TimeZone та Locale як властивості Calendar впливають на всі календарні розрахунки — встановлюйте їх явно для предбачуваної поведінки.
  • firstWeekday визначається локаллю та впливає на номери тижнів — для глобальних додатків задавайте його явно або використовуйте Calendar.current.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також