ISO8601DateFormatter เป็นคลาส Foundation ใน iOS และ macOS ที่ออกแบบมาสำหรับการจัดรูปแบบและแยกวิเคราะห์วันที่ตามมาตรฐานสากล ISO 8601 ตาม Apple Developer Documentation, 2024 ISO8601DateFormatter จะจัดการกับรูปแบบที่มีมิลลิวินาที เขตเวลา และเศษส่วนของวินาทีโดยอัตโนมัติ โดยไม่ต้องตั้งค่า DateFormat ด้วยตนเอง แตกต่างจาก DateFormatter คลาสนี้ไม่ขึ้นอยู่กับ Locale และ TimeZone — มันทำงานอย่างเคร่งครัดตามข้อกำหนด ISO 8601 ทำให้เหมาะสำหรับการแลกเปลี่ยนวันที่ระหว่างเซิร์ฟเวอร์และไคลเอ็นต์ คลาสนี้พร้อมใช้งานตั้งแต่ iOS 10 และ macOS 10.12
ประเด็นสำคัญ
ISO8601DateFormatter เป็นคลาสย่อยเฉพาะของ Formatter ใน Foundation ที่ใช้การแปลงสองทิศทางระหว่าง Date และสตริงรูปแบบ ISO 8601 มาตรฐาน ISO 8601 (International Standard for the Representation of Dates and Times) กำหนดรูปแบบสากลสำหรับการแลกเปลี่ยนวันที่และเวลา: 2024-07-21T14:30:00+00:00 แตกต่างจาก DateFormatter คลาสนี้ไม่ต้องระบุ dateFormat และกำหนดโครงสร้างสตริงโดยอัตโนมัติตามตัวเลือกที่กำหนด
ข้อได้เปรียบหลักของ ISO8601DateFormatter เหนือ DateFormatter: ไม่ขึ้นกับภาษา (การแยกวิเคราะห์ทำงานเหมือนกันบนทุกอุปกรณ์), รองรับเศษส่วนของวินาทีในตัว (ด้วยจำนวนตำแหน่งทศนิยมใดๆ) และการตรวจจับรูปแบบอัตโนมัติตามตัวเลือกที่ส่งผ่าน คลาสนี้ยังจัดการกับส่วนต่อท้าย Z (การกำหนด UTC), เขตเวลาในรูปแบบ +HH:mm และความแม่นยำที่ลดลง (เฉพาะวันที่ไม่มีเวลา) ได้อย่างถูกต้อง
ตาม ข้อกำหนด ISO (ISO 8601-1:2019) มาตรฐานรองรับความแม่นยำสี่ระดับ: ปี (2024), ปี-เดือน (2024-07), วันที่เต็ม (2024-07-21) และวันที่-เวลาพร้อมเขตเวลา (2024-07-21T14:30:00+00:00) ISO8601DateFormatter ครอบคลุมทุกระดับเหล่านี้ผ่านการรวมกันของตัวเลือกการจัดรูปแบบ ช่วยให้นักพัฒนาไม่ต้องสร้างสตริง dateFormat ด้วยตนเอง
หลักการทำงาน ของ ISO8601DateFormatter ขึ้นอยู่กับการรวมกันของตัวเลือกบิต (formatOptions) ซึ่งแต่ละตัวจะรวมส่วนประกอบของวันที่หรือเวลาที่เฉพาะเจาะจงในผลลัพธ์ ตัวอย่างเช่น ตัวเลือก .withFullDate รวมปี, เดือนและวัน; .withTime รวมชั่วโมง, นาทีและวินาที การรวมตัวเลือกทำให้นักพัฒนาได้รับระดับความแม่นยำที่ต้องการโดยไม่ต้องเขียนสตริง dateFormat
ภายใน ISO8601DateFormatter ใช้ไลบรารี ICU สำหรับการแยกวิเคราะห์ แต่มีกฎ ISO 8601 ที่ตายตัว ซึ่งหมายความว่ามันไม่สนใจการตั้งค่า Locale และ TimeZone บนอุปกรณ์ — ผลลัพธ์สามารถคาดเดาได้เสมอ สำหรับการตั้งค่าเขตเวลา จะใช้คุณสมบัติ timeZone ซึ่งค่าเริ่มต้นคือ UTC ถ้า timeZone ถูกตั้งค่าเป็น nil จะใช้เวลาท้องถิ่นของอุปกรณ์
| ตัวเลือก | คำอธิบาย | ตัวอย่างผลลัพธ์ |
|---|---|---|
| .withFullDate | ปี, เดือน, วัน | 2024-07-21 |
| .withTime | ชั่วโมง, นาที, วินาที | 14:30:00 |
| .withMilliseconds | เศษส่วนของวินาที (สูงสุด 3 หลัก) | .123 |
| .withFractionalSeconds | เศษส่วนของวินาที (ทุกระดับความแม่นยำ) | .123456 |
| .withTimeZone | เขตเวลา | +03:00 |
| .withColonSeparatorInTimeZone | ตัวคั่นทวิภาคในเขตเวลา | +03:00 (เทียบกับ +0300) |
| .withInternetDateTime | รูปแบบเต็ม (วันที่ + เวลา + โซน) | 2024-07-21T14:30:00+00:00 |
การรวมตัวเลือก: .withInternetDateTime เทียบเท่ากับการรวม .withFullDate, .withTime และ .withTimeZone เข้าด้วยกัน สำหรับการแยกวิเคราะห์สตริงที่มีมิลลิวินาที ให้เพิ่ม .withFractionalSeconds สิ่งสำคัญที่ต้องจำคือ .withMilliseconds จำกัดเศษส่วนของวินาทีไว้ที่สามหลัก ในขณะที่ .withFractionalSeconds รองรับทุกระดับความแม่นยำ — ตั้งแต่หนึ่งถึงเก้าหลักหลังจุดทศนิยม
ตัวเลือกการจัดรูปแบบ ของ ISO8601DateFormatter แบ่งออกเป็นสามกลุ่ม: ส่วนประกอบวันที่ (withFullDate, withYear, withMonth, withDay, withWeekOfYear), ส่วนประกอบเวลา (withTime, withHours, withMinutes, withSeconds) และการตั้งค่าเพิ่มเติม (withMilliseconds, withFractionalSeconds, withTimeZone, withColonSeparatorInTimeZone, withDashSeparatorInDate, withFullTime) โดยการรวมกัน คุณจะได้รับรูปแบบย่อย ISO 8601 ได้แทบทุกแบบ
ความละเอียดสำคัญ: .withFractionalSeconds และ .withMilliseconds เป็นแบบแยกกัน — ถ้าตั้งค่าทั้งสองอย่าง จะใช้ .withFractionalSeconds สำหรับการแยกวิเคราะห์มิลลิวินาทีจากข้อมูลเซิร์ฟเวור แนะนำให้ใช้ .withFractionalSeconds เนื่องจากเซิร์ฟเวอร์หลายแห่งส่งเศษส่วนของวินาทีด้วยสาม, หกหรือเก้าหลัก และ .withFractionalSeconds จัดการกับความยาวใดๆ
import Foundation
// Configure ISO8601DateFormatter
let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)
// Different format option combinations
formatter.formatOptions = [.withFullDate]
let dateOnly = formatter.string(from: Date())
print("Date: \(dateOnly)")
formatter.formatOptions = [.withFullDate, .withTime]
let dateTime = formatter.string(from: Date())
print("DateTime: \(dateTime)")
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let full = formatter.string(from: Date())
print("Full: \(full)")
// Parse string with milliseconds
let serverString = "2024-07-21T14:30:00.123456+03:00"
if let parsed = formatter.date(from: serverString) {
print("Parsed: \(parsed)")
}
การใช้งานพื้นฐาน ของ ISO8601DateFormatter เริ่มต้นด้วยการสร้างอินสแตนซ์, การตั้งค่า timeZone (แนะนำ UTC สำหรับข้อมูลเซิร์ฟเวอร์) และ formatOptions หลังจากนั้นสามารถเรียกใช้ string(from:) สำหรับการจัดรูปแบบและ date(from:) สำหรับการแยกวิเคราะห์ แตกต่างจาก DateFormatter ไม่ต้องกังวลเกี่ยวกับ Locale — คลาสไม่สนใจการตั้งค่าภูมิภาค
import Foundation
let formatter = ISO8601DateFormatter()
// Parse different ISO 8601 formats
let strings: [String] = [
"2024-07-21T14:30:00Z",
"2024-07-21T14:30:00+03:00",
"2024-07-21T14:30:00.123Z",
"2024-07-21"
]
for str in strings {
if let autoParsed = formatter.date(from: str) {
print("Parsed '\(str)': \(autoParsed)")
} else {
// Use withFullDate for date-only strings
formatter.formatOptions = [.withFullDate]
if let fallback = formatter.date(from: str) {
print("Fallback parsed '\(str)': \(fallback)")
}
formatter.formatOptions = [.withInternetDateTime]
}
}
// Serialize to RFC 3339 (GitHub API)
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let rfc3339 = formatter.string(from: Date())
print("RFC 3339: \(rfc3339)")
การแยกวิเคราะห์วันที่ที่มีเศษส่วนของวินาทีความยาวแปรผัน เป็นคุณสมบัติของ API สมัยใหม่หลายตัว เซิร์ฟเวอร์อาจส่ง 2024-07-21T14:30:00.123Z (3 หลัก) หรือ 2024-07-21T14:30:00.123456Z (6 หลัก) ISO8601DateFormatter ที่มีตัวเลือก .withFractionalSeconds จะจัดการกับทั้งสองรูปแบบได้อย่างถูกต้อง ในขณะที่ DateFormatter ที่มี dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" จะจัดการได้เฉพาะมิลลิวินาทีสามหลักเท่านั้น
import Foundation
let variantFormatter = ISO8601DateFormatter()
variantFormatter.formatOptions = [
.withInternetDateTime,
.withFractionalSeconds
]
// Different fractional second precision
let variants: [String] = [
"2024-07-21T14:30:00.1Z",
"2024-07-21T14:30:00.12Z",
"2024-07-21T14:30:00.123Z",
"2024-07-21T14:30:00.123456Z",
"2024-07-21T14:30:00.123456789Z"
]
for variant in variants {
if let parsed = variantFormatter.date(from: variant) {
print("OK: \(variant) -> \(parsed)")
} else {
print("FAIL: \(variant)")
}
}
// Use withMilliseconds (3 digits only)
variantFormatter.formatOptions = [
.withInternetDateTime,
.withMilliseconds
]
let milliParsed = variantFormatter.string(from: Date())
print("With milliseconds: \(milliParsed)")
การทดสอบการแยกวิเคราะห์ทุกรูปแบบ: โค้ดที่แสดงให้เห็นว่า ISO8601DateFormatter ที่มี .withFractionalSeconds จัดการเศษส่วนของวินาทีทุกระดับความยาวตั้งแต่ 1 ถึง 9 หลักได้สำเร็จ ซึ่งสำคัญสำหรับความเข้ากันได้กับแพลตฟอร์มเซิร์ฟเวอร์ต่างๆ: .NET มักสร้าง 7 หลัก (100 นาโนวินาที), Python — 6, Java — 3 หรือ 9 ขึ้นอยู่กับเวอร์ชัน
DateFormatter สามารถแยกวิเคราะห์ ISO 8601 ได้เช่นกัน แต่ต้องกำหนดค่า dateFormat, locale และ timeZone ด้วยตนเอง ปัญหาหลักคือ DateFormatter ขึ้นอยู่กับ Locale และถ้าไม่ได้ตั้งค่า en_US_POSIX การแยกวิเคราะห์อาจล้มเหลวสำหรับผู้ใช้จากภูมิภาคที่มีรูปแบบวันที่ไม่เป็นมาตรฐาน ISO8601DateFormatter แก้ปัญหานี้ในระดับสถาปัตยกรรม: มันไม่ใช้ Locale
| พารามิเตอร์ | ISO8601DateFormatter | DateFormatter |
|---|---|---|
| การกำหนดค่า Locale | ไม่ต้อง (ไม่สนใจ) | จำเป็นต้องใช้ en_US_POSIX |
| DateFormat | อัตโนมัติ (ผ่านตัวเลือก) | สตริงรูปแบบที่กำหนดเอง |
| เศษส่วนของวินาที | ทุกระดับความแม่นยำ (.withFractionalSeconds) | SSS ตายตัว |
| ส่วนต่อท้าย Z | จัดการอย่างถูกต้อง | ผ่าน dateFormat |
| ประสิทธิภาพ | สูงกว่า (เฉพาะทาง) | ต่ำกว่า (ทั่วไป) |
| มาตรฐาน | เฉพาะ ISO 8601 | ทุกรูปแบบ |
| เวอร์ชัน iOS | iOS 10+ | iOS 2+ |
เมื่อใดควรใช้ DateFormatter: ถ้าคุณต้องจัดรูปแบบวันที่ในรูปแบบที่ไม่ใช่ ISO 8601 (เช่น "21 กรกฎาคม 2024" สำหรับ UI) หรือถ้าคุณต้องรองรับ iOS 9 และเก่ากว่า สำหรับงานแลกเปลี่ยนวันที่ระหว่างเซิร์ฟเวอร์และไคลเอ็นต์ทั้งหมด ให้ใช้ ISO8601DateFormatter — มันปลอดภัยกว่า มีประสิทธิภาพมากกว่าและต้องการโค้ดน้อยกว่า DateFormatter สำหรับ ISO 8601 เป็นแหล่งของข้อบกพร่องที่อาจเกิดขึ้นเกี่ยวกับภาษาและการตั้งค่าภูมิภาค
การย้ายจาก DateFormatter ไปยัง ISO8601DateFormatter: แทนที่การสร้าง DateFormatter + การกำหนดค่า dateFormat + locale + timeZone ด้วยการสร้าง ISO8601DateFormatter + การกำหนดค่า formatOptions + timeZone การแยกวิเคราะห์สตริงยังคงไม่เปลี่ยนแปลงผ่าน date(from:) สำหรับความเข้ากันได้ย้อนหลัง คุณสามารถใช้ #available(iOS 10, *) พร้อมกับ fallback ไปยัง DateFormatter
การลืมกำหนดค่า formatOptions ทำให้ตัวจัดรูปแบบใช้ค่าเริ่มต้น — .withInternetDateTime ถ้าเซิร์ฟเวอร์ส่งวันที่โดยไม่มีเวลา (2024-07-21) การแยกวิเคราะห์จะคืนค่า nil ตรวจสอบเสมอว่า formatOptions ครอบคลุมทุกรูปแบบที่เป็นไปได้ที่อาจมาจากเซิร์ฟเวอร์ สำหรับ API ที่มีรูปแบบแปรผัน ให้ใช้ความพยายามแบบ fallback ด้วยการรวมตัวเลือกต่างๆ
ความสับสนระหว่าง withMilliseconds และ withFractionalSeconds เป็นข้อผิดพลาดทั่วไปเมื่อแยกวิเคราะห์วันที่ที่มีเศษส่วนของวินาที withMilliseconds คาดหวัง 3 หลักหลังจุดทศนิยมพอดี ถ้าเซิร์ฟเวอร์ส่ง 6 หลัก (ไมโครวินาที) การแยกวิเคราะห์ด้วย withMilliseconds จะล้มเหลว ใช้ .withFractionalSeconds สำหรับความเข้ากันได้กับจำนวนหลักใดๆ .withFractionalSeconds พร้อมใช้งานใน iOS 13; สำหรับเวอร์ชันเก่า ให้ใช้ DateFormatter พร้อมกับ dateFormat
การไม่สนใจเขตเวลา เป็นอีกปัญหาที่พบบ่อย ถ้าเซิร์ฟเวอร์ส่งวันที่พร้อมกับเขตเวลา (+03:00) และตัวจัดรูปแบบถูกตั้งค่าเป็น UTC การแยกวิเคราะห์จะไม่ล้มเหลว แต่ผลลัพธ์จะอยู่ใน UTC นักพัฒนามักคาดหวังให้ Date เก็บรักษาเขตเวลาไว้ แต่ Date เป็นช่วงเวลาสัมบูรณ์ — มันไม่เก็บข้อมูลเขตเวลา สำหรับการแสดงผลที่ถูกต้อง ให้บันทึกเขตเวลาแยกต่างหากหรือใช้ ISO8601DateFormatter พร้อมกับ timeZone ที่ถูกต้อง
ตาม Apple Forum (2024) ประมาณ 20% ของคำถามเกี่ยวกับ ISO8601DateFormatter เกี่ยวข้องกับรูปแบบที่วินาทีเป็นตัวเลือก มาตรฐาน ISO 8601 อนุญาตรูปแบบที่ไม่มีวินาที: 2024-07-21T14:30+03:00 ISO8601DateFormatter ที่มี .withInternetDateTime ไม่รองรับรูปแบบนี้ — สำหรับการแยกวิเคราะห์จะต้องใช้ DateFormatter ที่มี dateFormat = "yyyy-MM-dd'T'HH:mmZ" ข้อจำกัดนี้สำคัญที่ต้องพิจารณาเมื่อทำงานกับ API ที่ใช้รูปแบบเวลาย่อ
คำถามที่พบบ่อย
ISO8601DateFormatter — คลาส Foundation เฉพาะทางสำหรับการจัดรูปแบบและแยกวิเคราะห์วันที่ในรูปแบบ ISO 8601 พร้อมใช้งานตั้งแต่ iOS 10 จัดการรูปแบบมาตรฐานโดยอัตโนมัติโดยไม่ต้องตั้งค่า dateFormat ด้วยตนเอง
ISO8601DateFormatter ไม่ขึ้นอยู่กับ Locale ใช้ตัวเลือกแทน dateFormat และจัดการเศษส่วนของวินาทีทุกระดับความยาวได้อย่างถูกต้อง DateFormatter เป็นสากล แต่ต้องกำหนดค่าด้วยตนเองและมีแนวโน้มที่จะเกิดข้อบกพร่องเกี่ยวกับการตั้งค่าภูมิภาค
ใช้ตัวเลือก .withFractionalSeconds — รองรับตั้งแต่ 1 ถึง 9 หลักหลังจุดทศนิยม อย่าใช้ .withMilliseconds ถ้าความแม่นยำอาจเปลี่ยนแปลงได้ .withFractionalSeconds พร้อมใช้งานตั้งแต่ iOS 13
ค่าเริ่มต้นคือ UTC เมื่อต้องการเปลี่ยน ให้ตั้งค่าคุณสมบัติ timeZone ถ้า timeZone = nil จะใช้เวลาท้องถิ่นของอุปกรณ์ เมื่อแยกวิเคราะห์สตริงที่มีเขตเวลาชัดเจนในรูปแบบ +HH:MM ตัวจัดรูปแบบจะพิจารณาโดยอัตโนมัติ
เพราะค่าเริ่มต้นของ formatOptions คือ .withInternetDateTime ซึ่งคาดหวังวันที่ + เวลา + เขตเวลา สำหรับการแยกวิเคราะห์เฉพาะวันที่ ให้ตั้งค่า formatOptions = [.withFullDate] เพื่อรองรับทั้งสองรูปแบบ ให้ใช้ fallback ด้วยตัวเลือกที่แตกต่างกัน
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ