Thời gian có vẻ đơn giản cho đến khi bạn cố viết phần mềm xử lý nó đúng cách. Một sự kiện lên lịch lúc "3 giờ chiều" có nghĩa khác nhau ở Tokyo, London và New York. Giờ mùa hè dịch chuyển đồng hồ tiến hoặc lùi — nhưng không phải ở mọi nơi, và không vào cùng ngày. Một timestamp trông đúng trong môi trường cục bộ của bạn lại lỗi khi đưa lên production vì máy chủ ở múi giờ khác.
Múi giờ là một trong những chủ đề gây nhầm lẫn đáng tin cậy nhất trong phát triển phần mềm. Bài viết này giải thích cách chúng hoạt động, độ phức tạp đến từ đâu, và cách tránh những sai lầm phổ biến nhất.
Tại sao múi giờ tồn tại
Trái Đất quay 360 độ trong 24 giờ, nghĩa là mặt trời ở điểm cao nhất vào những thời điểm khác nhau tùy thuộc vào kinh độ của bạn. Trước thế kỷ 19, mỗi thành phố đặt đồng hồ theo giờ mặt trời địa phương — buổi trưa là khi mặt trời ở trên đỉnh đầu. Điều này hoạt động tốt cho đến khi đường sắt kết nối các thành phố xa nhau và lịch trình tàu cần một đồng hồ duy nhất, thống nhất.
Năm 1884, đại biểu từ 25 quốc gia gặp nhau tại Hội nghị Kinh tuyến Quốc tế ở Washington, D.C. và đồng ý chia thế giới thành 24 múi giờ tiêu chuẩn, mỗi múi lệch so với kinh tuyến gốc (kinh độ 0 độ) đi qua Greenwich, Anh.
Trên thực tế, ranh giới múi giờ theo biên giới chính trị, không phải đường kinh tuyến gọn gàng. Trung Quốc trải dài năm vùng địa lý nhưng dùng một giờ chính thức duy nhất (UTC+8). Ấn Độ dùng UTC+5:30 — lệch nửa giờ. Nepal dùng UTC+5:45. Bản đồ thực tế của các múi giờ rất lộn xộn.
UTC vs. GMT
GMT (Greenwich Mean Time) là giờ mặt trời trung bình tại Đài thiên văn Hoàng gia ở Greenwich. Nó là tham chiếu thời gian thế giới trong hơn một thế kỷ.
UTC (Coordinated Universal Time) thay thế GMT làm tiêu chuẩn quốc tế vào năm 1972. UTC dựa trên đồng hồ nguyên tử thay vì quan sát thiên văn, làm cho nó chính xác hơn nhiều. Cho hầu hết mục đích thực tế, UTC và GMT hiển thị cùng giờ, nhưng UTC là tham chiếu kỹ thuật đúng.
Tại sao "UTC" mà không phải "CUT"? Viết tắt là sự thỏa hiệp giữa tiếng Anh "Coordinated Universal Time" (CUT) và tiếng Pháp "Temps Universel Coordonné" (TUC). Không bên nào được viết tắt ưa thích, nên UTC được chọn như giải pháp trung lập về ngôn ngữ.
Giờ mùa hè: sự hỗn loạn có tổ chức
Khoảng 70 quốc gia áp dụng giờ mùa hè (DST), dịch đồng hồ tiến một giờ vào mùa xuân và lùi vào mùa thu. Mục đích là điều chỉnh giờ thức giấc với ánh sáng ban ngày. Kết quả là nguồn lỗi hai lần mỗi năm.
Các phức tạp chính:
- Không phổ quát. Phần lớn châu Phi, châu Á và Nam Mỹ không áp dụng DST. Trong nước Mỹ, Arizona và Hawaii không tham gia.
- Ngày khác nhau. EU chuyển vào Chủ nhật cuối của tháng 3 và tháng 10. Mỹ chuyển vào Chủ nhật thứ hai của tháng 3 và Chủ nhật đầu tiên của tháng 11. Chúng lệch nhau vài tuần mỗi năm.
- Thời gian mơ hồ. Khi đồng hồ lùi, giờ từ 1:00 sáng đến 2:00 sáng xảy ra hai lần. Timestamp "1:30 sáng" vào ngày đó là mơ hồ.
- Thời gian bị bỏ qua. Khi đồng hồ tiến, giờ từ 2:00 sáng đến 3:00 sáng không tồn tại. Cuộc họp lên lịch lúc 2:30 sáng vào ngày đó không bao giờ xảy ra.
- Thay đổi chính trị. Chính phủ có thể (và thực sự) thay đổi quy tắc DST với ít thông báo trước. Nga áp dụng DST vĩnh viễn năm 2011, sau đó chuyển sang giờ tiêu chuẩn vĩnh viễn năm 2014. Morocco đã thay đổi quy tắc DST nhiều lần.
ISO 8601: định dạng ngày phổ quát
Để tránh mơ hồ, tiêu chuẩn quốc tế ISO 8601 định nghĩa định dạng ngày và giờ rõ ràng:
2026-03-29T14:30:00Z
2026-03-29T14:30:00+02:00
2026-03-29T14:30:00-05:00
Tphân tách ngày và giờ.Zcó nghĩa là UTC (múi giờ "Zulu" trong thuật ngữ quân sự).+02:00hoặc-05:00là UTC offset.
Định dạng này rõ ràng, có thể sắp xếp dưới dạng văn bản thuần, và được hiểu phổ quát bởi các thư viện phân tích ngày. Khi nghi ngờ, hãy dùng ISO 8601.
Unix timestamp
Unix timestamp (còn gọi là epoch time hoặc POSIX time) là số giây đã trôi qua kể từ 1 tháng 1 năm 1970, 00:00:00 UTC — một thời điểm được gọi là Unix epoch.
| Dạng đọc được | Unix timestamp |
|---|---|
| 1970-01-01 00:00:00 UTC | 0 |
| 2000-01-01 00:00:00 UTC | 946684800 |
| 2026-03-29 12:00:00 UTC | 1774987200 |
Unix timestamp không có múi giờ — chúng luôn ở UTC. Điều này làm cho chúng lý tưởng để lưu trữ và so sánh thời gian trong phần mềm. Bạn chỉ chuyển đổi sang múi giờ địa phương ở lớp hiển thị.
Vấn đề năm 2038: Các hệ thống lưu Unix timestamp dưới dạng số nguyên có dấu 32-bit sẽ tràn vào ngày 19 tháng 1 năm 2038 lúc 03:14:07 UTC. Giá trị tối đa (2.147.483.647) quay lại số âm, được hiểu là tháng 12 năm 1901. Hầu hết hệ thống hiện đại dùng số nguyên 64-bit, sẽ không tràn trong 292 tỷ năm nữa.
Cơ sở dữ liệu múi giờ IANA
Phần mềm không chỉ cần UTC offset — nó cần biết toàn bộ lịch sử và quy tắc tương lai cho mỗi khu vực, bao gồm các chuyển đổi DST, thay đổi chính trị và bất thường lịch sử. Thông tin này nằm trong Cơ sở dữ liệu múi giờ IANA (còn gọi là cơ sở dữ liệu Olson hoặc tzdata).
Nó sử dụng các định danh như America/New_York, Europe/Paris, Asia/Tokyo. Mỗi mục ghi lại toàn bộ lịch sử UTC offset và quy tắc DST cho vị trí đó.
Đây là lý do bạn không bao giờ nên lưu múi giờ dưới dạng offset cố định như "+02:00". Offset cho biết chênh lệch hiện tại với UTC nhưng không nói gì về quy tắc DST. Europe/Paris là UTC+1 vào mùa đông và UTC+2 vào mùa hè. Định danh IANA nắm bắt cả hai.
Lỗi phổ biến trong phần mềm
- Lưu giờ địa phương không có múi giờ. Giá trị như
2026-03-29 14:30:00vô nghĩa nếu không biết nó thuộc múi giờ nào. Luôn lưu UTC hoặc kèm offset. - Giả định UTC offset bằng múi giờ. UTC+2 vào tháng 3 có thể là UTC+3 vào tháng 7 (nếu khu vực áp dụng DST). Lưu định danh IANA, không phải offset.
- Bỏ qua chuyển đổi DST khi lên lịch. Công việc hàng ngày lúc 2:30 sáng sẽ bị bỏ qua một lần mỗi năm và chạy hai lần một lần mỗi năm nếu bạn không xử lý DST.
- Giả định ngày có 24 giờ. Vào ngày chuyển đổi DST, một ngày có 23 hoặc 25 giờ. Tính "ngày mai cùng giờ" bằng cách cộng 86.400 giây sẽ lệch một giờ.
- Sử dụng JavaScript
Datemột cách ngây thơ.new Date("2026-03-29")được phân tích là UTC trên một số engine và giờ địa phương trên engine khác. Luôn chỉ rõ múi giờ.
Thực hành tốt nhất cho lập trình viên
- Lưu thời gian bằng UTC. Chuyển đổi sang múi giờ địa phương của người dùng chỉ ở lớp hiển thị.
- Dùng định danh múi giờ IANA (
America/New_York), không phải offset cố định (-05:00). - Dùng ISO 8601 để tuần tự hóa. Nó rõ ràng và có thể phân tích phổ quát.
- Dùng thư viện ngày trưởng thành. Trong JavaScript, dùng
Intl.DateTimeFormathoặc thư viện nhưdate-fns-tz. Trong Python, dùngzoneinfo(3.9+) hoặcpytz. Trong Java, dùngjava.time.ZonedDateTime. - Cập nhật
tzdata. Chính phủ thay đổi quy tắc DST. Hệ điều hành và runtime ngôn ngữ cần dữ liệu múi giờ hiện tại. - Kiểm thử với nhiều múi giờ. Đừng giả định máy chủ và người dùng cùng múi giờ.
Tìm hiểu thêm
Thời gian phức tạp một cách lừa dối, nhưng các quy tắc được ghi chép rõ ràng và các công cụ đã trưởng thành. Điều quan trọng là tôn trọng sự phức tạp thay vì giả định nó không tồn tại.
- Giải mã biểu thức Cron — lên lịch tác vụ qua các múi giờ
- Trình tạo băm và Trình kiểm tra Regex — thêm công cụ dành cho lập trình viên trên ToolK
