DateFormatter: แนวคิดหลัก การจัดรูปแบบวันที่และการแปลเป็นภาษาท้องถิ่น

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-07-12 เวลาอ่าน: 7 นาที

DateFormatter เป็นคลาส Foundation ที่ออกแบบมาเพื่อการแปลงแบบสองทิศทางระหว่างวัตถุ Date และการแสดงเป็นสตริง คลาสนี้คำนึงถึงภาษาท้องถิ่น เขตเวลา และปฏิทินของผู้ใช้ เพื่อให้แน่ใจว่าการแสดงวันที่ถูกต้องในทุกภูมิภาคของโลก ตามเอกสารสำหรับนักพัฒนา Apple (2025) DateFormatter รองรับสไตล์วันที่และเวลาที่กำหนดไว้ล่วงหน้าสี่แบบ รวมถึงรูปแบบที่กำหนดเองได้อย่างสมบูรณ์ผ่านสตริงเทมเพลต หากไม่มี DateFormatter จะไม่สามารถแสดงวันที่ให้ผู้ใช้อย่างถูกต้องในแอปพลิเคชันที่เป็นสากลได้

ประเด็นสำคัญ

  • DateFormatter — คลาสสำหรับแปลง Date เป็นสตริงและกลับกันพร้อมรองรับภาษาท้องถิ่นและเขตเวลา
  • dateStyle และ timeStyle — สไตล์ที่กำหนดไว้ล่วงหน้า (.short, .medium, .long, .full) สำหรับการจัดรูปแบบอย่างรวดเร็ว
  • dateFormat — สตริงเทมเพลตสำหรับรูปแบบที่กำหนดเองที่ระบุผ่านสัญลักษณ์ Unicode LDML
  • Locale และ TimeZone — คุณสมบัติของตัวจัดรูปแบบที่กำหนดการแสดงผลในภูมิภาคและเขตเวลา
  • ISO8601DateFormatter — ทางเลือกที่เร็วกว่าสำหรับรูปแบบ ISO 8601 เมื่อทำการซีเรียลไลซ์ใน API

DateFormatter คืออะไร?

DateFormatter เป็นคลาสจากเฟรมเวิร์ก Foundation ที่ทำการแปลงแบบสองทิศทางระหว่าง Date และสตริง ปรากฏครั้งแรกใน OpenStep ในชื่อ NSDateFormatter และตั้งแต่นั้นมาก็ยังคงเป็นเครื่องมือหลักสำหรับการจัดรูปแบบวันที่บนแพลตฟอร์ม Apple ทั้งหมด คลาสนี้สืบทอดจาก Formatter และให้ API ที่สะดวกสำหรับการแสดงวันที่ที่แปลเป็นภาษาท้องถิ่น

DateFormatter ทำงานบนพื้นฐานของรูปแบบ Unicode LDML — เดียวกับที่ใช้ใน ICU (International Components for Unicode) รูปแบบถูกกำหนดผ่านคุณสมบัติ dateFormat โดยสัญลักษณ์ y, M, d, H, m, s สอดคล้องกับปี, เดือน, วัน, ชั่วโมง, นาที, วินาที การทำซ้ำสัญลักษณ์จะกำหนดรูปแบบ: “y” — ปีสองหลัก, “yyyy” — ปีสี่หลัก

การสร้าง DateFormatter เป็น การดำเนินการที่มีค่าใช้จ่ายสูง เนื่องจากข้อมูลภาษาท้องถิ่นและปฏิทินถูกโหลดระหว่างการเริ่มต้น Apple แนะนำให้สร้างตัวจัดรูปแบบหนึ่งครั้งสำหรับการจัดรูปแบบแต่ละประเภทและนำกลับมาใช้ใหม่ ใน SwiftUI และ UIKit ตัวจัดรูปแบบมักถูกเก็บในแคชในคุณสมบัติแบบสแตติกหรือถูกสร้างแบบขี้เกียจเมื่อมีการเข้าถึงครั้งแรก

DateFormatter ใน iOS SDK

DateFormatter ถูกใช้ในส่วนประกอบระบบ iOS หลายตัว UIDatePicker ใช้ DateFormatter ภายในเพื่อแสดงวันที่ในโหมด countDownTimer TextField ที่มีตัวจัดรูปแบบสามารถตรวจสอบวันที่ที่ผู้ใช้ป้อนได้โดยอัตโนมัติ Core Data รองรับแอตทริบิวต์ประเภท Date แต่การแสดงเป็นสตริงจะดำเนินการผ่าน DateFormatter เสมอ

ความปลอดภัยของเธรด — DateFormatter ไม่ปลอดภัยต่อเธรด การแก้ไขคุณสมบัติของตัวจัดรูปแบบจากเธรดต่างกันทำให้เกิดพฤติกรรมที่ไม่ได้กำหนดไว้ สำหรับการใช้งานหลายเธรด ให้สร้างอินสแตนซ์ตัวจัดรูปแบบแยกต่างหากสำหรับแต่ละเธรด หรือใช้การซิงโครไนซ์ผ่าน NSLock หรือ คิวแบบอนุกรม

สไตล์การจัดรูปแบบ DateFormatter

dateStyle และ timeStyle เป็นวิธีที่ง่ายที่สุดในการกำหนดค่าการแสดงวันที่ แต่ละสไตล์มีสี่รูปแบบ: .short, .medium, .long, .full การรวมกันของ dateStyle และ timeStyle ช่วยให้กำหนดค่ารูปแบบวันที่และเวลาได้อย่างอิสระ และคุณสมบัติ .none จะปิดการใช้งานส่วนที่เกี่ยวข้อง

สำหรับภาษาท้องถิ่นของสหรัฐอเมริกา .short จัดรูปแบบวันที่เป็น “7/21/26” และสำหรับภาษาท้องถิ่นของรัสเซียเป็น “21.07.2026” สไตล์ .long สำหรับภาษาท้องถิ่นของรัสเซียแสดง “21 กรกฎาคม 2026” และ .full แสดง “วันอังคารที่ 21 กรกฎาคม 2026” พร้อมวันในสัปดาห์ ทั้งสี่สไตล์ปรับเปลี่ยนตามมาตรฐานภูมิภาคโดยอัตโนมัติ รวมถึงลำดับของส่วนประกอบและตัวคั่น

SFDateFormatter ใน iOS 15+ มีแนวทางทางเลือกผ่าน RelativeDateFormatter และ DateIntervalFormatter RelativeDateFormatter แสดง “วันนี้”, “เมื่อวาน”, “ใน 3 วัน” สำหรับบริบททันที DateIntervalFormatter แสดงช่วงวันที่: “21–25 กรกฎาคม 2026” — สำหรับการจองและการวางแผน

สไตล์ตัวอย่าง (ru_RU)ตัวอย่าง (en_US)
.short21.07.20267/21/26
.medium21 ก.ค. 2026Jul 21, 2026
.long21 กรกฎาคม 2026July 21, 2026
.fullวันอังคารที่ 21 กรกฎาคม 2026Tuesday, July 21, 2026

เมื่อรวมสไตล์ DateFormatter จะเลือกตัวคั่นโดยอัตโนมัติ: สำหรับ .short.date + .short.time ผลลัพธ์อาจเป็น “21.07.2026, 14:30” สำหรับ .full.date + .full.time — “วันอังคารที่ 21 กรกฎาคม 2026, 14:30:00 MSK” ตัวคั่นถูกจัดการโดยภาษาท้องถิ่น ไม่ใช่นักพัฒนา — ซึ่งรับประกันความสอดคล้องกับความคาดหวังในภูมิภาคของผู้ใช้

รูปแบบที่กำหนดเองผ่าน dateFormat

dateFormat อนุญาตให้ตั้งค่ารูปแบบการจัดรูปแบบตามอำเภอใจโดยใช้สัญลักษณ์ข้อกำหนด Unicode LDML ซึ่งให้การควบคุมการแสดงผลอย่างสมบูรณ์: คุณสามารถแสดงเฉพาะปีและเดือน หรือวันในสัปดาห์โดยไม่มีวันที่ หรือเวลาโดยไม่มีวินาที รูปแบบที่กำหนดเองเป็นสิ่งที่ขาดไม่ได้สำหรับข้อกำหนดการออกแบบเฉพาะ

สัญลักษณ์หลัก — yyyy (ปี: 2026), MM (เดือน: 07), dd (วัน: 21), HH (ชั่วโมง: 14), mm (นาที: 30), ss (วินาที: 00) สำหรับชื่อเดือนเต็มใช้ MMMM (กรกฎาคม), สำหรับแบบย่อ — MMM (ก.ค.) วันในสัปดาห์ — EEEE (วันอังคาร), แบบย่อ — E (อ.)

เมื่อใช้ dateFormat สิ่งสำคัญคือต้องตั้ง ภาษาท้องถิ่น ของตัวจัดรูปแบบ หากไม่ได้ตั้งค่าภาษาท้องถิ่น ตัวจัดรูปแบบจะใช้ภาษาท้องถิ่นของระบบ ซึ่งอาจไม่เป็นที่ต้องการสำหรับรูปแบบคงที่ใน API Apple แนะนำให้ตั้ง locale = Locale(identifier: “en_US_POSIX”) สำหรับรูปแบบคงที่ระหว่างภูมิภาค โดยเฉพาะเมื่อแยกวิเคราะห์วันที่จากการตอบกลับของเซิร์ฟเวอร์

swift
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ru_RU")
formatter.dateFormat = "d MMMM yyyy"
let customString = formatter.string(from: Date())
// "21 July 2026"

// การแยกวิเคราะห์สตริงที่กำหนดเอง
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = formatter.date(from: "2026-07-21 14:30:00")!

ข้อผิดพลาดใน dateFormat เป็นหนึ่งในสาเหตุทั่วไปของแอปคร্যช หากรูปแบบไม่ตรงกับสตริง เมธอด date(from:) จะคืนค่า nil ใช้ guard let หรือ ?? เพื่อแกะ optional อย่างปลอดภัย ในการตรวจสอบรูปแบบ ให้ทดสอบในทุกภาษาที่รองรับ — สัญลักษณ์ LDML บางตัวทำงานแตกต่างกันในภาษาท้องถิ่นต่างกัน

การแปลเป็นภาษาท้องถิ่นและ TimeZone

Locale กำหนดวิธีการแสดงชื่อเดือน ชื่อวันในสัปดาห์ และตัวคั่น DateFormatter ใช้ Locale.current โดยค่าเริ่มต้น แต่ในบางสถานการณ์จำเป็นต้องระบุภาษาท้องถิ่นเฉพาะ: สำหรับรูปแบบคงที่ในบันทึกใช้ en_US_POSIX, สำหรับวันที่ของเซิร์ฟเวอร์ — ภาษาท้องถิ่นที่ตรงกับเซิร์ฟเวอร์

คุณสมบัติ TimeZone กำหนดเขตเวลาสำหรับการแสดงผล โดยค่าเริ่มต้นจะใช้เขตเวลาของระบบ แต่สำหรับแอปพลิเคชันที่มีผู้ชมต่างประเทศ มักจำเป็นต้องแสดงวันที่ในเขตเวลาของผู้ใช้หรือใน UTC การเปลี่ยน timeZone มีผลต่อการแสดงผลเท่านั้น — ค่า Date ยังคงไม่เปลี่ยนแปลง

คุณสมบัติที่สำคัญ: หาก DateFormatter ถูกใช้ในการแยกวิเคราะห์สตริงและสตริงมีการระบุเขตเวลา (เช่น “2026-07-21T14:30:00Z” โดยมี Z สำหรับ UTC) คุณสมบัติ timeZone จะถูกเพิกเฉย — ตัวจัดรูปแบบใช้เขตเวลาจากสตริง หากไม่มีเขตเวลาในสตริง timeZone ของตัวจัดรูปแบบจะถูกนำไปใช้

swift
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ru_RU")
formatter.timeZone = TimeZone(identifier: "Europe/Moscow")
formatter.dateStyle = .long
formatter.timeStyle = .short

let moscowTime = formatter.string(from: Date())
// "21 July 2026, 14:30"

// การแยกวิเคราะห์โดยไม่มีเขตเวลาในสตริง
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd HH:mm"
let utcDate = formatter.date(from: "2026-07-21 10:30")!

AutoupdatingCurrentLocale — ชนิดภาษาท้องถิ่นพิเศษที่อัปเดตโดยอัตโนมัติเมื่อการตั้งค่าระบบของผู้ใช้เปลี่ยนไป DateFormatter รองรับโดยค่าเริ่มต้น หากแอปพลิเคชันทำงานในพื้นหลังและผู้ใช้เปลี่ยนภาษาของระบบ ตัวจัดรูปแบบที่สร้างก่อนการเปลี่ยนแปลงจะยังคงใช้ภาษาท้องถิ่นเก่า — ในการอัปเดตจำเป็นต้องสร้างอินสแตนซ์ใหม่

ISO8601DateFormatter และทางเลือก

ISO8601DateFormatter เป็นตัวจัดรูปแบบเฉพาะสำหรับการทำงานกับวันที่ในรูปแบบ ISO 8601 รูปแบบนี้เป็นมาตรฐานโดยพฤตินัยสำหรับ REST API, JSON และการแลกเปลี่ยนข้อมูล ISO8601DateFormatter ทำงานเร็วกว่า DateFormatter อย่างมีนัยสำคัญเนื่องจากไม่ขึ้นอยู่กับภาษาท้องถิ่นและใช้ไวยากรณ์การแยกวิเคราะห์แบบคงที่

ตัวเลือกหลักของตัวจัดรูปแบบ — .withInternetDateTime (2026-07-21T14:30:00Z), .withFractionalSeconds (เพิ่มมิลลิวินาที), .withTimeZone (รวมค่าชดเชยเขตเวลา) การรวมตัวเลือกต่าง ๆ เข้าด้วยกัน คุณจะได้รับ ISO 8601 ทุกรูปแบบ: มีมิลลิวินาที มีเขตเวลา มีเฉพาะวันที่

JSONEncoder.DateEncodingStrategy ช่วยให้กำหนดค่าการเข้ารหัสวันที่ทั่วโลกสำหรับโมเดล Codable ทั้งหมด ตัวเลือก — .iso8601 (ใช้ ISO8601DateFormatter), .formatted(DateFormatter), .millisecondsSince1970, .secondsSince1970 การเลือกกลยุทธ์ส่งผลกระทบต่อวงจรชีวิตการซีเรียลไลซ์ทั้งหมดและควรสอดคล้องกันในทุกจุดสิ้นสุด API

swift
// ISO8601DateFormatter
let isoFormatter = ISO8601DateFormatter()
isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let isoString = isoFormatter.string(from: Date())
// "2026-07-21T14:30:00.000Z"

// JSONEncoder กับ ISO8601
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601

// ทางเลือก: JSONEncoder กับตัวจัดรูปแบบที่กำหนดเอง
let customEncoder = JSONEncoder()
customEncoder.dateEncodingStrategy = .formatted(myFormatter)

DateFormatter เทียบกับ ISO8601DateFormatter — เลือก ISO8601DateFormatter สำหรับการซีเรียลไลซ์และแยกวิเคราะห์วันที่ใน API เนื่องจากเร็วกว่า DateFormatter 5-10 เท่าและไม่เกิดข้อผิดพลาดจากการแปลเป็นภาษาท้องถิ่น ใช้ DateFormatter สำหรับอินเทอร์เฟซผู้ใช้ที่ต้องการการแสดงผลที่แปลเป็นภาษาท้องถิ่นพร้อมชื่อเดือนและวันในภาษาพื้นเมืองของผู้ใช้

ตัวอย่างการจัดรูปแบบวันที่

มาดูสถานการณ์จริงของการใช้ DateFormatter ในแอป iOS: การแสดงวันที่ในฟีดข่าว การป้อนวันเกิด และการส่งออกรายงานที่มีวันที่ในเขตเวลาต่างกัน

การแสดงวันที่ข่าวในรายการ

RelativeDateFormatter เหมาะที่สุดสำหรับฟีดข่าว โดยแสดง “เมื่อสักครู่”, “5 นาทีที่แล้ว”, “เมื่อวาน” สำหรับข่าวล่าสุดและเปลี่ยนเป็นวันที่เต็มสำหรับข่าวเก่า เกณฑ์การสลับถูกกำหนดค่าผ่านปฏิทิน: สำหรับข่าวใช้เกณฑ์ 24 ชั่วโมง, สำหรับแอปส่งข้อความ — หนึ่งสัปดาห์

swift
func formatRelativeDate(_ date: Date) -> String {
    let relative = RelativeDateFormatter()
    relative.unitsStyle = .full

    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    formatter.timeStyle = .short

    let daysDiff = Calendar.current.dateComponents(
        [.day], from: date, to: Date()
    ).day ?? 0

    return daysDiff < 1
        ? relative.localizedString(for: date, relativeTo: Date())
        : formatter.string(from: date)
}

การป้อนวันเกิด — อีกสถานการณ์ทั่วไป DateFormatter ถูกกำหนดค่าด้วย dateFormat เฉพาะ “dd.MM.yyyy” และ locale “ru_RU” เมื่อแยกวิเคราะห์สตริงที่ป้อน สิ่งสำคัญคือต้องจัดการข้อผิดพลาดที่อาจเกิดขึ้น: ตัวจัดรูปแบบคืนค่า nil สำหรับสตริงที่ไม่ถูกต้อง หลังจากการแยกวิเคราะห์สำเร็จ วันที่จะถูกตรวจสอบว่าอยู่ในช่วงที่ยอมรับได้ — ไม่ก่อนปี 1900, ไม่เกินวันนี้

การส่งออกรายงานที่มีวันที่ ต้องใช้รูปแบบคงที่ที่ไม่ขึ้นอยู่กับภาษาท้องถิ่นของผู้ใช้ ใช้ dateFormat “yyyy-MM-dd HH:mm:ss” กับ locale en_US_POSIX และเขตเวลา UTC วิธีการนี้รับประกันว่าไฟล์จะเปิดอย่างถูกต้องในทุกประเทศโดยไม่ขึ้นอยู่กับการตั้งค่าภูมิภาคของระบบ

สถานการณ์ตัวจัดรูปแบบการตั้งค่าหลัก
ฟีดข่าวRelativeDateFormatterunitsStyle = .full
การป้อนวันที่DateFormatterdateFormat + ทางเลือกสำรอง
การซีเรียลไลซ์ APIISO8601DateFormatterwithInternetDateTime
การส่งออกรายงานDateFormatteren_US_POSIX + UTC

คำถามที่พบบ่อย

ทำไม DateFormatter ถึงคืนค่า nil สำหรับสตริงที่ถูกต้อง?

สาเหตุที่พบบ่อยที่สุด — ความไม่ตรงกันระหว่าง dateFormat กับรูปแบบสตริง ตัวอย่างเช่น รูปแบบ “dd.MM.yyyy” จะไม่แยกวิเคราะห์สตริง “2026-07-21” สาเหตุที่สอง — ความไม่ตรงกันของภาษาท้องถิ่น: สตริง “July 21, 2026” จะไม่ถูกแยกวิเคราะห์ด้วยภาษาท้องถิ่น ru_RU สาเหตุที่สาม — การพิมพ์ผิดในสัญลักษณ์ LDML: ใช้ yyyy ไม่ใช่ YYYY (ความหมายต่างกัน)

ฉันควรสร้าง DateFormatter ใหม่ทุกครั้งที่เรียกใช้หรือไม่?

ไม่ DateFormatter เป็นวัตถุหนัก การเริ่มต้นรวมถึงการโหลดข้อมูลภาษาท้องถิ่น สร้างหนึ่งอินสแตนซ์ต่อประเภทการจัดรูปแบบและนำกลับมาใช้ใหม่ ในสภาพแวดล้อมแบบหลายเธรด ใช้พื้นที่จัดเก็บเฉพาะเธรดหรือพูลตัวจัดรูปแบบที่มีคิวแบบอนุกรมสำหรับการซิงโครไนซ์

DateFormatter แตกต่างจาก RelativeDateFormatter อย่างไร?

DateFormatter แสดงวันที่แบบสัมบูรณ์ (21 กรกฎาคม 2026) ในขณะที่ RelativeDateFormatter แสดงวันที่แบบสัมพัทธ์ (วันนี้ เมื่อวาน ใน 3 วัน) RelativeDateFormatter ถูกนำมาใช้ใน iOS 15+ และใช้เทมเพลต LDML เดียวกัน แต่เลือกการแสดงแบบสัมพัทธ์โดยอัตโนมัติ

จะจัดการกับวันที่ไม่มีเขตเวลาจาก API อย่างไร?

ตั้งค่า timeZone ของตัวจัดรูปแบบ เป็น UTC ก่อนแยกวิเคราะห์ หากเซิร์ฟเวอร์คืนวันที่ในเวลาท้องถิ่นโดยไม่ระบุเขตเวลา ให้ตรวจสอบข้อกำหนดของ API — ส่วนใหญ่หมายถึง UTC สำหรับ ISO 8601 ที่ลงท้ายด้วย Z ไม่จำเป็นต้องใช้ timeZone — ตัวจัดรูปแบบแยกวิเคราะห์ค่าชดเชยจากสตริง

จะทำให้ DateFormatter ปลอดภัยต่อเธรดได้อย่างไร?

อย่าใช้อินสแตนซ์เดียวจากเธรดต่างกัน โดยไม่มีการซิงโครไนซ์ สร้างอินสแตนซ์ใหม่ในแต่ละเธรดหรือใช้ Thread.current.threadDictionary สำหรับจัดเก็บ ทางเลือกคือ NSLock ที่ล็อกระหว่างการทำงานของ string(from:) และ date(from:)

สรุป

  • DateFormatter — คลาส Foundation สำหรับแปลง Date เป็นสตริงและกลับกันพร้อมรองรับภาษาท้องถิ่น เขตเวลา และปฏิทิน
  • สไตล์ที่กำหนดไว้ล่วงหน้า dateStyle และ timeStyle ในรูปแบบ .short, .medium, .long, .full ครอบคลุมสถานการณ์ UI ส่วนใหญ่
  • dateFormat ที่กำหนดเอง ผ่านสัญลักษณ์ LDML ให้ควบคุมรูปแบบได้อย่างสมบูรณ์แต่ต้องระมัดระวังเกี่ยวกับการแปลเป็นภาษาท้องถิ่น
  • Locale และ TimeZone — คุณสมบัติที่จำเป็นสำหรับการแสดงผลที่ถูกต้อง: สำหรับ UI — ภาษาท้องถิ่นของระบบ, สำหรับ API — en_US_POSIX และ UTC
  • ISO8601DateFormatter — ตัวเลือกที่ต้องการสำหรับการซีเรียลไลซ์วันที่ใน API เนื่องจากความเร็วและความเสถียร
  • DateFormatter ไม่ปลอดภัยต่อเธรด — ใช้อินสแตนซ์แยกต่างหากสำหรับแต่ละเธรดหรือซิงโครไนซ์การเข้าถึง
  • RelativeDateFormatter (iOS 15+) — โซลูชันที่ดีที่สุดสำหรับการแสดงวันที่แบบสัมพัทธ์ในฟีดข่าวและแอปส่งข้อความ

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

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

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

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