วันที่และเวลาในการพัฒนามือถือ — หนึ่งในหัวข้อสำคัญในวิศวกรรมมือถือ iOS ใช้ DateFormatter และ ISO8601DateFormatter, Android ใช้ DateTimeFormatter และ LocalDate จาก java.time ตาม Android Developer Docs การเลือก API มือถือที่ถูกต้องสำหรับการจัดรูปแบบวันที่และเวลาส่งผลโดยตรงต่อประสิทธิภาพและการแสดงผลที่ถูกต้องในเขตเวลาที่แตกต่างกัน
ประเด็นสำคัญ
บน iOS วันที่และเวลา จะถูกจัดรูปแบบตามปกติผ่าน DateFormatter จาก Foundation คลาสนี้แปลง Date เป็นสตริงและกลับกันโดยใช้รูปแบบ การตั้งค่าภูมิภาค และเขตเวลาที่ระบุ ปัญหาหลักของ DateFormatter — มันไม่ปลอดภัยต่อเธรดและสร้างได้ช้า ดังนั้นในโค้ดมือถือ อินสแตนซ์จะต้องถูกแคช วันที่ในการพัฒนามือถือบน iOS ต้องใช้ตัวจัดรูปแบบที่ถูกต้อง
การสร้าง DateFormatter ใช้เวลาประมาณ 1 มิลลิวินาทีเนื่องจากต้องแยกวิเคราะห์รูปแบบ dateFormat โหลดการตั้งค่าภูมิภาค และกำหนดเขตเวลา เมื่อจัดรูปแบบรายการวันที่หลายร้อยรายการ การสร้างซ้ำๆ ทำให้เกิดความล่าช้าที่สังเกตเห็นได้ใน UI วิธีแก้ไข — สร้างตัวจัดรูปแบบหนึ่งครั้งสำหรับทั้งหน้าจอและนำกลับมาใช้ใหม่ สำหรับการเข้าถึงแบบหลายเธรด ให้ใช้การซิงโครไนซ์ผ่าน DispatchQueue
import Foundation
// DateFormatter ที่ถูกแคช — สร้างครั้งเดียว
private let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy HH:mm"
formatter.locale = Locale(identifier: "ru_RU")
formatter.timeZone = TimeZone(identifier: "Europe/Moscow")
return formatter
}()
// ISO8601DateFormatter — ทางเลือกน้ำหนักเบา
private let isoFormatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formatter
}()
let now = Date()
let formattedDate = dateFormatter.string(from: now)
// "01.07.2026 14:30"
let isoDate = isoFormatter.string(from: now)
// "2026-07-01T14:30:00.000Z"
สำหรับรูปแบบ ISO 8601 ให้ใช้ ISO8601DateFormatter — ไม่จำเป็นต้องระบุรูปแบบและทำงานเร็วกว่า DateFormatter บน iOS 10+ คลาสนี้เหมาะสำหรับการทำงานกับวันที่จากเซิร์ฟเวอร์ สำหรับรูปแบบเก่า ให้ใช้ DateFormatter แต่ต้องแคชอินสแตนซ์เป็นคุณสมบัติแบบสแตติกหรือซิงเกิลตัน
บน Android วันที่และเวลา จะถูกประมวลผลผ่านแพ็คเกจ java.time ซึ่งพร้อมใช้งานตั้งแต่ API 26 คลาสหลัก: LocalDate (วันที่เท่านั้น), LocalTime (เวลาเท่านั้น), LocalDateTime (วันที่และเวลาไม่มีเขตเวลา) และ ZonedDateTime (มีเขตเวลา) DateTimeFormatter — ตัวจัดรูปแบบที่ปลอดภัยต่อเธรดสำหรับแพลตฟอร์มมือถือ Android วันที่ในแอปพลิเคชันมือถือจะถูกจัดรูปแบบผ่าน DateTimeFormatter และ LocalDate โดยไม่สูญเสียประสิทธิภาพ
LocalDate เก็บวันที่โดยไม่มีเวลา — เหมาะสำหรับวันเกิด ZonedDateTime เก็บข้อมูลครบถ้วนพร้อมเขตเวลา DateTimeFormatter รองรับรูปแบบที่กำหนดไว้ล่วงหน้า (ISO_LOCAL_DATE, ISO_DATE_TIME) และรูปแบบกำหนดเองผ่าน ofPattern คลาส java.time ทั้งหมดไม่สามารถเปลี่ยนแปลงได้และปลอดภัยต่อเธรด ซึ่งกำจัดสภาวะการแข่งขันเมื่อทำงานในหลายเธรด
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.ZoneId
// การสร้างและจัดรูปแบบวันที่ปัจจุบัน
val today = LocalDate.now()
val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy")
val formatted = today.format(formatter)
// การทำงานกับเขตเวลา
val utcTime = ZonedDateTime.now(ZoneId.of("UTC"))
val moscowTime = utcTime.withZoneSameInstant(ZoneId.of("Europe/Moscow"))
DateTimeFormatter มีประสิทธิภาพมากกว่า SimpleDateFormat อย่างมากเนื่องจากไม่ต้องแยกวิเคราะห์รูปแบบทุกครั้งที่เรียกใช้ ขอแนะนำให้สร้างค่าคงที่ของตัวจัดรูปแบบในระดับคลาสหรืออ็อบเจกต์คู่หู ในการแปลง Unix Timestamp ให้ใช้ Instant.ofEpochSecond จากนั้นแปลงเป็น LocalDateTime หรือ ZonedDateTime ผ่านเขตเวลา
วันที่และเวลา ที่ถูกต้องบนหน้าจอผู้ใช้ขึ้นอยู่กับการจัดการเขตเวลาที่เหมาะสม วันที่และเวลาในการพัฒนามือถือสำหรับ Android ต้องพิจารณาเขตเวลาของอุปกรณ์ กฎทอง: เก็บเวลาใน UTC แปลงเป็นเขตเวลาท้องถิ่นเฉพาะสำหรับการแสดงผล iOS มี TimeZone จาก Foundation, Android มี ZoneId จาก java.time API ทั้งสองสำหรับแพลตฟอร์มมือถือรองรับการตรวจจับเขตเวลาปัจจุบันของอุปกรณ์โดยอัตโนมัติ วันที่ในแอปพลิเคชันมือถือขึ้นอยู่กับ ZoneId และ TimeZone
เวลาออมแสง สร้างความคลุมเครือ: ช่วงเวลาหนึ่งสามารถแสดงด้วยเวลาท้องถิ่นที่แตกต่างกันสองเวลา java.time และ Foundation จัดการ DST โดยอัตโนมัติ ใช้ตัวระบุ IANA (Europe/Moscow, America/New_York) ไม่ใช่ตัวย่อ (MSK, EST) — ตัวย่อคลุมเครือและละเว้นการเปลี่ยนเวลาออมแสง
import Foundation
// การแปลง UTC เป็นเวลาท้องถิ่นบน iOS
func formatToLocalTime(utcDate: Date, timeZoneId: String) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy HH:mm"
formatter.timeZone = TimeZone(identifier: timeZoneId)
return formatter.string(from: utcDate)
}
// การกำหนดเขตเวลาของอุปกรณ์
let localTimeZone = TimeZone.current
let isDST = localTimeZone.isDaylightSavingTime()
let secondsFromGMT = localTimeZone.secondsFromGMT()
บน Android ใช้ ZoneId.systemDefault() เพื่อรับเขตเวลาของอุปกรณ์ เมื่อแปลง UTC เป็นเวลาท้องถิ่น ให้สร้าง ZonedDateTime ด้วย ZoneOffset.UTC และใช้ withZoneSameInstant สำหรับ API เก่าที่ไม่มี java.time ให้ใช้ TimeZone.getDefault() และ Calendar — แต่ควรเพิ่ม ThreeTenABP โดยตรงจะดีกว่า
import java.time.Instant
import java.time.ZonedDateTime
import java.time.ZoneId
// แปลง Unix Timestamp เป็นเวลาท้องถิ่น
fun toLocalDisplay(unixSeconds: Long): String {
val instant = Instant.ofEpochSecond(unixSeconds)
val local = instant.atZone(ZoneId.systemDefault())
return local.format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"))
}
ThreeTenABP — ไลบรารีอะแดปเตอร์ที่พอร์ต java.time ไปยัง Android API ต่ำกว่า 26 ให้คลาสเดียวกัน: LocalDate, LocalTime, ZonedDateTime, DateTimeFormatter, Instant หากแอปพลิเคชันรองรับ Android 5 หรือ 6 (API 21-25) ThreeTenABP เป็นวิธีเดียวที่จะใช้ API มือถือสมัยใหม่โดยไม่ต้องเปลี่ยนไปใช้ Calendar ที่เลิกใช้แล้ว วันที่และเวลาในการพัฒนามือถือสำหรับอุปกรณ์เก่าถูกใช้งานผ่านไลบรารีนี้ สำหรับอุปกรณ์เก่า วันที่และเวลา ถูกใช้งานผ่าน ThreeTenABP
ไลบรารีต้องการการเริ่มต้นในเมธอด Application.onCreate() โดยเรียก AndroidThreeTen.init() ซึ่งจะโหลดข้อมูลเขตเวลาจาก assets หลังจากการเริ่มต้น API จะเหมือนกับ java.time ทุกประการ — โค้ดสำหรับ API 26+ และ API 21+ จะเหมือนกัน ขนาดไลบรารีประมาณ 400 KB ซึ่งยอมรับได้สำหรับแอปพลิเคชันมือถือส่วนใหญ่
import org.threeten.bp.LocalDate
import org.threeten.bp.format.DateTimeFormatter
// การเริ่มต้นใน Application
class App : Application() {
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
}
}
// API เหมือนกับ java.time
val today = LocalDate.now()
val formatted = today.format(DateTimeFormatter.ISO_LOCAL_DATE)
ประสิทธิภาพของ ThreeTenABP เทียบได้กับ java.time ดั้งเดิมบนอุปกรณ์สมัยใหม่ บนอุปกรณ์เก่าอาจมีความล่าช้าในการเรียกครั้งแรกเนื่องจากการโหลดข้อมูลเขตเวลา ย้ายโค้ดที่มีอยู่จาก Calendar ไปยัง ThreeTenABP อย่างค่อยเป็นค่อยไป: แทนที่ Date และ Calendar ด้วย Instant และ LocalDate ในโมดูลใหม่ ปล่อยให้โค้ดเก่าไม่เปลี่ยนแปลง
Unix Timestamp — จำนวนวินาทีที่ผ่านไปตั้งแต่วันที่ 1 มกราคม 1970 00:00 UTC นี่คือรูปแบบสากลสำหรับการเก็บและแลกเปลี่ยนเวลาระหว่างเซิร์ฟเวอร์และไคลเอนต์ อย่างไรก็ตาม เวลาบนอุปกรณ์อาจแตกต่างจากเวลาจริงเนื่องจากการตั้งค่าผู้ใช้ สำหรับการซิงโครไนซ์จะใช้ NTP — โปรโตคอลเวลาที่แม่นยำผ่าน UDP Unix Timestamp เป็นรูปแบบที่เก็บ วันที่และเวลา บนเซิร์ฟเวอร์
Date().timeIntervalSince1970 บน iOS คืนค่าวินาทีตั้งแต่ปี 1970 บน Android System.currentTimeMillis() คืนค่ามิลลิวินาที เพื่อความแม่นยำ ให้ใช้ NTP: ไลบรารี TrueTime สำหรับ iOS และ AndroidNtp สำหรับ Android ส่งคำขอไปยังเซิร์ฟเวอร์ NTP และคำนวณค่าชดเชยเวลา ความแม่นยำของ NTP คือ 1-10 ms บนเครือข่ายท้องถิ่นและ 10-100 ms ผ่านอินเทอร์เน็ต
import Foundation
// การรับ Unix Timestamp บน iOS
let seconds = Date().timeIntervalSince1970
let milliseconds = Int64(seconds * 1000)
// การซิงโครไนซ์ NTP กับ TrueTime
TrueTime.shared.start { result in
switch result {
case .success:
let ntpTime = TrueTime.shared.now()
print("เวลาที่แน่นอน: \(ntpTime)")
case .failure(let error):
print("ข้อผิดพลาด NTP: \(error)")
}
}
หากไม่มีการซิงโครไนซ์ในแอปพลิเคชันมือถือ อาจเกิดข้อผิดพลาด: การประทับเวลาเหตุการณ์ไม่ถูกต้อง การสูญเสียการซิงค์กับเซิร์ฟเวอร์ เวลาในการพัฒนามือถือจะถูกซิงโครไนซ์ผ่าน NTP หนึ่งครั้งเมื่อเริ่มต้นแอปพลิเคชัน หลังจากนั้นจะใช้ ค่าชดเชย ที่คำนวณได้ สำหรับแอปพลิเคชันมือถือที่ต้องการความแม่นยำสูง (การเงิน, โลจิสติกส์) โปรโตคอล NTP เป็นสิ่งจำเป็นก่อนส่งข้อมูลไปยังเซิร์ฟเวอร์
คำถามที่พบบ่อย
DateFormatter แยกวิเคราะห์รูปแบบการจัดรูปแบบ โหลดการตั้งค่าภูมิภาค และกำหนดเขตเวลาทุกครั้งที่มีการสร้างอินสแตนซ์ แคชตัวจัดรูปแบบเป็นค่าคงที่แบบสแตติกหรือใช้ ISO8601DateFormatter สำหรับ ISO 8601 — มันเบาและเร็วกว่า
DateTimeFormatter จากแพ็คเกจ java.time — ปลอดภัยต่อเธรดและมีประสิทธิภาพ พร้อมใช้งานตั้งแต่ API 26 สำหรับเวอร์ชันเก่า ให้ใช้ ThreeTenABP ซึ่งมี API ที่เหมือนกัน
เพิ่ม ThreeTenABP — backport ของ java.time สำหรับ Android API 21+ เริ่มต้นผ่าน AndroidThreeTen.init() ใน Application.onCreate() API เหมือนกับ java.time ดั้งเดิมทุกประการ
ใช่ เก็บเวลาใน UTC บนเซิร์ฟเวอร์และในฐานข้อมูลท้องถิ่น แปลงเป็นเขตเวลาท้องถิ่นของผู้ใช้เฉพาะในขั้นตอนการแสดงผลเท่านั้น ซึ่งช่วยขจัดข้อผิดพลาดเมื่อเปลี่ยนเขตเวลาหรือเดินทาง
Date().timeIntervalSince1970 คืนค่าจำนวนวินาทีตั้งแต่วันที่ 1 มกราคม 1970 สำหรับมิลลิวินาที ให้คูณด้วย 1000 และแปลงเป็น Int64 สำหรับการซิงโครไนซ์ที่แม่นยำ ให้ใช้ไลบรารี TrueTime กับเซิร์ฟเวอร์ NTP
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ