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 با در نظر گرفتن timezone استخراج می‌کند.
  • Calendar.dateInterval تاریخ شروع و پایان یک دوره مشخص (روز، هفته، ماه) را برمی‌گرداند.

Calendar در Foundation چیست؟

Calendar — کلاسی از Foundation است که محاسبات تقویمی را بر اساس ICU (International Components for Unicode) پیاده‌سازی می‌کند. تقویم تعیین می‌کند که زمان مطلق (Date) چگونه به اجزای تقویمی نگاشت می‌شود: سال، ماه، روز، ساعت، دقیقه، ثانیه. بدون Calendar نمی‌توان فهمید امروز چه سال، ماه و روزی است — Date به خودی خود این اطلاعات را ندارد.

تقویم سه گروه پارامتر را در نظر می‌گیرد: سیستم تقویمی (گرگوری، بودایی، ژاپنی)، منطقه زمانی و محل. Calendar.current هر سه را از تنظیمات سیستمی کاربر ترکیب می‌کند. Calendar.autoupdatingCurrent — نسخه ویژه‌ای که به طور خودکار با تغییر تنظیمات بدون راه‌اندازی مجدد برنامه از طریق NotificationCenter به‌روزرسانی می‌شود.

تقویم در Foundation یک نوع مقداری (value type) است. Calendar(identifier:) یک نمونه جدید با پارامترهای ثابت ایجاد می‌کند. تقویم قابل کپی، مقایسه با == و استفاده به عنوان کلید در دیکشنری است. این امکان ایجاد تقویم‌هایی با تنظیمات خاص timeZone و locale را برای تست فراهم می‌کند.

Calendar و NSCalendar

Calendar — نسخه Swift از NSCalendar Objective-C است، با پل as Calendar / as NSCalendar. در Swift مدرن در همه جا از Calendar استفاده می‌شود. NSCalendar برای سازگاری معکوس با API Objective-C باقی مانده است. Calendar مجموعه کاملی از روش‌ها بدون پیشوند NS، با آرگومان‌های type-safe و اختیاری Swift دارد.

Thread Safety — Calendar برای خواندن thread-safe است. نمونه ایجاد شده را می‌توان از چندین thread به طور امن خواند. تغییر ویژگی‌ها (timeZone, locale) thread-safe نیست — برای پیکربندی‌های مختلف نمونه‌های جداگانه Calendar ایجاد کنید.

انواع تقویم‌ها در Foundation

Foundation از 17 سیستم تقویمی از طریق enum Calendar.Identifier پشتیبانی می‌کند. هر سیستم قوانین خاص خود را برای سال‌های کبیسه، تعداد ماه‌ها و شروع عصر دارد. انتخاب تقویم بر تمام محاسبات تأثیر می‌گذارد: dateComponents, dateInterval, nextDate.

سیستم‌های تقویمی اصلی:

  • .gregorian — استاندارد بین‌المللی، 12 ماه، 365/366 روز، عصر میلادی.
  • .buddhist — تقویم بودایی، مورد استفاده در تایلند، کامبوج، لائوس، عصر 543 سال از گرگوری جلوتر است.
  • .japanese — تقویم ژاپنی بر اساس دوران‌های امپراتوری، 12 ماه مانند گرگوری.
  • .hebrew — تقویم عبری، 12/13 ماه، ماه کبیسه آدار دوم.
  • .islamic — تقویم اسلامی، 12 ماه قمری، 354/355 روز.
  • .indian — تقویم ملی هند (ساکا)، 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 — یکی از مفیدترین متدها برای تحلیل و 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 — نیاز به تطابق دقیق دارد.

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 لیست تمام شناسه‌های تقویمی پشتیبانی شده را برمی‌گرداند. static property Calendar.availableCalendarIdentifiers — آرایه‌ای از رشته‌ها با همان شناسه‌ها. برای ساختن UI انتخاب تقویم و بررسی در دسترس بودن یک سیستم تقویمی خاص روی دستگاه استفاده می‌شود.

swift
// تقویم با منطقه زمانی خاص
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 = دوشنبه (در fa_IR)

// لیست تقویم‌های موجود
for identifier in Calendar.availableIdentifiers {
    print(identifier)
}

firstWeekday — ویژگی Calendar که تعیین می‌کند کدام روز هفته اولین محسوب می‌شود. در محل فارسی Sunday = 7 (شنبه اولین). در آمریکایی Sunday = 1. این بر عملکرد weekOfMonth و weekOfYear تأثیر می‌گذارد: تاریخ یکسان ممکن است در محل‌های مختلف به شماره هفته‌های متفاوتی تعلق داشته باشد. برای برنامه‌های دارای تاریخ از Calendar.current استفاده کنید یا firstWeekday را به صراحت تنظیم کنید.

نمونه‌های کار با Calendar

سناریوهای عملی را بررسی می‌کنیم که قابلیت‌های Calendar را نشان می‌دهد. هر مثال یک وظیفه خاص توسعه iOS را حل می‌کند و روش صحیح استفاده از محاسبات تقویمی را نشان می‌دهد.

بررسی: تاریخ در این ماه است؟

Calendar.dateInterval(of: .month, for:) مرزهای ماه جاری را برمی‌گرداند. بررسی قرارگیری Date در این بازه — سریع‌ترین راه برای تعیین تعلق تاریخ به ماه جاری. روش جایگزین — Calendar.compare با granularity .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) برای ماه‌های 31 روزه 1..<32 و برای فوریه سال غیرکبیسه 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: سال, month: 2, day: 29)) != nil — اگر 29 فوریه وجود داشته باشد، سال کبیسه است. Calendar خود قوانین سیستم تقویمی خاص را در نظر می‌گیرد.

آیا می‌توان firstWeekday را پس از ایجاد Calendar تغییر داد؟

بله، ویژگی firstWeekday قابل نوشتن است. تغییر بر weekOfMonth، weekOfYear و تمام محاسبات مربوط به شماره هفته‌ها تأثیر می‌گذارد. هنگام تنظیم locale = Locale(identifier: "fa_IR") firstWeekday به طور خودکار 7 (شنبه) می‌شود. تنظیم دستی مقدار locale را لغو می‌کند.

خلاصه

  • Calendar — کلاس Foundation برای محاسبات تقویمی که Date را از طریق DateComponents با اجزای قابل خواندن برای انسان مرتبط می‌کند.
  • 17 سیستم تقویمی توسط Foundation پشتیبانی می‌شوند — از گرگوری تا بودایی و ژاپنی، با در نظر گرفتن خودکار قوانین منطقه‌ای.
  • Calendar.current مطابق با تقویم سیستمی کاربر است — از آن برای UI و DateFormatter استفاده کنید.
  • Calendar.dateInterval و Calendar.range — متدهای کلیدی برای کار با مرزهای دوره‌ها و محدوده اجزا.
  • Calendar.date(byAdding:) به درستی طول مختلف ماه‌ها و سال‌های کبیسه را پردازش می‌کند — آن را با محاسبات TimeInterval جایگزین نکنید.
  • TimeZone و Locale به عنوان ویژگی‌های Calendar بر تمام محاسبات تقویمی تأثیر می‌گذارند — برای رفتار قابل پیش‌بینی آنها را به صراحت تنظیم کنید.
  • firstWeekday توسط locale تعیین می‌شود و بر شماره هفته‌ها تأثیر می‌گذارد — برای برنامه‌های جهانی آن را به صراحت تنظیم کنید یا از Calendar.current استفاده کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید