อธิบาย 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) | มักพบใน |
|---|---|---|---|
| วินาที | 1700000000 | 10 | date +%s, API จำนวนมาก, claim exp ของ JWT |
| มิลลิวินาที | 1700000000000 | 13 | Date.now() ใน JavaScript, System.currentTimeMillis() ใน Java |
| ไมโครวินาที | 1700000000000000 | 16 | ฐานข้อมูลและระบบบันทึกล็อกบางระบบ |
| นาโนวินาที | 1700000000000000000 | 19 | UnixNano() ใน 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 ของตัวเอง