DateIntervalFormatter เป็นคลาส Foundation ใน iOS และ macOS ที่ออกแบบมาสำหรับจัดรูปแบบช่วงวันที่เป็นสตริงที่แปลเป็นภาษาท้องถิ่น: “21–25 กรกฎาคม 2024”, “ตั้งแต่วันที่ 21 ถึง 25 กรกฎาคม 2024” หรือ “Jul 21–25, 2024” ตาม Apple Developer Documentation, 2024 DateIntervalFormatter จะกำหนดโดยอัตโนมัติว่าควรทำซ้ำส่วนประกอบวันที่ใด (หากช่วงเวลาอยู่ในเดือนเดียวกัน เดือนจะแสดงเพียงครั้งเดียว) และแปลภาษาตัวคั่นและลำดับคำ ซึ่งแตกต่างจากการต่อวันที่จัดรูปแบบแล้วสองวันด้วยตนเอง คลาสนี้คำนึงถึงกฎไวยากรณ์ของภาษา คลาสนี้พร้อมใช้งานตั้งแต่ iOS 8 และ macOS 10.10
ประเด็นสำคัญ
DateIntervalFormatter เป็นคลาสย่อยของ Formatter ใน Foundation ที่รับออบเจ็กต์ DateInterval (ช่วงระหว่างวันที่สองวัน) และส่งคืนสตริงที่แปลภาษาแล้วซึ่งอธิบายช่วงเวลานั้น ตัวอย่างเช่น สำหรับช่วงวันที่ 21 ถึง 25 กรกฎาคม 2024 ผลลัพธ์จะเป็น “21–25 กรกฎาคม 2024” สำหรับ th_TH คลาสจะจัดกลุ่มองค์ประกอบที่ซ้ำกันอย่างชาญฉลาด: หากวันที่ทั้งสองอยู่ในเดือนเดียวกัน ชื่อเดือนจะแสดงเพียงครั้งเดียว
คุณค่าหลักของ DateIntervalFormatter คือการจัดการไวยากรณ์ช่วงโดยอัตโนมัติ เมื่อจัดรูปแบบด้วยตนเอง นักพัฒนาจะต้องเปรียบเทียบเดือน/ปีเริ่มต้นและสิ้นสุด เลือกตัวคั่น (ขีดกลาง, “ถึง”, “bis”) และจัดการกับการผันคำ DateIntervalFormatter ทำสิ่งนี้โดยอัตโนมัติโดยอิงจากข้อมูล CLDR (Common Locale Data Repository) ซึ่งช่วยลดข้อบกพร่องในแอปพลิเคชันระหว่างประเทศ โดยเฉพาะอย่างยิ่งสำหรับภาษาที่มีไวยากรณ์ซับซ้อน
ตาม Unicode CLDR v44 (2024) กฎการจัดรูปแบบช่วงวันที่แตกต่างกันอย่างมีนัยสำคัญระหว่างภาษา เยอรมันใช้คำบุพบท “bis” (21.–25. Juli 2024) ฝรั่งเศสใช้ “au” (du 21 au 25 juillet 2024) ญี่ปุ่นใช้ยัติภังค์ (2024 Jul 21–25) DateIntervalFormatter ครอบคลุมรูปแบบต่างๆ เหล่านี้ทั้งหมดโดยไม่ต้องใช้โค้ดเพิ่มเติมจากนักพัฒนา
หลักการทำงาน ของ DateIntervalFormatter ขึ้นอยู่กับการวิเคราะห์วันที่สองวันซึ่งประกอบเป็นช่วงเวลา และการเลือกการแสดงสตริงที่เหมาะสมที่สุดโดยพิจารณาจากตำแหน่งที่ตั้ง คลาสจะเปรียบเทียบส่วนประกอบวันที่ (ปี เดือน วัน) และตัดสินใจจัดกลุ่ม: หากปีเหมือนกันจะแสดงครั้งเดียว หากเดือนเหมือนกันจะแสดงครั้งเดียว หากวันแตกต่างกันจะแสดงช่วงวัน
| ช่วง | th_TH |
|---|---|
| หนึ่งวัน | 21 กรกฎาคม 2024 |
| ภายในเดือน | 21–25 กรกฎาคม 2024 |
| เดือนต่างกัน | 21 กรกฎาคม – 5 สิงหาคม 2024 |
| ปีต่างกัน | 25 ธันวาคม 2024 – 5 มกราคม 2025 |
| รวมเวลา | 21 กรกฎาคม 2024, 14:30–16:45 |
ตรรกะการจัดกลุ่ม ถูกนำไปใช้ตามเทมเพลต CLDR ตัวจัดรูปแบบจะตรวจสอบความเท่าเทียมกันของแต่ละส่วนประกอบวันที่ (Calendar.Component: year, month, day) ตามลำดับจากมากไปน้อย เมื่อส่วนประกอบใดแตกต่างกัน ส่วนประกอบที่เล็กกว่าทั้งหมดจะแสดงสำหรับวันที่ทั้งสอง ตัวอย่างเช่น สำหรับ 21–25 กรกฎาคม: ปีตรงกัน เดือนตรงกัน วันแตกต่าง → เดือนแสดงครั้งเดียว วันแสดงสำหรับแต่ละวันที่
หมายเหตุสำคัญ: DateIntervalFormatter ไม่รองรับการปรับแต่งตัวคั่นผ่าน API ตัวคั่นถูกกำหนดโดยตำแหน่งที่ตั้งและไม่สามารถแทนที่ได้ หากต้องการตัวคั่นที่ไม่ได้มาตรฐาน (เช่น “ถึง” แทน “ถึง”) คุณจะต้องจัดรูปแบบแต่ละวันที่ด้วยตนเองโดยใช้ DateFormatter และต่อสตริงด้วยตัวคั่นแบบกำหนดเอง
DateIntervalFormatter มีชุดรูปแบบสองชุด: dateStyle และ timeStyle สำหรับแต่ละปลายของช่วง รูปแบบเหล่านี้ทำงานคล้ายกับ DateFormatter: .none ซ่อนส่วนประกอบ .short ให้รูปแบบสั้น (21/07/2567) .medium (21 ก.ค. 2567) .long (21 กรกฎาคม 2567) .full (วันอาทิตย์ที่ 21 กรกฎาคม 2567) การรวม dateStyle และ timeStyle เข้าด้วยกันสามารถทำให้ได้ระดับรายละเอียดที่แตกต่างกัน
สำคัญ: dateStyle และ timeStyle ใช้กับปลายทั้งสองของช่วงเท่าๆ กัน คุณไม่สามารถตั้งค่ารูปแบบที่แตกต่างกันสำหรับจุดเริ่มต้นและจุดสิ้นสุด (เช่น .medium สำหรับจุดเริ่มต้นและ .short สำหรับจุดสิ้นสุด) หากต้องการรูปแบบที่แตกต่างกัน คุณต้องจัดรูปแบบแต่ละวันที่ด้วย DateFormatter แยกกันและต่อผลลัพธ์
import Foundation
let formatter = DateIntervalFormatter()
// การรวมรูปแบบต่างๆ
let startDate = Date()
let endDate = Date().addingTimeInterval(345600) // +4 วัน
let interval = DateInterval(start: startDate, end: endDate)
// วันที่เท่านั้น รูปแบบกลาง
formatter.dateStyle = .medium
formatter.timeStyle = .none
print("วันที่เท่านั้น: \(formatter.string(from: interval))")
// รูปแบบสั้นรวมเวลา
formatter.dateStyle = .short
formatter.timeStyle = .short
print("สั้นรวมเวลา: \(formatter.string(from: interval))")
// รูปแบบเต็ม
formatter.dateStyle = .full
formatter.timeStyle = .none
print("วันที่เต็ม: \(formatter.string(from: interval))")
การใช้งานพื้นฐาน ของ DateIntervalFormatter รวมถึงการสร้างอินสแตนซ์ การตั้งค่ารูปแบบ และการเรียก string(from:) เมธอดยอมรับ DateInterval ซึ่งเป็นโครงสร้างที่มีคุณสมบัติ start และ end DateInterval สามารถสร้างได้จากค่า Date สองค่าหรือเริ่มต้นด้วย Duration (iOS 16+) หลังจากจัดรูปแบบ ผลลัพธ์ก็พร้อมสำหรับการแสดงผลใน UI โดยไม่ต้องประมวลผลเพิ่มเติม
import Foundation
let calendar = Calendar.current
let now = Date()
// ตัวอย่างช่วงเฉพาะ
let examples: [(title: String, start: Date, end: Date)] = [
(
"2-hour event",
now,
now.addingTimeInterval(7200)
),
(
"Week-long vacation",
now,
now.addingTimeInterval(604800)
),
(
"Cross-year period",
calendar.date(from: DateComponents(
year: 2024, month: 12, day: 25
))!,
calendar.date(from: DateComponents(
year: 2025, month: 1, day: 5
))!
)
]
let intervalFormatter = DateIntervalFormatter()
intervalFormatter.dateStyle = .medium
intervalFormatter.timeStyle = .short
for (title, start, end) in examples {
let interval = DateInterval(start: start, end: end)
let result = intervalFormatter.string(from: interval)
print("\(title): \(result)")
}
// จัดรูปแบบผ่าน Calendar (iOS 16+)
if let nextWeek = calendar.date(
byAdding: .day, value: 7, to: now
) {
let weekInterval = DateInterval(start: now, end: nextWeek)
print("7 วันถัดไป: \(intervalFormatter.string(from: weekInterval))")
}
DateIntervalFormatter และการจองปฏิทิน: ในแอปพลิเคชันการจอง (โรงแรม ตั๋ว) ช่วงวันที่เป็นองค์ประกอบ UI ที่สำคัญ DateIntervalFormatter จัดการช่วงข้ามปีอย่างถูกต้อง (เช่น วันที่ 28 ธันวาคม ถึง 3 มกราคม) โดยจัดกลุ่มเดือนและปีสำหรับแต่ละวันที่แยกกัน สำหรับสถานการณ์ดังกล่าว ขอแนะนำให้ใช้ dateStyle = .medium และ timeStyle = .none ซึ่งเป็นรูปแบบที่อ่านง่ายที่สุดสำหรับวันที่ไม่มีเวลา
import Foundation
let formatter = DateIntervalFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .none
// ตัวอย่างการจอง
struct Booking {
let checkIn: Date
let checkOut: Date
var formattedInterval: String {
let interval = DateInterval(start: checkIn, end: checkOut)
return formatter.string(from: interval)
}
}
let booking = Booking(
checkIn: calendar.date(from: DateComponents(
year: 2024, month: 7, day: 21
))!,
checkOut: calendar.date(from: DateComponents(
year: 2024, month: 7, day: 25
))!
)
print("การจอง: \(booking.formattedInterval)")
// ใช้กับตำแหน่งที่ตั้งต่างๆ
let enFormatter = DateIntervalFormatter()
enFormatter.dateStyle = .medium
enFormatter.timeStyle = .none
enFormatter.locale = Locale(identifier: "en_US")
print("อังกฤษ: \(enFormatter.string(from: interval))")
DateInterval เทียบกับ DateIntervalFormatter: DateInterval เป็นโครงสร้างที่แสดงช่วงวันที่ (เริ่มต้นและสิ้นสุด) DateIntervalFormatter เป็นตัวจัดรูปแบบที่แปลงโครงสร้างนี้เป็นสตริง DateInterval ยังรองรับการตรวจสอบ contains(Date) และ intersection(with:) ซึ่งมีประโยชน์สำหรับตรรกะการจอง (การตรวจสอบวันที่ทับซ้อน)
DateIntervalFormatter แปลภาษาเอาต์พุตผ่านคุณสมบัติ locale โดยค่าเริ่มต้นจะใช้ Locale.current ซึ่งเป็นตำแหน่งที่ตั้งของอุปกรณ์ หากต้องการแสดงช่วงในภาษาอื่น (เช่น เนื้อหาภาษาอังกฤษกับอินเทอร์เฟซภาษาไทย) ให้ตั้งค่า locale อย่างชัดเจน ซึ่งแตกต่างจาก DateFormatter ตรงที่ DateIntervalFormatter ไม่จำเป็นต้องตั้งค่า locale สำหรับข้อมูลฝั่งเซิร์ฟเวอร์ โดยจะใช้ตรรกะการจัดกลุ่มภายในที่อิงตาม CLDR เสมอ
ความแตกต่างทางภาษาในการจัดรูปแบบช่วงวันที่ซับซ้อนกว่าวันที่เดี่ยวๆ อย่างมาก ภาษาไทยใช้ “ถึง”: ตั้งแต่วันที่ 21 ถึง 25 กรกฎาคม ภาษาอังกฤษใช้ขีดกลางธรรมดา: Jul 21–25 ภาษาฝรั่งเศสใช้ “au”: du 21 au 25 juillet DateIntervalFormatter ครอบคลุมรูปแบบต่างๆ เหล่านี้ทั้งหมด
import Foundation
let formatter = DateIntervalFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
let start = Date()
let end = Date().addingTimeInterval(259200) // +3 วัน
let interval = DateInterval(start: start, end: end)
// แสดงตำแหน่งที่ตั้งต่างๆ
let localeIds = ["ru_RU", "en_US", "de_DE", "fr_FR", "ja_JP", "zh_CN"]
for localeId in localeIds {
formatter.locale = Locale(identifier: localeId)
print("\(localeId): \(formatter.string(from: interval))")
}
// การแปลภาษาสำหรับช่วงเวลา
formatter.dateStyle = .none
formatter.timeStyle = .short
let todayStart = Date()
let todayEnd = Date().addingTimeInterval(3600)
let timeInterval = DateInterval(start: todayStart, end: todayEnd)
for localeId in ["ru_RU", "en_US", "de_DE"] {
formatter.locale = Locale(identifier: localeId)
print("\(localeId) เวลา: \(formatter.string(from: timeInterval))")
}
รายละเอียดการแปลภาษา: สำหรับภาษาไทย DateIntervalFormatter ใช้ “ถึง” สำหรับรูปแบบยาว สำหรับรูปแบบสั้น จะใช้ขีดกลาง การเลือกรูปแบบขึ้นอยู่กับบริบท: สำหรับรายการจองใช้ .medium สำหรับมุมมองรายละเอียดใช้ .long หรือ .full
DateIntervalFormatter เติมเต็มช่องว่างการจัดรูปแบบช่วงวันที่ซึ่งคลาส Foundation อื่น ๆ ไม่ครอบคลุม DateFormatter จัดรูปแบบวันที่เดี่ยว RelativeDateTimeFormatter จัดรูปแบบวันที่สัมพัทธ์ (ที่แล้ว/ใน) DateComponentsFormatter จัดรูปแบบระยะเวลา (2 ชั่วโมง 30 นาที) แต่ละคลาสเหล่านี้แก้ปัญหาเฉพาะและไม่สามารถใช้แทนกันได้
| คลาส | วัตถุประสงค์ | ตัวอย่างเอาต์พุต |
|---|---|---|
| DateIntervalFormatter | ช่วงวันที่ | 21–25 กรกฎาคม 2024 |
| DateFormatter | วันที่เดี่ยว | 21 กรกฎาคม 2024 |
| RelativeDateTimeFormatter | เวลาสัมพัทธ์ | 3 วันที่แล้ว |
| DateComponentsFormatter | ระยะเวลา | 2 ชั่วโมง 30 นาที |
| ISO8601DateFormatter | รูปแบบ ISO 8601 | 2024-07-21T14:30:00Z |
เมื่อใดควรใช้ DateIntervalFormatter: สำหรับแสดงช่วงวันที่ใน UI — การจองโรงแรม กิจกรรม การกรองตามวันที่ ประวัติการดำเนินงาน สำหรับการคำนวณระยะเวลาระหว่างวันที่ ให้ใช้ DateComponentsFormatter ไม่ใช่ DateIntervalFormatter เพราะอย่างหลังไม่ได้ออกแบบมาสำหรับแสดงระยะเวลา DateFormatter ควรใช้สำหรับวันที่เดี่ยวเท่านั้น ไม่ใช่สำหรับการต่อวันที่จัดรูปแบบแล้วสองวันผ่านการต่อสตริง เพราะจะทำให้สูญเสียการแปลภาษาของตัวคั่น
กรณีพิเศษ: DateIntervalFormatter ไม่รองรับการแสดงช่วงแบบเปิด (ไม่มีวันที่สิ้นสุด) หากไม่ทราบจุดสิ้นสุดของช่วง (เช่น “ตั้งแต่วันที่ 21 กรกฎาคม”) ให้ใช้ DateFormatter กับ “ตั้งแต่” นอกจากนี้ DateIntervalFormatter ไม่รองรับช่วงที่ระบุเวลาสำหรับวันที่ต่างกัน เวลาจะถูกนำไปใช้กับแต่ละวันที่แยกกัน ซึ่งอาจดูซ้ำซ้อนสำหรับช่วงสั้นๆ
คำถามที่พบบ่อย
DateIntervalFormatter เป็นคลาส Foundation สำหรับจัดรูปแบบช่วงวันที่เป็นสตริงที่แปลภาษา พร้อมใช้งานตั้งแต่ iOS 8 จัดกลุ่มส่วนประกอบที่ซ้ำกัน (เดือน ปี) โดยอัตโนมัติและเลือกตัวคั่นที่ถูกต้องสำหรับภาษา
เปรียบเทียบส่วนประกอบ จากปีถึงวัน หากปีตรงกันจะแสดงครั้งเดียว หากเดือนตรงกันจะแสดงครั้งเดียว วันที่แตกต่างกันจะแสดงเป็นช่วง ทำให้ได้ “21–25 กรกฎาคม 2024” แทน “21 กรกฎาคม 2024 – 25 กรกฎาคม 2024”
ไม่ ตัวคั่นถูกกำหนดโดยตำแหน่งที่ตั้งและไม่สามารถปรับแต่งผ่าน API ได้ สำหรับตัวคั่นแบบกำหนดเอง (เช่น “ถึง” แทน “ถึง”) ให้จัดรูปแบบแต่ละวันที่ด้วย DateFormatter แยกกันและต่อสตริงด้วยตนเอง
DateIntervalFormatter จัดรูปแบบช่วงวันที่ (สองวัน) เป็นหน่วยเดียวกับการจัดกลุ่มส่วนประกอบ DateFormatter จัดรูปแบบวันที่เดี่ยว สำหรับช่วง ให้ใช้ DateIntervalFormatter เสมอเพราะให้การแปลภาษาตัวคั่นที่ถูกต้อง
โดยอัตโนมัติ — DateIntervalFormatter ตรวจจับว่าปีต่างกันและแสดงวันที่เต็มสำหรับทั้งสองปลาย: “25 ธันวาคม 2024 – 5 มกราคม 2025” ไม่จำเป็นต้องกำหนดค่าเพิ่มเติม เพียงตั้งค่า dateStyle
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ