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 เองไม่มีข้อมูลนี้

ปฏิทินคำนึงถึงพารามิเตอร์สามกลุ่ม: ระบบปฏิทิน (เกรกอเรียน พุทธศักราช ญี่ปุ่น) เขตเวลา และ locale Calendar.current รวมทั้งสามอย่างจากการตั้งค่าระบบของผู้ใช้ Calendar.autoupdatingCurrent เป็นเวอร์ชันพิเศษที่จะอัปเดตอัตโนมัติเมื่อการตั้งค่าเปลี่ยนแปลงโดยไม่ต้องรีสตาร์ทแอปผ่าน NotificationCenter

ปฏิทินเป็น ออบเจกต์แบบ value (value type) ใน Foundation Calendar(identifier:) สร้างอินสแตนซ์ใหม่ที่มีพารามิเตอร์คงที่ Calendar สามารถคัดลอก เปรียบเทียบด้วย == และใช้เป็นคีย์ใน dictionary ได้ ทำให้สามารถสร้างปฏิทินที่มีการตั้งค่า timeZone และ locale เฉพาะสำหรับการทดสอบ

Calendar และ NSCalendar

Calendar คือเวอร์ชัน Swift ของ NSCalendar ใน Objective-C ซึ่งเชื่อมต่อกันด้วย as Calendar / as NSCalendar ใน Swift สมัยใหม่จะใช้ Calendar ทุกที่ NSCalendar ยังคงไว้สำหรับความเข้ากันได้ย้อนหลังกับ API ของ Objective-C Calendar มีชุดเมธอดครบถ้วนโดยไม่มีคำนำหน้า NS พร้อมอาร์กิวเมนต์แบบ type-safe และ Swift optionality

Thread Safety — Calendar ปลอดภัยต่อเธรดสำหรับการอ่าน อินสแตนซ์ที่สร้างแล้วสามารถอ่านจากหลายเธรดได้อย่างปลอดภัย การแก้ไขคุณสมบัติ (timeZone, locale) ไม่ปลอดภัยต่อเธรด — สำหรับการกำหนดค่าที่แตกต่างกันควรสร้างอินสแตนซ์ 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 — ปฏิทินประจำชาติอินเดีย (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 เป็นหนึ่งในเมธอดที่มีประโยชน์ที่สุดสำหรับ analytics และ 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 ใน locale รัสเซียสัปดาห์เริ่มต้นด้วยวันจันทร์ ในอเมริกัน — วันอาทิตย์

Calendar.availableIdentifiers คืนค่ารายการของตัวระบุปฏิทินที่รองรับทั้งหมด static property Calendar.availableCalendarIdentifiers — อาร์เรย์ของสตริงที่มีตัวระบุเดียวกัน ใช้สำหรับสร้าง UI เลือกปฏิทินและตรวจสอบความพร้อมใช้งานของระบบปฏิทินเฉพาะบนอุปกรณ์

swift
// Calendar ที่มีเขตเวลาเฉพาะ
var utcCalendar = Calendar(identifier: .gregorian)
utcCalendar.timeZone = TimeZone(identifier: "UTC")!

// Calendar ที่มี locale รัสเซีย
var russianCalendar = Calendar(identifier: .gregorian)
russianCalendar.locale = Locale(identifier: "ru_RU")

// วันแรกของสัปดาห์ขึ้นอยู่กับ locale
let firstWeekday = russianCalendar.firstWeekday
// 2 = วันจันทร์ (ใน ru_RU)

// รายการปฏิทินที่พร้อมใช้งาน
for identifier in Calendar.availableIdentifiers {
    print(identifier)
}

firstWeekday — คุณสมบัติของ Calendar ที่กำหนดว่าวันใดของสัปดาห์ถือเป็นวันแรก ใน locale รัสเซีย Sunday = 2 (วันจันทร์เป็นวันแรก) ในอเมริกัน Sunday = 1 ซึ่งส่งผลต่อการทำงานของ weekOfMonth และ weekOfYear: วันที่เดียวกันอาจถูกจัดให้อยู่ในหมายเลขสัปดาห์ที่ต่างกันในแต่ละ locale สำหรับแอปที่ใช้งานวันที่ ใช้ 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) คืนค่า 1..<32 สำหรับเดือนที่มี 31 วัน หรือ 1..<29 สำหรับเดือนกุมภาพันธ์ของปีที่ไม่ใช่อธิกสุรทิน นี่คือวิธีที่ถูกต้องในการหาจำนวนวันในเดือน ไม่ใช่การเขียนค่าแบบ hardcode

การเพิ่มเดือน ผ่าน Calendar.date(byAdding:value:to:) จัดการกับวันที่ปลายขอบอย่างถูกต้อง หากเพิ่ม 1 เดือนจากวันที่ 31 มกราคม 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 ถูกกำหนดโดย locale และมีผลต่อหมายเลขสัปดาห์ — สำหรับแอประดับโลกให้กำหนดอย่างชัดเจนหรือใช้ Calendar.current

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม