ข้ามไปยังเนื้อหา
LeedyBox
สนับสนุน

อธิบาย Unix Timestamp

Unix timestamp คือตัวเลขเพียงค่าเดียวที่แทนจุดเวลาหนึ่ง จัดเก็บและเปรียบเทียบได้ง่าย แต่หน่วย เขตเวลา และรูปแบบข้อความเป็นสาเหตุของข้อผิดพลาดในการแปลงจำนวนมาก

เวลา Unix คืออะไร

เวลา Unix (Unix time หรือที่เรียกว่า epoch time หรือ POSIX time) นับจำนวนวินาทีนับตั้งแต่ 1970-01-01 00:00:00 UTC ซึ่งเรียกว่า Unix epoch ค่า 0 คือ epoch นั่นเอง 1700000000 คือ 2023-11-14 22:13:20 UTC และค่าติดลบคือจุดเวลาก่อนปี 1970 เช่น -1 คือ 1969-12-31 23:59:59 UTC

ตัวเลขนี้ไม่มีเขตเวลา และหมายถึงจุดเวลาเดียวกันในทุกที่ จึงเหมาะสำหรับการจัดเก็บ เรียงลำดับ และเปรียบเทียบเหตุการณ์

เวลา Unix ไม่นับอธิกวินาที (leap second) ทุกวันมี 86,400 วินาทีพอดี เมื่อมีการเพิ่มอธิกวินาทีใน UTC ระบบจะนับวินาทีหนึ่งซ้ำ หรือกระจายวินาทีนั้นไปในช่วงเวลาที่ยาวขึ้น ("smearing") ผลต่างของเวลาที่คร่อมอธิกวินาทีจึงสั้นกว่าเวลาที่ผ่านไปจริงหนึ่งวินาที

วินาที มิลลิวินาที ไมโครวินาที หรือนาโนวินาที

หลายระบบนับเป็นหน่วยที่เล็กกว่า และตัวเลขไม่ได้บอกว่าเป็นหน่วยใด จุดเวลาเดียวกันจึงอาจปรากฏเป็นตัวเลขสี่ค่าที่ต่างกันมาก

หน่วย2023-11-14 22:13:20 UTCจำนวนหลัก (2001–2286)มักพบใน
วินาที170000000010date +%s, API จำนวนมาก, claim exp ของ JWT
มิลลิวินาที170000000000013Date.now() ใน JavaScript, System.currentTimeMillis() ใน Java
ไมโครวินาที170000000000000016ฐานข้อมูลและระบบบันทึกล็อกบางระบบ
นาโนวินาที170000000000000000019UnixNano() ใน Go, time.time_ns() ใน Python

การนับจำนวนหลักเป็นวิธีตรวจสอบที่เร็วที่สุดสำหรับวันที่ในปัจจุบัน เครื่องมือแปลงของ LeedyBox ใช้แนวคิดเดียวกัน ค่าที่ไม่เกิน 1011 จะอ่านเป็นวินาที ไม่เกิน 1014 เป็นมิลลิวินาที ไม่เกิน 1017 เป็นไมโครวินาที และค่าที่ใหญ่กว่านั้นเป็นนาโนวินาที หลักการคร่าว ๆ นี้ใช้ไม่ได้กับวันที่ที่ห่างจากปัจจุบันมาก จึงควรตรวจสอบหน่วยเมื่อผลลัพธ์ดูแปลก

ความแม่นยำก็สำคัญเช่นกัน ตัวเลขใน JavaScript แทนจำนวนเต็มได้อย่างแม่นยำเพียงถึง 253 (ประมาณ 9 × 1015) ค่านาโนวินาทีอย่าง 1700000000123456789 จึงสูญเสียหลักท้าย ๆ เมื่อแยกวิเคราะห์เป็นตัวเลขปกติ ให้เก็บค่าเหล่านี้เป็นสตริงหรือใช้ BigInt ซึ่งเครื่องมือแปลงก็ทำเช่นนี้ภายใน หากต้องการอ่าน claim exp ของโทเค็น ให้ถอดรหัสด้วยเครื่องมือ Base64 / URL / JWT

เขตเวลาและเวลาออมแสง

การแสดง timestamp เป็นวันที่และเวลาต้องใช้เขตเวลา UTC เป็นเวลาอ้างอิง ส่วนเวลาท้องถิ่นจะบวกค่าชดเชย (offset) เช่น +09:00 ในโซล หรือ −05:00 ในนิวยอร์กช่วงฤดูหนาว

ควรใช้ชื่อเขตเวลาของ IANA เช่น Asia/Seoul หรือ America/New_York แทนค่าชดเชยแบบคงที่และตัวย่อ เพราะชื่อจะรวมประวัติและกฎเวลาออมแสงของเขตเวลานั้นไว้ ขณะที่ตัวย่ออย่าง CST อาจหมายถึงหลายเขตเวลา กฎเหล่านี้มาจากฐานข้อมูลเขตเวลาซึ่งเปลี่ยนแปลงหลายครั้งต่อปี และเบราว์เซอร์ ระบบปฏิบัติการ และภาษาโปรแกรมต่างก็มีสำเนาของตัวเอง ผลลัพธ์สำหรับวันที่ในอดีตหรืออนาคตจึงอาจแตกต่างกัน

เวลาออมแสงทำให้เกิดกรณียุ่งยากสองกรณีเมื่อแปลงเวลาท้องถิ่นเป็น timestamp

  • ช่วงที่ขาดหาย (gap): เมื่อเลื่อนนาฬิกาไปข้างหน้า เวลาท้องถิ่นบางช่วงจะไม่มีอยู่ ใน America/New_York เวลา 2026-03-08 02:30 จะถูกข้ามไป เพราะนาฬิกากระโดดจาก 02:00 ไปเป็น 03:00
  • ช่วงที่ซ้อนกัน (overlap): เมื่อเลื่อนนาฬิกาถอยหลัง เวลาท้องถิ่นบางช่วงจะเกิดขึ้นสองครั้ง เวลา 2026-11-01 01:30 เกิดขึ้นครั้งหนึ่งใน EDT (UTC−04:00) และอีกครั้งในอีกหนึ่งชั่วโมงต่อมาใน EST (UTC−05:00)

ไลบรารีแต่ละตัวจัดการกรณีเหล่านี้ต่างกัน เครื่องมือแปลงของ LeedyBox ใช้เวลาที่ถูกต้องถัดไปในกรณีช่วงที่ขาดหาย (ในที่นี้คือ 03:30) และใช้เวลาที่เกิดขึ้นครั้งแรกในกรณีช่วงที่ซ้อนกัน พร้อมอธิบายไว้ในหมายเหตุ งานที่ตั้งเวลาไว้ก็พบปัญหาเดียวกัน ดูวิธีอ่านนิพจน์ Cron

ISO 8601, RFC 3339 และวันที่ใน HTTP

เมื่อจุดเวลาต้องให้คนอ่านได้ ให้เขียนในรูปแบบ ISO 8601 หรือ RFC 3339 ซึ่งเป็นโปรไฟล์สำหรับอินเทอร์เน็ตที่เข้มงวดกว่า

2023-11-14T22:13:20Z
2023-11-15T07:13:20+09:00
2023-11-14T22:13:20.500Z

Z หมายถึง UTC เมื่อมี Z หรือค่าชดเชย สตริงจะไม่กำกวม สตริงที่ไม่มีทั้งสองอย่าง เช่น 2023-11-14T22:13:20 คือเวลาท้องถิ่นในเขตเวลาที่ไม่ทราบ และตัวแยกวิเคราะห์แต่ละตัวอ่านค่านี้ไม่เหมือนกัน

ส่วนหัว HTTP เช่น Last-Modified และ Expires ใช้รูปแบบเก่าที่มาจากรูปแบบวันที่ของอีเมล (RFC 2822 ปัจจุบันคือ RFC 5322) โดยเขียนเป็นเวลา GMT เช่น Tue, 14 Nov 2023 22:13:20 GMT

ปัญหาปี 2038

ระบบรุ่นเก่าจำนวนมากเก็บเวลา Unix ไว้ในจำนวนเต็มแบบมีเครื่องหมายขนาด 32 บิต ค่าสูงสุดคือ 2147483647 ซึ่งตรงกับ 2038-01-19 03:14:07 UTC หนึ่งวินาทีหลังจากนั้นตัวนับจะล้นไปเป็น -2147483648 ซึ่งอ่านได้เป็น 1901-12-13 20:45:52 UTC ระบบปฏิบัติการและภาษาโปรแกรมในปัจจุบันส่วนใหญ่ใช้ค่าเวลาแบบ 64 บิต ซึ่งครอบคลุมหลายแสนล้านปี แต่ฟิลด์ 32 บิตยังคงพบได้ในอุปกรณ์ฝังตัว รูปแบบไฟล์ คอลัมน์ฐานข้อมูล และโปรโตคอลเครือข่าย หากโค้ดหรือข้อมูลของคุณเก็บวินาทีไว้ใน 32 บิต ให้วางแผนการเปลี่ยนแปลงแต่เนิ่น ๆ และทดสอบวันที่หลังปี 2038 ตั้งแต่ตอนนี้

Timestamp ในโค้ดและฐานข้อมูล

JavaScript ทำงานในหน่วยมิลลิวินาที

Math.floor(Date.now() / 1000);                  // เวลา Unix ปัจจุบันในหน่วยวินาที
new Date(1700000000 * 1000).toISOString();      // "2023-11-14T22:13:20.000Z"
Date.parse("2026-10-03T09:00:00+09:00") / 1000; // 1790985600

time.time() ของ Python คืนค่าวินาทีเป็นเลขทศนิยม (float) ให้ส่ง tz=timezone.utc ไปด้วย มิฉะนั้น fromtimestamp() จะคืนค่าเวลาท้องถิ่นของคอมพิวเตอร์โดยไม่มีเขตเวลา

import time
from datetime import datetime, timezone

int(time.time())                                     # วินาที
datetime.fromtimestamp(1700000000, tz=timezone.utc)  # 2023-11-14 22:13:20+00:00
datetime.fromisoformat("2026-10-03T09:00:00+09:00").timestamp()  # 1790985600.0

ใน SQL ฟังก์ชันจะแตกต่างกันไปตามฐานข้อมูล

-- PostgreSQL
SELECT extract(epoch FROM now());   -- วินาที พร้อมเศษทศนิยม
SELECT to_timestamp(1700000000);    -- timestamp with time zone
-- MySQL
SELECT UNIX_TIMESTAMP();
SELECT FROM_UNIXTIME(1700000000);   -- ตามเขตเวลาของเซสชัน

สำหรับการจัดเก็บ แนวปฏิบัติไม่กี่ข้อต่อไปนี้ช่วยป้องกันปัญหาส่วนใหญ่ได้

  • เก็บจุดเวลาที่เกิดขึ้นแล้วเป็น UTC ไม่ว่าจะในคอลัมน์ประเภท timestamp with time zone หรือเป็นจำนวนเต็ม 64 บิตที่ระบุหน่วยไว้ในชื่อคอลัมน์ เช่น created_at_ms
  • เก็บเหตุการณ์ในอนาคตที่อิงเวลาท้องถิ่น เช่น การประชุมเวลา 09:00 ที่โซล เป็นวันที่และเวลาท้องถิ่นพร้อมชื่อเขตเวลาของ IANA แล้วคำนวณ timestamp เมื่อจำเป็น เพื่อไม่ให้การเปลี่ยนกฎในภายหลังทำให้เวลาประชุมเลื่อนไป
  • อย่าใช้หลายหน่วยปนกันในคอลัมน์เดียว หน่วยที่ผิดจะทำให้ค่าคลาดเคลื่อนไป 1,000 เท่าหรือมากกว่า

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

ทำไม timestamp จึงแปลงเป็นวันที่ในปี 1970

ค่านั้นน่าจะถูกอ่านผิดหน่วย ค่าวินาทีที่ถูกอ่านเป็นมิลลิวินาทีจะได้วันที่ในเดือนมกราคม 1970 และค่ามิลลิวินาทีที่ถูกอ่านเป็นวินาทีจะได้วันที่ในอนาคตอีกหลายหมื่นปี ให้นับจำนวนหลัก หรือเลือกหน่วยในเครื่องมือแปลง

Unix timestamp ขึ้นอยู่กับเขตเวลาของผู้ใช้หรือไม่

ไม่ timestamp นับวินาทีจากจุดเวลาคงที่ใน UTC มีเพียงวันที่และเวลาที่แสดงสำหรับค่านั้นเท่านั้นที่ขึ้นอยู่กับเขตเวลา

เวลา Unix แทนอธิกวินาทีได้หรือไม่

ไม่ได้ เวลา Unix ถือว่าทุกวันมี 86,400 วินาที เวลา UTC อย่าง 23:59:60 จึงไม่มี timestamp ของตัวเอง