Instant — คลาสที่ไม่เปลี่ยนสภาพจากแพ็คเกจ java.time ซึ่งแสดงจุดบนเส้นเวลาใน UTC ด้วยความแม่นยำระดับนาโนวินาที แตกต่างจาก LocalDateTime ตรงที่ Instant ไม่มีวันที่และเวลาในรูปแบบที่มนุษย์อ่านได้ — มันคือการแสดงช่วงเวลาของเครื่องจักร ตามข้อกำหนดของ Oracle Java 17 (2024) Instant ได้รับการออกแบบสำหรับการแลกเปลี่ยนการประทับเวลาของเครื่องจักรและเป็นอะนาล็อกของ System.currentTimeMillis() แต่มีความแม่นยำระดับนาโนวินาที
ประเด็นสำคัญ
Instant คือคลาสที่จำลองจุดเดียวบนเส้นเวลา การแสดงภายในประกอบด้วยสองฟิลด์: long seconds (จำนวนวินาทีจาก 1970-01-01T00:00:00Z) และ int nanos (นาโนวินาทีภายในวินาทีปัจจุบัน ตั้งแต่ 0 ถึง 999999999)
ช่วงค่าของ Instant อยู่ที่ -31557014167219200 ถึง 31556889864403199 วินาทีจากยุค ครอบคลุมประมาณ 292 ล้านปีในทั้งสองทิศทาง ซึ่งเพียงพอสำหรับงานปฏิบัติใด ๆ รวมถึงการคำนวณทางดาราศาสตร์
ตาม Baeldung (2024) Instant คือสะพานระหว่างประเภทที่มนุษย์อ่านได้ (LocalDateTime, ZonedDateTime) และรูปแบบเครื่องจักร (การประทับเวลาในมิลลิวินาที) Instant ใช้สำหรับการบันทึก การแคช การซิงโครไนซ์ และงานทั้งหมดที่ช่วงเวลาสัมบูรณ์ในเวลามีความสำคัญ
คลาสนี้อินเทอร์เฟส Comparable (สำหรับการเปรียบเทียบช่วงเวลา) และ Temporal (สำหรับใช้ใน API ทั่วไปของ java.time) Instant ไม่เปลี่ยนสภาพ — เมธอดทั้งหมดคืนค่าวัตถุใหม่
ก่อน Java 8 มีการใช้ java.util.Date และ System.currentTimeMillis() สำหรับทำงานกับช่วงเวลา ทั้งสองวิธีมีข้อเสีย Date เปลี่ยนแปลงได้ ไม่ปลอดภัยต่อเธรด จัดเก็บเวลาเป็นมิลลิวินาทีจากยุค แต่ชื่อเมธอดล้าสมัย (getYear() คืนค่า 116 สำหรับปี 2016)
Long (การประทับเวลาอย่างง่าย) รวดเร็วและกะทัดรัด แต่ไม่มีการสนับสนุนนาโนวินาทีในตัว ไม่แสดงในรูปแบบที่อ่านได้ และต้องการการแยกวิเคราะห์ด้วยตนเองระหว่างการดีบัก วิธี Long ยังไม่แยกประเภทข้อมูล — นักพัฒนาอาจส่งค่าที่ไม่ถูกต้อง
Instant แก้ปัญหาทั้งหมดเหล่านี้ มันไม่เปลี่ยนสภาพ มีข้อมูลความแม่นยำที่ชัดเจน (วินาที + นาโนวินาที) ซีเรียลไลซ์เป็นรูปแบบ ISO-8601 “2026-07-21T15:00:00Z” และมี API ที่หลากหลายสำหรับการแปลง ตาม SonarSource (2024) Instant เป็นการแทนที่ Date ที่แนะนำในโปรเจ็กต์ใหม่ทั้งหมด
ช่วงเวลาปัจจุบันได้รับผ่าน Instant.now() แตกต่างจาก LocalDateTime.now() ตรงที่ Instant.now() จะคืนค่าเวลาใน UTC เสมอ โดยไม่สนใจเขตเวลาของอุปกรณ์ สิ่งนี้ทำให้เหมาะสำหรับการประทับเวลาของเซิร์ฟเวอร์
จากค่าที่มีอยู่: Instant.ofEpochSecond(long epochSecond) — จากวินาทีตั้งแต่ยุค, Instant.ofEpochMilli(long epochMilli) — จากมิลลิวินาที, Instant.parse(CharSequence) — จากสตริง ISO-8601 (“2026-07-21T15:00:00Z”)
สำหรับการอ่าน: getEpochSecond() — จำนวนวินาทีตั้งแต่ยุค, toEpochMilli() — จำนวนมิลลิวินาที, getNano() — นาโนวินาที เมธอด toString() คืนค่าสตริงในรูปแบบ ISO-8601
val now = Instant.now()
val fromSeconds = Instant.ofEpochSecond(1784700000)
val fromMillis = Instant.ofEpochMilli(1784700000000)
val parsed = Instant.parse("2026-07-21T15:00:00Z")
val epochSecond = now.getEpochSecond()
val epochMilli = now.toEpochMilli()
val nanos = now.getNano()
Instant ถูกแปลงเป็น ZonedDateTime ผ่าน atZone(ZoneId) ตัวอย่างเช่น Instant.now().atZone(ZoneId.of(“Europe/Moscow”)) คืนค่า ZonedDateTime สำหรับมอสโก หากไม่มีโซน การแปลงเป็นไปไม่ได้ — Instant ไม่มีข้อมูลปฏิทิน
การแปลง Instant เป็น LocalDateTime: atZone(ZoneId).toLocalDateTime() วิธีนี้ชัดเจนและไม่สูญเสียข้อมูล การแปลงย้อนกลับ: LocalDateTime.atZone(ZoneId).toInstant()
สำหรับความเข้ากันได้กับ java.util.Date: Date.from(instant) และ date.toInstant() นี่คือการแปลงสองทิศทางที่รักษาความแม่นยำถึงมิลลิวินาที (Date ไม่รองรับนาโนวินาที) สำหรับ java.sql.Timestamp ให้ใช้ Timestamp.from(instant) พร้อมการสนับสนุนนาโนวินาที
val instant = Instant.now()
val zoned = instant.atZone(ZoneId.of("Europe/Moscow"))
val localDateTime = instant
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
val oldDate = Date.from(instant)
val backToInstant = oldDate.toInstant()
คุณสมบัติหลักของ Instant คือมันเป็นอิสระจากเขตเวลาโดยสมบูรณ์ Instant.now() คืนค่าผลลัพธ์เดียวกันบนอุปกรณ์ใดก็ได้ทุกที่ในโลก ซึ่งทำได้โดยการกำหนดเวลาใน UTC
เขตเวลาจำเป็นสำหรับการแสดง Instant ให้มนุษย์ดูเท่านั้น สำหรับสิ่งนี้ ใช้ atZone(ZoneId) ZoneId.systemDefault() คืนค่าเขตเวลาของอุปกรณ์ที่ตั้งค่าในระบบปฏิบัติการ ZoneOffset.UTC คือค่าคงที่สำหรับ UTC
ในระบบกระจาย แนะนำให้จัดเก็บและส่งการประทับเวลาทั้งหมดใน Instant (หรือ OffsetDateTime พร้อม ZoneOffset.UTC) การแปลงเป็นเวลาท้องถิ่นจะดำเนินการบนไคลเอนต์ก่อนแสดงให้ผู้ใช้ดูเท่านั้น สิ่งนี้ป้องกันความสับสนเกี่ยวกับเขตเวลา
ในแอปพลิเคชัน Android แบบกระจาย การซิงโครไนซ์เวลามีความสำคัญต่อการแคชที่ถูกต้อง การแจ้งเตือน และการแก้ไขร่วมกัน Instant เป็นตัวเลือกธรรมชาติสำหรับงานนี้เนื่องจากการยึดกับ UTC
เมื่อเปรียบเทียบการประทับเวลาจากอุปกรณ์ต่าง ๆ ต้องพิจารณาว่านาฬิการะบบอาจแตกต่างกัน แนะนำให้ใช้เวลาเซิร์ฟเวอร์เป็นข้อมูลอ้างอิง เซิร์ฟเวอร์คืนค่า Instant ใน UTC และไคลเอนต์เปรียบเทียบกับ Instant ในเครื่องเฉพาะสำหรับการคำนวณสัมพัทธ์
ในการคำนวณความแตกต่างระหว่างสองช่วงเวลา ให้ใช้ Duration.between(Instant start, Instant end) เมธอดนี้คืนค่า Duration ซึ่งสามารถแปลงเป็นชั่วโมง นาที วินาที เมธอด isAfter() และ isBefore() อนุญาตให้เปรียบเทียบช่วงเวลา
fun isCacheExpired(
cachedAt: Instant,
ttlMinutes: Long
): Boolean {
val elapsed = Duration.between(cachedAt, Instant.now())
return elapsed.toMinutes() >= ttlMinutes
}
ตัวอย่างแรกคือการบันทึกเหตุการณ์พร้อมการประทับเวลา Instant ถูกบันทึกในฐานข้อมูล Room และส่งไปยังเซิร์ฟเวอร์ การประทับเวลาถูกบันทึกใน UTC เพื่อการตีความที่ชัดเจน
data class EventLog(
val id: Long = 0,
val eventName: String,
val timestamp: Instant
)
class Converters {
@TypeConverter
fun fromInstant(value: Instant?): Long? {
return value?.toEpochMilli()
}
@TypeConverter
fun toInstant(value: Long?): Instant? {
return value?.let { Instant.ofEpochMilli(it) }
}
}
ตัวอย่างที่สองคือการกำหนดเวลาที่ผ่านไปตั้งแต่เหตุการณ์ เราใช้ Duration.between เพื่อแสดง “5 นาทีที่แล้ว”, “2 ชั่วโมงที่แล้ว” — รูปแบบที่พบได้ทั่วไปในแอปส่งข้อความและโซเชียลเน็ตเวิร์ก
fun timeAgo(instant: Instant): String {
val duration = Duration.between(instant, Instant.now())
return when {
duration.toMinutes() < 1 -> "just now"
duration.toHours() < 1 -> "${duration.toMinutes()} min ago"
duration.toDays() < 1 -> "${duration.toHours()} h ago"
else -> "${duration.toDays()} d ago"
}
}
ตัวอย่างที่สามคือการซิงโครไนซ์ข้อมูลระหว่างเซิร์ฟเวอร์และไคลเอนต์ เราใช้ Instant เพื่อติดตามเวลาอัปเดตล่าสุด
class SyncManager {
private var lastSyncAt: Instant? = null
fun sync() {
val syncStart = Instant.now()
// server request with lastSyncAt
lastSyncAt = syncStart
}
fun shouldSync(intervalMinutes: Long): Boolean {
val last = lastSyncAt ?: return true
return Duration.between(last, Instant.now())
.toMinutes() >= intervalMinutes
}
}
ข้อผิดพลาดแรกคือการใช้ Instant.now().toString() สำหรับแสดงให้ผู้ใช้ดู Instant แสดงผลในรูปแบบ UTC “2026-07-21T15:00:00Z” ซึ่งมนุษย์อ่านไม่ได้ แปลง Instant ผ่าน atZone() เป็นเขตเวลาท้องถิ่นเสมอก่อนแสดง
ข้อผิดพลาดที่สองคือการสูญเสียนาโนวินาทีเมื่อแปลงเป็น java.util.Date Date รองรับเฉพาะมิลลิวินาที หาก Instant มีนาโนวินาที พวกมันจะถูกทิ้งใน Date.from(instant) ใช้ Instant.truncatedTo(ChronoUnit.MILLIS) เพื่อระบุความแม่นยำอย่างชัดเจน
ข้อผิดพลาดที่สามคือความสับสนระหว่าง toEpochMilli() และ getEpochSecond() toEpochMilli() คืนค่าจำนวนมิลลิวินาทีตั้งแต่ยุค (long) ในขณะที่ getEpochSecond() คืนค่าจำนวนวินาที (long) การสับสนเมธอดเหล่านี้อาจทำให้เกิดข้อผิดพลาด 1,000 เท่า
ข้อผิดพลาดที่สี่คือการสันนิษฐานว่า Instant.now() ซิงโครไนซ์ในทุกอุปกรณ์ นาฬิการะบบอาจแตกต่างกันเป็นนาทีหรือแม้แต่ชั่วโมง สำหรับการดำเนินการที่สำคัญต่อเวลา (การยืนยันตัวตน การชำระเงิน) ให้ใช้ Instant ของเซิร์ฟเวอร์ เป็นแหล่งความจริง
คำถามที่พบบ่อย
System.currentTimeMillis() คืนค่า long — จำนวนมิลลิวินาทีตั้งแต่ยุคโดยไม่มีการผูกเขตเวลา Instant ให้ฟังก์ชันการทำงานเดียวกันแต่มีความแม่นยำระดับนาโนวินาทีและ API ที่หลากหลายสำหรับการแปลง การเปรียบเทียบ และความเข้ากันได้กับ java.time
Room ไม่รองรับ Instant โดยตรง ใช้ TypeConverter ที่แปลง Instant เป็น Long (toEpochMilli) และกลับ (Instant.ofEpochMilli) สำหรับความแม่นยำระดับนาโนวินาที ให้บันทึกสองฟิลด์: ยุค-วินาทีและนาโนวินาที
ใช่ Instant ไม่เปลี่ยนสภาพและใช้ equals() และ hashCode() อย่างถูกต้อง Instant สองตัวที่มีค่าเดียวกันจะเท่ากัน สิ่งนี้ทำให้เป็นคีย์ที่เชื่อถือได้สำหรับ HashMap และคอลเล็กชันอื่น ๆ แตกต่างจาก java.util.Date ที่เปลี่ยนแปลงได้
ใช้ Duration.between(start, end) เพื่อรับ Duration หรือ ChronoUnit.SECONDS.between(start, end) สำหรับความแตกต่างในวินาที (long) Duration มีเมธอด toMinutes(), toHours(), toDays() และ toNanos()
Instant ออกแบบมาเป็นจุดสัมบูรณ์บนเส้นเวลา หากไม่ระบุเขตเวลาหรือ UTC การแยกวิเคราะห์เป็นไปไม่ได้เพราะ Instant ไม่มีข้อมูลปฏิทิน ส่วนต่อท้าย “Z” หมายถึงออฟเซ็ตเป็นศูนย์ (UTC) และจำเป็นสำหรับรูปแบบ ISO-8601
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ