Instant: คืออะไร การประทับเวลา และการประยุกต์ใช้ในการพัฒนา

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

Instant — คลาสที่ไม่เปลี่ยนสภาพจากแพ็คเกจ java.time ซึ่งแสดงจุดบนเส้นเวลาใน UTC ด้วยความแม่นยำระดับนาโนวินาที แตกต่างจาก LocalDateTime ตรงที่ Instant ไม่มีวันที่และเวลาในรูปแบบที่มนุษย์อ่านได้ — มันคือการแสดงช่วงเวลาของเครื่องจักร ตามข้อกำหนดของ Oracle Java 17 (2024) Instant ได้รับการออกแบบสำหรับการแลกเปลี่ยนการประทับเวลาของเครื่องจักรและเป็นอะนาล็อกของ System.currentTimeMillis() แต่มีความแม่นยำระดับนาโนวินาที

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

  • Instant — ช่วงเวลาใน UTC ด้วยความแม่นยำระดับนาโนวินาที ไม่เปลี่ยนสภาพและปลอดภัยต่อเธรด
  • จัดเก็บเวลาเป็นจำนวนวินาทีจากยุค (1970-01-01T00:00:00Z) บวกนาโนวินาที
  • Instant.now() คืนค่าช่วงเวลาปัจจุบันใน UTC โดยไม่ขึ้นกับเขตเวลาของอุปกรณ์
  • สำหรับการแปลงเป็นรูปแบบที่มนุษย์อ่านได้ ใช้ atZone(ZoneId) ซึ่งคืนค่า ZonedDateTime
  • Instant เป็นประเภทที่ต้องการสำหรับการส่งผ่านการประทับเวลาใน API และระบบกระจาย

Instant คืออะไร?

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 ไม่เปลี่ยนสภาพ — เมธอดทั้งหมดคืนค่าวัตถุใหม่

Instant เทียบกับ Date เทียบกับ Long

ก่อน 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

ช่วงเวลาปัจจุบันได้รับผ่าน 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

kotlin
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 เป็นรูปแบบอื่น

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) พร้อมการสนับสนุนนาโนวินาที

kotlin
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 คือมันเป็นอิสระจากเขตเวลาโดยสมบูรณ์ Instant.now() คืนค่าผลลัพธ์เดียวกันบนอุปกรณ์ใดก็ได้ทุกที่ในโลก ซึ่งทำได้โดยการกำหนดเวลาใน UTC

เขตเวลาจำเป็นสำหรับการแสดง Instant ให้มนุษย์ดูเท่านั้น สำหรับสิ่งนี้ ใช้ atZone(ZoneId) ZoneId.systemDefault() คืนค่าเขตเวลาของอุปกรณ์ที่ตั้งค่าในระบบปฏิบัติการ ZoneOffset.UTC คือค่าคงที่สำหรับ UTC

ในระบบกระจาย แนะนำให้จัดเก็บและส่งการประทับเวลาทั้งหมดใน Instant (หรือ OffsetDateTime พร้อม ZoneOffset.UTC) การแปลงเป็นเวลาท้องถิ่นจะดำเนินการบนไคลเอนต์ก่อนแสดงให้ผู้ใช้ดูเท่านั้น สิ่งนี้ป้องกันความสับสนเกี่ยวกับเขตเวลา

การซิงโครไนซ์เวลาผ่าน Instant

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

เมื่อเปรียบเทียบการประทับเวลาจากอุปกรณ์ต่าง ๆ ต้องพิจารณาว่านาฬิการะบบอาจแตกต่างกัน แนะนำให้ใช้เวลาเซิร์ฟเวอร์เป็นข้อมูลอ้างอิง เซิร์ฟเวอร์คืนค่า Instant ใน UTC และไคลเอนต์เปรียบเทียบกับ Instant ในเครื่องเฉพาะสำหรับการคำนวณสัมพัทธ์

ในการคำนวณความแตกต่างระหว่างสองช่วงเวลา ให้ใช้ Duration.between(Instant start, Instant end) เมธอดนี้คืนค่า Duration ซึ่งสามารถแปลงเป็นชั่วโมง นาที วินาที เมธอด isAfter() และ isBefore() อนุญาตให้เปรียบเทียบช่วงเวลา

kotlin
fun isCacheExpired(
    cachedAt: Instant,
    ttlMinutes: Long
): Boolean {
    val elapsed = Duration.between(cachedAt, Instant.now())
    return elapsed.toMinutes() >= ttlMinutes
}

ตัวอย่างเชิงปฏิบัติใน Android

ตัวอย่างแรกคือการบันทึกเหตุการณ์พร้อมการประทับเวลา Instant ถูกบันทึกในฐานข้อมูล Room และส่งไปยังเซิร์ฟเวอร์ การประทับเวลาถูกบันทึกใน UTC เพื่อการตีความที่ชัดเจน

kotlin
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 ชั่วโมงที่แล้ว” — รูปแบบที่พบได้ทั่วไปในแอปส่งข้อความและโซเชียลเน็ตเวิร์ก

kotlin
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 เพื่อติดตามเวลาอัปเดตล่าสุด

kotlin
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 ของเซิร์ฟเวอร์ เป็นแหล่งความจริง

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

Instant แตกต่างจาก System.currentTimeMillis() อย่างไร?

System.currentTimeMillis() คืนค่า long — จำนวนมิลลิวินาทีตั้งแต่ยุคโดยไม่มีการผูกเขตเวลา Instant ให้ฟังก์ชันการทำงานเดียวกันแต่มีความแม่นยำระดับนาโนวินาทีและ API ที่หลากหลายสำหรับการแปลง การเปรียบเทียบ และความเข้ากันได้กับ java.time

วิธีการบันทึก Instant ใน Room Database?

Room ไม่รองรับ Instant โดยตรง ใช้ TypeConverter ที่แปลง Instant เป็น Long (toEpochMilli) และกลับ (Instant.ofEpochMilli) สำหรับความแม่นยำระดับนาโนวินาที ให้บันทึกสองฟิลด์: ยุค-วินาทีและนาโนวินาที

สามารถใช้ Instant เป็นคีย์ใน HashMap ได้หรือไม่?

ใช่ Instant ไม่เปลี่ยนสภาพและใช้ equals() และ hashCode() อย่างถูกต้อง Instant สองตัวที่มีค่าเดียวกันจะเท่ากัน สิ่งนี้ทำให้เป็นคีย์ที่เชื่อถือได้สำหรับ HashMap และคอลเล็กชันอื่น ๆ แตกต่างจาก java.util.Date ที่เปลี่ยนแปลงได้

วิธีการรับความแตกต่างระหว่าง Instant สองตัว?

ใช้ Duration.between(start, end) เพื่อรับ Duration หรือ ChronoUnit.SECONDS.between(start, end) สำหรับความแตกต่างในวินาที (long) Duration มีเมธอด toMinutes(), toHours(), toDays() และ toNanos()

ทำไม Instant.parse() ต้องมีส่วนต่อท้าย Z หรือออฟเซ็ต?

Instant ออกแบบมาเป็นจุดสัมบูรณ์บนเส้นเวลา หากไม่ระบุเขตเวลาหรือ UTC การแยกวิเคราะห์เป็นไปไม่ได้เพราะ Instant ไม่มีข้อมูลปฏิทิน ส่วนต่อท้าย “Z” หมายถึงออฟเซ็ตเป็นศูนย์ (UTC) และจำเป็นสำหรับรูปแบบ ISO-8601

สรุป

  • Instant — คลาสที่ไม่เปลี่ยนสภาพสำหรับช่วงเวลาสัมบูรณ์ใน UTC ด้วยความแม่นยำระดับนาโนวินาที เหมาะสำหรับการประทับเวลาในระบบกระจาย
  • แตกต่างจาก java.util.Date ตรงที่ Instant ไม่เปลี่ยนสภาพ ปลอดภัยต่อเธรด และมีความแม่นยำระดับนาโนวินาที แตกต่างจาก Long — ระบุประเภทอย่างชัดเจนและไม่ทำให้สับสนกับค่าตัวเลขอื่น
  • สำหรับแสดงให้ผู้ใช้ดู Instant ถูกแปลงผ่าน atZone(ZoneId) เป็น ZonedDateTime มิฉะนั้นเวลาจะแสดงใน UTC
  • สำหรับการส่งใน API ใช้ Instant.toString() (ISO-8601) หรือ toEpochMilli() เพื่อความเข้ากันได้กับรูปแบบเก่า
  • เมื่อทำงานกับ Room ให้บันทึก Instant เป็น Long ผ่าน TypeConverter โดยใช้เมธอด toEpochMilli()
  • ในการคำนวณเวลาที่ผ่านไป ใช้ Duration.between() ซึ่งจัดการนาโนวินาทีได้อย่างถูกต้อง
  • อย่าพึ่งพาการซิงโครไนซ์นาฬิกาของอุปกรณ์ — สำหรับการดำเนินการที่สำคัญ ให้ใช้ Instant ของเซิร์ฟเวอร์ เป็นแหล่งความจริง

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

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

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

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