Calendar — adalah kelas Foundation yang mendefinisikan sistem kalender dan menyediakan metode untuk perhitungan kalender: ekstraksi komponen tanggal, perhitungan selisih antar tanggal, pencarian batas periode, dan pergeseran tanggal. Kalender menghubungkan waktu absolut (Date) dengan komponen yang dapat dibaca manusia dan mempertimbangkan fitur regional: awal minggu, zona waktu, dan waktu musim panas. Menurut Apple Developer Documentation (2025), Foundation mendukung 17 sistem kalender — dari Gregorian hingga Buddha dan Jepang, menjadikan Calendar alat universal untuk aplikasi yang diinternasionalisasi.
Poin Utama
Calendar — adalah kelas Foundation yang mengimplementasikan perhitungan kalender berdasarkan ICU (International Components for Unicode). Kalender menentukan bagaimana waktu absolut (Date) dipetakan ke komponen kalender: tahun, bulan, hari, jam, menit, detik. Tanpa Calendar, tidak mungkin mengetahui tahun, bulan, dan hari apa sekarang — Date sendiri tidak mengandung informasi ini.
Kalender mempertimbangkan tiga kelompok parameter: sistem kalender (Gregorian, Buddha, Jepang), zona waktu, dan lokal. Calendar.current menggabungkan ketiganya dari pengaturan sistem pengguna. Calendar.autoupdatingCurrent — versi khusus yang diperbarui secara otomatis saat pengaturan berubah tanpa memulai ulang aplikasi melalui NotificationCenter.
Kalender adalah tipe nilai (value type) di Foundation. Calendar(identifier:) membuat instance baru dengan parameter tetap. Kalender dapat disalin, dibandingkan dengan ==, dan digunakan sebagai kunci dalam kamus. Ini memungkinkan pembuatan kalender dengan pengaturan timeZone dan locale tertentu untuk pengujian.
Calendar — versi Swift dari NSCalendar Objective-C, dengan jembatan as Calendar / as NSCalendar. Di Swift modern, Calendar digunakan di mana-mana. NSCalendar tetap untuk kompatibilitas mundur dengan API Objective-C. Calendar memiliki set lengkap metode tanpa awalan NS, dengan argumen type-safe dan opsionalitas Swift.
Thread Safety — Calendar aman untuk dibaca dari banyak thread. Instance yang dibuat dapat dibaca dengan aman dari beberapa thread. Modifikasi properti (timeZone, locale) tidak aman untuk thread — buat instance Calendar terpisah untuk konfigurasi yang berbeda.
Foundation mendukung 17 sistem kalender melalui enumerasi Calendar.Identifier. Setiap sistem memiliki aturan sendiri untuk tahun kabisat, jumlah bulan, dan awal era. Pilihan kalender mempengaruhi semua perhitungan: dateComponents, dateInterval, nextDate.
Sistem kalender utama:
Calendar(identifier: .gregorian) — paling sering digunakan. Sesuai dengan standar internasional ISO 8601 dan merupakan kalender default di sebagian besar negara. Untuk aplikasi dengan audiens internasional, gunakan Calendar.current — secara otomatis sesuai dengan kalender sistem pengguna.
| Pengidentifikasi | Tipe | Wilayah Penggunaan |
|---|---|---|
| .gregorian | Matahari | Internasional |
| .buddhist | Matahari | Thailand, Kamboja |
| .japanese | Matahari | Jepang |
| .hebrew | Bulan-Matahari | Israel |
| .islamic | Bulan | Negara Islam |
| .chinese | Bulan-Matahari | Cina |
DateComponents dan Calendar — pasangan yang tak terpisahkan. Calendar.dateComponents(_:from:) mengekstrak komponen dari Date dengan mempertimbangkan zona waktu kalender. Calendar.date(from:) membangun Date dari DateComponents, mengisi bidang yang hilang dengan nilai default: hari = 1, jam = 0, menit = 0, detik = 0.
Metode Calendar.component mengekstrak satu komponen, yang nyaman untuk pemeriksaan cepat. Calendar.dateComponents mengekstrak satu set komponen dalam satu panggilan — ini lebih efisien karena Calendar melakukan perhitungan kalender sekali, bukan untuk setiap komponen secara terpisah. Untuk daftar 3+ komponen, selalu gunakan dateComponents.
Calendar.compare membandingkan dua Date dengan presisi yang ditentukan. Parameter toGranularity menentukan hingga komponen mana perbandingan dilakukan: .year membandingkan hanya tahun, .month — tahun dan bulan, .day — tahun, bulan, hari. Ini nyaman untuk memeriksa apakah dua tanggal termasuk dalam hari yang sama, tanpa memperhitungkan waktu.
let calendar = Calendar.current
let now = Date()
// Ekstraksi satu komponen
let year = calendar.component(.year, from: now)
// Ekstraksi satu set komponen
let comps = calendar.dateComponents(
[.year, .month, .day], from: now
)
// Perbandingan dengan presisi hari
let isSameDay = calendar.compare(date1, to: date2,
toGranularity: .day) == .orderedSame
// Periksa apakah tanggal hari ini
let isToday = calendar.isDateInToday(someDate)
Calendar.isDateInToday, isDateInTomorrow, isDateInYesterday — metode untuk pemeriksaan relatif. Calendar.isDate(_:inSameDayAs:) memeriksa apakah dua tanggal jatuh pada hari kalender yang sama dengan mempertimbangkan zona waktu kalender. Metode ini menggunakan Calendar.compare secara internal dan dioptimalkan untuk panggilan yang sering.
Calendar.dateInterval — salah satu metode paling berguna untuk analitik dan UI. Mengembalikan DateInterval untuk komponen yang ditentukan: awal dan akhir hari, minggu, bulan, tahun. DateInterval berisi start (Date) dan end (Date) — batas periode. Misalnya, dateInterval(of: .weekOfYear, for: Date()) mengembalikan awal Senin dan akhir Minggu dari minggu saat ini.
Calendar.date dengan byAdding — metode untuk pergeseran tanggal. Calendar.date(byAdding: .day, value: 7, to: Date()) mengembalikan tanggal seminggu lagi. Calendar.date(byAdding: DateComponents) — versi yang lebih fleksibel, memungkinkan pergeseran beberapa komponen sekaligus: +1 bulan +3 hari. Calendar secara otomatis mempertimbangkan panjang bulan yang berbeda dan tahun kabisat.
Calendar.nextDate mencari tanggal berikutnya yang cocok dengan DateComponents yang ditentukan. Parameter matchingPolicy menentukan perilaku saat tidak cocok: .nextTime — kecocokan berikutnya dalam waktu, .nextTimePreservingSmallerComponents — mempertahankan menit dan detik dari tanggal asli, .strict — memerlukan kecocokan yang tepat.
let calendar = Calendar.current
let today = Date()
// Awal dan akhir minggu
let weekInterval = calendar.dateInterval(
of: .weekOfYear, for: today
)!
// Pergeseran 1 bulan
let nextMonth = calendar.date(
byAdding: .month, value: 1, to: today
)!
// Pergeseran melalui DateComponents
var delta = DateComponents()
delta.month = 1
delta.day = 3
let shifted = calendar.date(byAdding: delta, to: today)!
// Jumat berikutnya tanggal 13
let friday13Components = DateComponents(
weekday: 6, day: 13
)
let nextFriday13 = calendar.nextDate(
after: today, matching: friday13Components,
matchingPolicy: .nextTime
)
EnumerateDates — metode yang kuat untuk mengulang tanggal berdasarkan pola. Calendar.enumerateDates(startingAfter:matching:matchingPolicy:using:) memanggil blok untuk setiap kecocokan sampai blok mengembalikan stop = true. Digunakan untuk menghasilkan acara berulang di kalender dan jadwal. Metode ini lebih efisien daripada loop manual dengan nextDate karena dioptimalkan oleh ICU.
TimeZone — bagian tak terpisahkan dari Calendar. Zona waktu menentukan waktu kalender mana yang sesuai dengan Date absolut. Date yang sama di UTC dan di Moskow memberikan komponen yang berbeda: Date() di UTC mungkin menunjukkan 10:00, dan di MSK — 13:00. Calendar.timeZone secara default sama dengan TimeZone.current.
Locale mempengaruhi hari pertama dalam seminggu, jumlah minimum hari di minggu pertama tahun (minDaysInFirstWeek), dan nama bulan/hari dalam seminggu (saat konversi melalui DateFormatter). Calendar.locale secara default sama dengan Locale.current. Di lokal Indonesia, minggu dimulai pada hari Senin, di lokal Amerika — pada hari Minggu.
Calendar.availableIdentifiers mengembalikan daftar semua pengidentifikasi kalender yang didukung. static property Calendar.availableCalendarIdentifiers — array string dengan pengidentifikasi yang sama. Digunakan untuk membangun UI pemilihan kalender dan untuk memeriksa ketersediaan sistem kalender tertentu pada perangkat.
// Kalender dengan zona waktu tertentu
var utcCalendar = Calendar(identifier: .gregorian)
utcCalendar.timeZone = TimeZone(identifier: "UTC")!
// Kalender dengan lokal Rusia
var russianCalendar = Calendar(identifier: .gregorian)
russianCalendar.locale = Locale(identifier: "ru_RU")
// Hari pertama minggu tergantung lokal
let firstWeekday = russianCalendar.firstWeekday
// 2 = Senin (di id_ID)
// Daftar kalender yang tersedia
for identifier in Calendar.availableIdentifiers {
print(identifier)
}
firstWeekday — properti Calendar yang menentukan hari mana dalam seminggu yang dianggap pertama. Di lokal Indonesia, Sunday = 2 (Senin pertama). Di lokal Amerika, Sunday = 1. Ini mempengaruhi cara kerja weekOfMonth dan weekOfYear: tanggal yang sama dapat ditetapkan ke nomor minggu yang berbeda di lokal yang berbeda. Untuk aplikasi dengan tanggal, gunakan Calendar.current atau atur firstWeekday secara eksplisit.
Mari kita lihat skenario praktis yang menunjukkan kemampuan Calendar. Setiap contoh menyelesaikan tugas spesifik pengembangan iOS dan menunjukkan cara yang benar dalam menggunakan perhitungan kalender.
Calendar.dateInterval(of: .month, for:) mengembalikan batas bulan saat ini. Memeriksa apakah Date termasuk dalam interval ini — cara tercepat untuk menentukan apakah tanggal termasuk dalam bulan saat ini. Cara alternatif — Calendar.compare dengan granularity .month: jika hasilnya .orderedSame, maka bulannya cocok.
func isInCurrentMonth(_ date: Date) -> Bool {
let calendar = Calendar.current
let monthInterval = calendar.dateInterval(
of: .month, for: Date()
)!
return monthInterval.contains(date)
}
// Jumlah hari dalam sebulan
func daysInMonth(for date: Date) -> Int {
let calendar = Calendar.current
return calendar.range(
of: .day, in: .month, for: date
)?.count ?? 0
}
// Penambahan bulan dengan penyelesaian yang benar
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:) mengembalikan rentang nilai yang diizinkan untuk komponen yang ditentukan dalam konteks komponen lain. Misalnya, range(of: .day, in: .month, for: date) mengembalikan 1..<32 untuk bulan dengan 31 hari atau 1..<29 untuk Februari tahun non-kabisat. Ini adalah cara yang benar untuk mengetahui jumlah hari dalam sebulan, bukan menggunakan nilai hardcode.
Menambahkan bulan melalui Calendar.date(byAdding:value:to:) menangani tanggal batas dengan benar. Jika 31 Januari ditambahkan 1 bulan, Calendar mengembalikan 28 Februari (atau 29 di tahun kabisat), bukan 3 Maret, seperti yang terjadi jika menambahkan 30 hari melalui TimeInterval. Ini adalah alasan lain untuk tidak menggunakan TimeInterval untuk perhitungan kalender.
| Metode Calendar | Tujuan | Contoh |
|---|---|---|
| dateInterval | Batas periode | Awal dan akhir bulan |
| range(of:in:for:) | Rentang komponen | Hari di bulan saat ini |
| date(byAdding:) | Pergeseran tanggal | +1 bulan dari hari ini |
| isDateInToday | Pemeriksaan hari ini | Apakah tanggal hari ini |
| compare(toGranularity:) | Perbandingan dengan presisi | Hari yang sama tanpa waktu |
Pertanyaan yang Sering Diajukan
Calendar.current mengembalikan kalender dari pengaturan sistem pengguna — mungkin bukan Gregorian (misalnya, Buddha di Thailand). Calendar(identifier: .gregorian) selalu membuat kalender Gregorian terlepas dari pengaturan. Untuk menampilkan tanggal, gunakan Calendar.current, untuk logika bisnis — pengidentifikasi yang dipilih secara eksplisit.
Ini terkait dengan panjang bulan yang berbeda. Jika tanggal saat ini adalah 31 Januari, penambahan 1 bulan menghasilkan 28 Februari, karena Februari tidak memiliki 31 hari. Calendar secara otomatis menyelesaikan tanggal hingga hari terakhir yang diizinkan dalam bulan tersebut. Untuk kontrol yang tepat, gunakan DateComponents dengan day: 1 untuk pindah ke hari pertama bulan.
DateFormatter menggunakan Calendar.current — kalender sistem pengguna. Jika aplikasi harus selalu menampilkan tanggal dalam kalender Gregorian terlepas dari pengaturan, atur formatter.calendar = Calendar(identifier: .gregorian). Ini menjamin tampilan yang seragam untuk semua pengguna.
Calendar.range(of: .day, in: .year, for: date) mengembalikan 365 atau 366 hari. Lebih sederhana: Calendar.date(from: DateComponents(year: tahun, month: 2, day: 29)) != nil — jika 29 Februari ada, tahun tersebut adalah tahun kabisat. Calendar sendiri memperhitungkan aturan untuk sistem kalender tertentu.
Ya, properti firstWeekday dapat ditulis. Perubahan mempengaruhi weekOfMonth, weekOfYear, dan semua perhitungan yang terkait dengan nomor minggu. Saat mengatur locale = Locale(identifier: "id_ID"), firstWeekday secara otomatis menjadi 2 (Senin). Pengaturan manual menggantikan nilai dari locale.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga