Làm việc với Unix Timestamp: Giây hay mili giây, múi giờ và mốc tràn số năm 2038
Unix timestamp là gì, phân biệt giây và mili giây, cách chuyển đổi trong JavaScript, Python, SQL, nguyên tắc xử lý múi giờ và vì sao sự cố năm 2038 vẫn có thể làm hỏng phần mềm của bạn.
Hầu như hệ thống nào cũng lưu thời gian ở đâu đó: log, đơn hàng, phiên đăng nhập, hạn của token. Bên dưới, phần lớn đều dựa vào Unix timestamp. Trông chỉ là một con số đơn giản, nhưng nó gây ra rất nhiều lỗi, từ lỗi lệch 1000 lần cho đến nguy cơ tràn số vào tháng 1/2038.
Unix timestamp là gì?
Unix timestamp (còn gọi là Unix time, POSIX time hay epoch time) là số giây đã trôi qua kể từ 00:00:00 UTC ngày 1/1/1970, thời điểm được gọi là "Unix epoch".
0 → 1970-01-01 00:00:00 UTC
1700000000 → 2023-11-14 22:13:20 UTC
2147483647 → 2038-01-19 03:14:07 UTC
Những ưu điểm khiến nó có mặt ở khắp nơi:
- Không phụ thuộc múi giờ. Cùng một thời điểm thì timestamp ở Hà Nội và New York là như nhau.
- Dễ so sánh và tính toán. "Token hết hạn chưa?" chỉ là
now > exp. "Một ngày sau" là+ 86400. - Gọn nhẹ. Một số nguyên rẻ để lưu trữ, đánh chỉ mục và truyền qua mạng.
Bạn có thể đổi bất kỳ timestamp nào sang ngày giờ dễ đọc (và ngược lại) bằng công cụ chuyển đổi Unix Timestamp.
Giây hay mili giây?
Lỗi timestamp phổ biến nhất là nhầm đơn vị:
| Số chữ số | Đơn vị | Nguồn thường gặp |
|---|---|---|
10 (vd. 1700000000) |
Giây | Unix/Linux, PHP, Python time.time() (dạng float), exp/iat của JWT, đa số API |
13 (vd. 1700000000000) |
Mili giây | JavaScript Date.now(), Java System.currentTimeMillis() |
| 16 / 19 | Micro / nano giây | Cơ sở dữ liệu, Go time.UnixNano(), hệ thống tracing |
Nếu ngày hiển thị ra tháng 1/1970, rất có thể bạn truyền giây vào chỗ cần mili giây. Nếu ra một năm xa tít như 55.000, bạn đã làm ngược lại.
Chuyển đổi trong các ngôn ngữ phổ biến
JavaScript
// thời điểm hiện tại
const seconds = Math.floor(Date.now() / 1000);
// timestamp → Date (nhớ nhân 1000)
const date = new Date(1700000000 * 1000);
date.toISOString(); // "2023-11-14T22:13:20.000Z"
Python
from datetime import datetime, timezone
ts = int(datetime.now(timezone.utc).timestamp())
datetime.fromtimestamp(1700000000, tz=timezone.utc)
# datetime.datetime(2023, 11, 14, 22, 13, 20, tzinfo=datetime.timezone.utc)
SQL
-- PostgreSQL
SELECT to_timestamp(1700000000); -- timestamptz
SELECT extract(epoch FROM now())::bigint;
-- MySQL
SELECT FROM_UNIXTIME(1700000000);
SELECT UNIX_TIMESTAMP();
Shell
date +%s # timestamp hiện tại
date -u -d @1700000000 # GNU date: Tue Nov 14 22:13:20 UTC 2023
Nguyên tắc xử lý múi giờ
Timestamp không có múi giờ. Rắc rối chỉ xuất hiện khi bạn đổi nó sang ngày giờ địa phương:
- Lưu thời điểm theo UTC (dạng timestamp hoặc cột
timestamptz) và chỉ đổi sang giờ địa phương khi hiển thị. - Gửi chuỗi ISO 8601 có độ lệch múi giờ trong API nếu không dùng timestamp dạng số, ví dụ
2023-11-14T22:13:20Zhoặc2023-11-15T05:13:20+07:00. - Lưu tên múi giờ của người dùng (ví dụ
Asia/Ho_Chi_Minh), không chỉ lưu độ lệch, khi cần lên lịch sự kiện trong tương lai. Độ lệch có thể thay đổi theo giờ mùa hè hoặc quyết định của chính phủ. - Nhớ rằng job lập lịch chạy theo múi giờ của máy chủ. Cron job trên máy chủ UTC muốn chạy lúc 09:00 giờ Việt Nam phải viết
0 2 * * *. Kiểm tra lịch bằng công cụ Phân tích biểu thức Cron.
Unix time cũng bỏ qua giây nhuận: mỗi ngày luôn được coi là đúng 86.400 giây. Với gần như mọi ứng dụng, đó chính là điều bạn muốn.
Sự cố năm 2038
Trước đây nhiều hệ thống lưu Unix time trong số nguyên có dấu 32 bit (time_t trên nền tảng 32 bit). Giá trị lớn nhất mà số này chứa được là:
2^31 − 1 = 2.147.483.647 → 2038-01-19 03:14:07 UTC
Một giây sau, giá trị bị tràn và quay vòng về −2.147.483.648, tức là 20:45:52 UTC ngày 13/12/1901. Phần mềm không chuẩn bị sẵn có thể đột nhiên cho rằng chứng chỉ chưa có hiệu lực, từ chối đăng nhập hoặc tính ra thời lượng âm.
Nguy cơ này còn thật không?
Hệ điều hành 64 bit hiện đại dùng time_t 64 bit, đủ dùng khoảng 292 tỷ năm. Linux cũng đã hỗ trợ thời gian 64 bit cho kiến trúc 32 bit (kernel 5.6 và glibc 2.34). Nhưng năm 2038 vẫn có thể gây rắc rối ở những nơi như:
- Thiết bị nhúng và IoT chạy firmware 32 bit cũ hàng chục năm: router, ô tô, bộ điều khiển công nghiệp, thiết bị y tế.
- Cột cơ sở dữ liệu: kiểu cột
TIMESTAMPcủa MySQL vẫn giới hạn đến2038-01-19 03:14:07UTC. Hãy dùngDATETIMEhoặcBIGINTcho những ngày có thể vượt quá mốc đó. - Định dạng tệp và giao thức có trường thời gian 32 bit.
- Mã ứng dụng ép timestamp về số nguyên 32 bit, ví dụ trường
int32trong Protocol Buffers hoặc cộtINT.
Và năm 2038 không còn xa. Một khoản vay 15 năm hay một chứng chỉ dài hạn tạo hôm nay đã có ngày kết thúc sau mốc đó.
Chuẩn bị thế nào
- Dùng số nguyên 64 bit (
BIGINT,int64) cho timestamp ở mọi nơi. - Tránh kiểu
TIMESTAMPcủa MySQL cho ngày trong tương lai. - Kiểm thử hệ thống với ngày sau 2038, ví dụ tạo hạn sử dụng là
2147483648và xem điều gì xảy ra. - Rà soát các thành phần nhúng và bên thứ ba về cách xử lý thời gian 32 bit.
Kết luận
Unix timestamp đơn giản mà mạnh mẽ: một số nguyên, một thời điểm chung cho cả thế giới. Để tránh lỗi, luôn biết đơn vị đang dùng (giây hay mili giây), lưu theo UTC, chỉ đổi sang giờ địa phương khi hiển thị, và đảm bảo không chỗ nào trong hệ thống "nhét" thời gian vào 32 bit.
Cần giải mã một timestamp trong log hay JWT ngay bây giờ? Công cụ chuyển đổi Unix Timestamp miễn phí đổi giây và mili giây sang ngày giờ ở mọi múi giờ, ngay trong trình duyệt của bạn.
Tác giả
Nguyễn Thành Nam
Full-Stack Engineer · Backend & Platform
Kỹ sư full-stack với hơn 9 năm kinh nghiệm xây dựng API và nền tảng lưu lượng lớn bằng Go, NestJS, PHP (Laravel, Phalcon) và Node.js. Chuyên về thiết kế API GraphQL, hệ thống thời gian thực và tối ưu hiệu năng; xây dựng NICETOOL.dev cho cộng đồng lập trình viên.