DateComponents เป็นโครงสร้าง Foundation ที่จัดเก็บส่วนประกอบของวันที่ในปฏิทินเป็นฟิลด์แยกต่างหาก: ปี, เดือน, วัน, ชั่วโมง, นาที, วินาที และอื่นๆ ต่างจาก Date ที่แสดงถึงช่วงเวลาสัมบูรณ์ DateComponents จะมีค่าที่มนุษย์อ่านได้ซึ่งขึ้นอยู่กับปฏิทินและเขตเวลา ตามเอกสารของ Apple Developer (2025) DateComponents ถูกใช้เป็นตัวเชื่อมกลางระหว่าง Date และ Calendar — ผ่านมัน วันที่ในปฏิทินจะถูกดึงออกมาและสร้างขึ้น การคำนวณและการเลื่อนวันที่จะดำเนินการโดยไม่ต้องใช้เลขคณิตด้วยตนเอง
ประเด็นสำคัญ
DateComponents เป็นชนิดค่า Foundation ที่ออกแบบมาเพื่อจัดเก็บส่วนประกอบเวลาของปฏิทิน แต่ละส่วนประกอบแสดงด้วยฟิลด์ Int ทางเลือก: year, month, day, hour, minute, second, nanosecond, weekday, weekOfMonth, weekOfYear, quarter, yearForWeekOfYear และอื่นๆ
ความแตกต่างหลักจาก Date คือ การผูกกับปฏิทิน Date จัดเก็บเวลาสัมบูรณ์ (จำนวนวินาทีจากวันที่อ้างอิง) ในขณะที่ DateComponents เป็นการแสดงผลที่มนุษย์อ่านได้ซึ่งมีความหมายเฉพาะในบริบทของ Calendar ที่กำหนดเท่านั้น Date เดียวกันสามารถแสดงด้วย DateComponents ที่แตกต่างกันในปฏิทินและเขตเวลาที่ต่างกัน
DateComponents ไม่ใช่ชนิดเวลาอิสระ แต่เป็นคอนเทนเนอร์ข้อมูล ในการแปลความหมาย DateComponents เป็นวันที่ จำเป็นต้องมี Calendar ที่เข้าใจว่าส่วนประกอบเกี่ยวข้องกับระบบปฏิทินอย่างไร Calendar.dateComponents(from: Date) ทำการดึงส่วนประกอบ, Calendar.date(from: DateComponents) ทำการประกอบย้อนกลับ
แต่ละฟิลด์ของ DateComponents เป็นทางเลือก (Int?) ซึ่งเป็นพื้นฐานสำหรับการทำงานกับวันที่บางส่วน หากระบุเฉพาะปีและเดือน Calendar จะเติมฟิลด์ที่ขาดหายไปด้วยค่าเริ่มต้น: วัน = 1, ชั่วโมง = 0, นาที = 0 ซึ่งสะดวกสำหรับการสร้างวันที่เริ่มต้นช่วงเวลา — เพียงระบุส่วนประกอบที่เกี่ยวข้องเท่านั้น
เมื่อเปรียบเทียบ DateComponents ด้วยตัวดำเนินการ == ฟิลด์ที่ระบุเท่านั้น (ไม่ใช่ nil) ที่ถูกเปรียบเทียบ โครงสร้าง DateComponents สองตัวที่มีปี 2026 เหมือนกันแต่เดือนต่างกันถือว่าแตกต่างกัน isEqual จาก NSObjectProtocol ไม่ใช้กับ DateComponents — DateComponents ไม่ได้สืบทอดจาก NSObject
ฟิลด์หลักของ DateComponents ประกอบด้วย year, month, day, hour, minute, second, nanosecond แต่ละฟิลด์จัดเก็บค่าตัวเลขในหน่วยที่เกี่ยวข้อง: ปี — 2026, เดือน — 1..12, วัน — 1..31, ชั่วโมง — 0..23, นาที — 0..59, วินาที — 0..59 นาโนวินาทีสามารถอยู่ในช่วง 0 ถึง 999999999
ฟิลด์สัปดาห์ — weekday (1..7, โดยที่ 1 = วันอาทิตย์ในปฏิทินเกรกอเรียน), weekOfMonth, weekOfYear ฟิลด์เหล่านี้ขึ้นอยู่กับ Calendar และไม่มีความหมายนอกบริบทของมัน weekday ขึ้นอยู่กับการตั้งค่า firstWeekday ของปฏิทิน: ในตำแหน่งที่ตั้งของรัสเซีย สัปดาห์เริ่มต้นในวันจันทร์ (weekday = 2 ในระบบเกรกอเรียน) ในขณะที่ในตำแหน่งที่ตั้งของอเมริกาเริ่มต้นในวันอาทิตย์ (weekday = 1)
ฟิลด์เฉพาะทาง — quarter (1..4), yearForWeekOfYear (ปีที่สัปดาห์นั้นสังกัดอยู่), isLeapMonth (แฟล็กบูลีนสำหรับเดือนอธิกสุรทินในปฏิทินฮีบรูหรือจีน) ฟิลด์ calendar และ timeZone จัดเก็บการอ้างอิงไปยังวัตถุที่เกี่ยวข้องที่โครงสร้างถูกสร้างขึ้นด้วย
| หมวดหมู่ | ฟิลด์ | ช่วง |
|---|---|---|
| ปฏิทิน | year, month, day | 1..∞, 1..12, 1..31 |
| เวลา | hour, minute, second, nanosecond | 0..23, 0..59, 0..59, 0..999999999 |
| สัปดาห์ | weekday, weekOfMonth, weekOfYear | 1..7, 1..5, 1..53 |
| พิเศษ | quarter, yearForWeekOfYear | 1..4, ขึ้นอยู่กับ |
เมื่อดึงส่วนประกอบผ่าน Calendar.dateComponents สิ่งสำคัญคือขอเฉพาะฟิลด์ที่จำเป็นเพื่อประสิทธิภาพ Calendar ดึงฟิลด์ที่ร้องขอทั้งหมดในครั้งเดียว — ซึ่งเร็วกว่าการเรียก Calendar.component สำหรับแต่ละฟิลด์แยกกันอย่างมาก
การเริ่มต้น DateComponents — วิธีที่ง่ายที่สุด: สร้างโครงสร้างว่างและเติมฟิลด์ที่จำเป็น ฟิลด์ที่ไม่ได้ระบุทั้งหมดจะได้รับ nil โดยอัตโนมัติ วันที่createdจากส่วนประกอบบางส่วนจะไม่ถูกตรวจสอบความถูกต้องในขั้นตอนการเริ่มต้น — ข้อผิดพลาดอาจเกิดขึ้นได้เฉพาะเมื่อแปลงเป็น Date ผ่าน Calendar
ตัวเริ่มต้น DateComponents(calendar:timeZone:era:year:month:day:hour:minute:second:nanosecond:weekday:…) อนุญาตให้ตั้งค่าฟิลด์ทั้งหมดในการเรียกครั้งเดียว ตัวเริ่มต้นนี้สะดวกสำหรับการสร้างวันที่สมบูรณ์จากค่าที่เตรียมไว้ แต่ไม่ค่อยได้ใช้กับอาร์กิวเมนต์มากกว่า 5-6 ตัวเนื่องจากความสามารถในการอ่าน
Calendar.dateComponents(_:from:) — วิธีหลักในการรับ DateComponents จาก Date ที่มีอยู่ อาร์กิวเมนต์ที่สองคือชุดของส่วนประกอบที่จะดึง Calendar ดำเนินการคำนวณปฏิทินโดยคำนึงถึงเขตเวลาและส่งคืนโครงสร้างที่มีเฉพาะฟิลด์ที่ร้องขอ ฟิลด์ที่เหลือยังคงเป็น nil
import Foundation
// การสร้างผ่านตัวเริ่มต้นฟิลด์
var components = DateComponents()
components.year = 2026
components.month = 7
components.day = 21
// การดึงจาก Date
let now = Date()
let extracted = Calendar.current.dateComponents(
[.year, .month, .day],
from: now
)
print("Today: \(extracted.day!).\(extracted.month!).\(extracted.year!)")
// การสร้างผ่านตัวเริ่มต้นแบบขยาย
let birthday = DateComponents(
calendar: Calendar.current,
year: 1990, month: 5, day: 15
)
เมื่อสร้าง DateComponents โดยการตั้งค่าฟิลด์ด้วยตนเอง ให้ตรวจสอบ Calendar ก่อนแปลงเป็น Date เสมอ เมื่อแปลง date(from:) Calendar อาจส่งคืน nil หากส่วนประกอบสร้างวันที่ไม่มีอยู่ — ตัวอย่างเช่น 31 กุมภาพันธ์ หรือ 30 กุมภาพันธ์ในปีที่ไม่ใช่อธิกสุรทิน การตรวจสอบวันที่เป็นความรับผิดชอบของ Calendar ไม่ใช่ DateComponents
Calendar.date(from:) — วิธีหลักในการแปลง DateComponents เป็น Date Calendar แปลความหมายส่วนประกอบตามปฏิทินและเขตเวลาของตัวเอง หากไม่ได้ตั้งค่าฟิลด์บางฟิลด์ (nil) Calendar จะใช้ค่าเริ่มต้น: วัน = 1, ชั่วโมง = 0, นาที = 0, วินาที = 0
เมธอดส่งคืน Date แบบทางเลือก — nil เกิดขึ้นเมื่อส่วนประกอบขัดแย้งกันหรือสร้างวันที่ไม่ถูกต้อง สาเหตุทั่วไปของ nil: วันที่ไม่มีอยู่ (32 มกราคม, 29 กุมภาพันธ์ 2023), ฟิลด์ที่ขัดแย้งกัน (weekday=1, day=5 ในชุดเดียวกัน), ปีที่เป็นไปไม่ได้สำหรับปฏิทินที่กำหนด (ปี 0 ในปฏิทินเกรกอเรียน)
DateComponents พร้อม timeZone — ถ้า DateComponents มี timeZone Calendar จะใช้มันระหว่างการแปลง ถ้าไม่ได้ระบุ timeZone Calendar จะใช้ timeZone ปัจจุบันของตัวเอง ถ้า Calendar.timeZone ไม่ตรงกับเขตเวลาที่คาดหวังของวันที่ ผลลัพธ์อาจแตกต่างกันหลายชั่วโมง — ตรวจสอบให้แน่ใจว่า timeZone ถูกตั้งค่าอย่างชัดเจนในวัตถุใดวัตถุหนึ่ง
let calendar = Calendar(identifier: .gregorian)
// การสร้าง Date จาก DateComponents
var comps = DateComponents()
comps.year = 2026
comps.month = 12
comps.day = 25
comps.hour = 10
if let date = calendar.date(from: comps) {
print("Christmas: \(date)")
}
// การสร้างด้วยการระบุ timeZone
calendar.timeZone = TimeZone(identifier: "UTC")!
let utcComps = DateComponents(
calendar: calendar, year: 2026, month: 7, day: 21,
hour: 12
)
let utcDate = calendar.date(from: utcComps)!
Calendar.dateComponents สำหรับความแตกต่างของวันที่ — กรณีการใช้งานอีกอย่างของ DateComponents Calendar.dateComponents([.year, .month, .day], from: Date(), to: futureDate) ส่งคืนความแตกต่างในปี, เดือน และวันระหว่างสองวันที่ นี่เป็นวิธีที่ถูกต้องในการคำนวณอายุแทนที่จะหาร TimeInterval ด้วยจำนวนวินาทีในหนึ่งปี เนื่องจาก Calendar คำนึงถึงปีอธิกสุรทิน
Calendar — คลาสหลักที่ทำงานกับ DateComponents การดำเนินการทั้งหมดในการดึง, ประกอบ และเปรียบเทียบวันที่ผ่าน Calendar หากไม่มี Calendar DateComponents ก็แค่ชุดตัวเลขที่ไม่มีความหมายทางเวลา Calendar ให้การแปลความหมายแก่ส่วนประกอบ: กำหนดว่าเดือน 2 คือกุมภาพันธ์ และ weekday 2 คือวันจันทร์
Calendar.nextDate และ Calendar.enumerateDates — สองเมธอดที่อิงตาม DateComponents nextDate(after: Date(), matching: DateComponents) ค้นหาวันที่ถัดไปที่ตรงกับส่วนประกอบที่ระบุ — ตัวอย่างเช่น วันจันทร์ถัดไปหลังจากวันนี้ enumerateDates(startingAfter:matching:matchingPolicy:using:) วนซ้ำวันที่ทั้งหมดที่ตรงกับรูปแบบจนถึงขีดจำกัดที่ระบุ
Calendar.dateInterval — เมธอดที่ส่งคืน DateInterval สำหรับส่วนประกอบที่ระบุ dateInterval(of: .month, for: Date()) ส่งคืนจุดเริ่มต้นและจุดสิ้นสุดของเดือนปัจจุบัน ภายใน เมธอดนี้ใช้ DateComponents เพื่อค้นหาขอบเขตช่วงเวลา: สร้าง DateComponents ด้วยวันแรกและวันสุดท้ายของเดือนและแปลงเป็น Date ผ่าน Calendar
let calendar = Calendar.current
// วันจันทร์หน้า
let nextMonday = calendar.nextDate(
after: Date(),
matching: DateComponents(weekday: 2),
matchingPolicy: .nextTime
)!
// ความแตกต่างระหว่างวันที่ในหน่วยวัน
let diff = calendar.dateComponents(
[.day], from: Date(), to: nextMonday
)
// ช่วงเดือน
let monthInterval = calendar.dateInterval(
of: .month, for: Date()
)!
let startOfMonth = monthInterval.start
let endOfMonth = monthInterval.end
MatchingPolicy — พารามิเตอร์สำคัญของเมธอด Calendar เมื่อทำงานกับ DateComponents strictPolicy ต้องการการตรงกันที่แน่นอนของส่วนประกอบทั้งหมด, nextTimePolicy เลือกการตรงกันตามเวลาถัดไป, nextTimePreservingSmallerComponents รักษาส่วนประกอบที่เล็กกว่า (นาที, วินาที) จากวันที่ต้นทาง การเลือกนโยบายส่งผลต่อผลลัพธ์ของการค้นหาวันที่ โดยเฉพาะเมื่อเลื่อนผ่านการเปลี่ยนแปลงเวลาออมแสง
มาสำรวจกรณีการใช้งานจริงของ DateComponents ในแอปพลิเคชันกัน แต่ละตัวอย่างแสดงงานทั่วไปที่นักพัฒนา iOS พบเมื่อทำงานกับวันที่ในปฏิทิน
Calendar.nextDate กับ DateComponents(day: 1) ค้นหาวันแรกของเดือนถัดไป Calendar กำหนดจำนวนวันในเดือนปัจจุบันโดยอัตโนมัติและย้ายไปเดือนถัดไป สำหรับการแจ้งเตือนที่เกิดขึ้นซ้ำ ให้ใช้ enumerateDates หรือ Combine.Timer ด้วยคีย์ Calendar
func firstDayOfNextMonth(from date: Date) -> Date {
let calendar = Calendar.current
let comps = DateComponents(day: 1)
return calendar.nextDate(
after: date,
matching: comps,
matchingPolicy: .nextTime
)!
}
// การคำนวณอายุในหน่วยปี
func ageInYears(from birthDate: Date) -> Int {
let calendar = Calendar.current
let ageComponents = calendar.dateComponents(
[.year], from: birthDate, to: Date()
)
return ageComponents.year ?? 0
}
// การจัดกลุ่มเหตุการณ์ตามปีและเดือน
func groupEventsByMonth(_ events: [Event]) -> [String: [Event]] {
let calendar = Calendar.current
return Dictionary(grouping: events) { event in
let comps = calendar.dateComponents(
[.year, .month], from: event.date
)
return "\(comps.year!)-\(comps.month!)"
}
}
การคำนวณอายุ ผ่าน Calendar.dateComponents([.year], from:to:) — วิธีที่ถูกต้องเพียงวิธีเดียวที่คำนึงถึงปีอธิกสุรทิน การคำนวณแบบ TimeInterval (วินาที / 31536000) ให้ข้อผิดพลาดสำหรับผู้ที่เกิดวันที่ 29 กุมภาพันธ์ Calendar กำหนดอย่างถูกต้องว่าวันเกิดเกิดขึ้นในปีปัจจุบันหรือไม่และส่งคืนอายุที่แน่นอน
การจัดกลุ่มตามปีและเดือน — งานทั่วไปสำหรับหน้าจอประวัติหรือปฏิทิน DateComponents ทำหน้าที่เป็นคีย์การจัดกลุ่ม: ดึงปีและเดือนจากวันที่ของเหตุการณ์ สร้างคีย์สตริง และจัดกลุ่มผ่าน Dictionary(grouping:) สำหรับการแสดงผล ให้ใช้ DateFormatter ด้วยเทมเพลต "LLLL yyyy" สำหรับชื่อเดือนที่แปลเป็นภาษาท้องถิ่น
| งาน | เมธอด Calendar | บทบาท DateComponents |
|---|---|---|
| วันแรกของเดือน | nextDate(after:matching:) | day: 1 |
| การคำนวณอายุ | dateComponents(from:to:) | [.year] จากความแตกต่าง |
| การจัดกลุ่มวันที่ | dateComponents(_:from:) | คีย์ year + month |
| การค้นหาวันในสัปดาห์ | nextDate(after:matching:) | weekday: N |
คำถามที่พบบ่อย
สาเหตุ: วันที่ไม่มีอยู่ (31 เมษายน), ฟิลด์ที่ขัดแย้งกัน (weekday=1 กับ day=5), การรวมฟิลด์ที่ไม่ถูกต้องสำหรับปฏิทินที่เลือก Calendar พยายามแปลความหมายส่วนประกอบในระบบของมัน — หากการรวมกันเป็นไปไม่ได้ ผลลัพธ์คือ nil ใช้ guard let หรือ if let เสมอเมื่อทำการแปลง
ได้ ผ่านตัวดำเนินการ == DateComponents ใช้ Equatable เปรียบเทียบทุกฟิลด์ โครงสร้างสองตัวเท่ากันถ้าฟิลด์ทั้งหมดของพวกมันเท่ากัน (nil == nil ถือว่าเป็นจริง) หากต้องการเปรียบเทียบเฉพาะฟิลด์บางส่วน — ให้ดึงชุดเดียวกันผ่าน Calendar.dateComponents
Date เป็นช่วงเวลาสัมบูรณ์ที่ไม่มีการผูกกับปฏิทิน DateComponents เป็นชุดตัวเลขที่มนุษย์อ่านได้ (ปี, เดือน, วัน) ซึ่งมีความหมายเฉพาะในบริบทของ Calendar เท่านั้น Date สามารถเปรียบเทียบ, ลบ, ทำให้เป็นลำดับเป็น ISO 8601 ได้ DateComponents เป็นการแสดงผลระดับกลางสำหรับการโต้ตอบกับปฏิทิน
ตั้งค่าเฉพาะฟิลด์ year และ month ปล่อยให้ฟิลด์อื่นเป็น nil เมื่อแปลงเป็น Date ผ่าน Calendar.date(from:) Calendar จะตั้งค่าโดยอัตโนมัติ: วัน = 1, ชั่วโมง = 0, นาที = 0 ผลลัพธ์คือ Date ที่ตรงกับวันแรกของเดือนที่ระบุเวลาเที่ยงคืน
DateComponents ไม่จัดเก็บข้อมูลเขตเวลาในฟิลด์ของมัน — ค่าฟิลด์ (ปี, เดือน, วัน) ขึ้นอยู่กับ timeZone ที่พวกมันถูกดึงมา ส่วนประกอบ "21 กรกฎาคม 2026 14:00 MSK" และ "21 กรกฎาคม 2026 10:00 UTC" แสดงถึง Date เดียวกัน แต่ฟิลด์ DateComponents ต่างกัน
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม