Giả sử bạn có một app đặt vé máy bay. Chuyến bay cất cánh từ New York lúc 10:00 giờ địa phương và hạ cánh ở London lúc 22:00 giờ địa phương. Nếu không để ý đến múi giờ, server của bạn có thể biến mọi thứ thành một mớ hỗn độn, hiển thị sai giờ hạ cánh.
Múi giờ — đúng kiểu bạn thân chí cốt (hoặc kẻ thù truyền kiếp khi mọi thứ không như ý). Nếu user của bạn ở nhiều quốc gia khác nhau hoặc bạn cần làm việc với lịch trình phụ thuộc vào giờ địa phương (kiểu như giờ bay hoặc lịch sự kiện), thì việc xử lý múi giờ là cực kỳ quan trọng luôn.
Các loại dữ liệu thời gian
Bọn mình đã nói qua rồi, có hai loại dữ liệu để làm việc với dấu thời gian:
TIMESTAMP: ngày và giờ không có múi giờ.TIMESTAMPTZ: ngày và giờ có múi giờ.
Cùng xem lại ví dụ cho dễ hiểu nhé.
-- Tạo bảng với hai cột: TIMESTAMP và TIMESTAMPTZ
CREATE TABLE flight_schedule (
flight_id SERIAL PRIMARY KEY,
departure_time TIMESTAMP,
departure_time_with_tz TIMESTAMPTZ
);
-- Thêm dữ liệu
INSERT INTO flight_schedule (departure_time, departure_time_with_tz)
VALUES
('2023-10-25 10:00:00', '2023-10-25 10:00:00+00');
-- Kiểm tra dữ liệu
SELECT * FROM flight_schedule;
Kết quả sẽ phụ thuộc vào múi giờ của server bạn. Ví dụ:
| flight_id | departure_time | departure_time_with_tz |
|---|---|---|
| 1 | 2023-10-25 10:00:00 | 2023-10-25 10:00:00+00 |
Điểm khác biệt chính:
- Cột
departure_timechỉ lưu ngày và giờ mà không gắn với bất kỳ múi giờ nào. - Cột
departure_time_with_tzlưu ngày và giờ kèm thông tin múi giờ (+00trong ví dụ này).
Chuyển đổi thời gian giữa các múi giờ
Để làm việc với múi giờ trong PostgreSQL, bạn dùng hàm AT TIME ZONE.
Chuyển đổi UTC sang giờ địa phương
Giả sử bạn có một dấu thời gian ở định dạng UTC (giờ phối hợp quốc tế). Bạn muốn hiển thị nó cho user ở múi giờ America/New_York.
SELECT
'2023-10-25 14:00:00+00'::TIMESTAMPTZ AT TIME ZONE 'America/New_York' AS local_time;
Kết quả:
| local_time |
|---|
| 2023-10-25 10:00:00 |
AT TIME ZONE ở đây như kiểu phép thuật: nó chuyển đổi thời gian từ UTC sang múi giờ bạn chỉ định.
Chuyển đổi giờ địa phương sang UTC
Giờ thử bài toán ngược lại: bạn có thời gian ở America/New_York, và muốn chuyển nó sang UTC.
SELECT
'2023-10-25 10:00:00'::TIMESTAMP AT TIME ZONE 'America/New_York' AS utc_time;
Kết quả:
| utc_time |
|---|
| 2023-10-25 14:00:00+00 |
Lưu ý là kết quả sẽ ở dạng TIMESTAMPTZ, vì nó bao gồm thông tin về múi giờ (UTC trong trường hợp này).
Làm việc với kiểu dữ liệu TIMESTAMPTZ
Khi bạn dùng TIMESTAMPTZ, PostgreSQL sẽ tự động tính đến múi giờ của server (hoặc múi giờ bạn set).
Bạn có thể set múi giờ cho session hiện tại bằng lệnh:
SET TIMEZONE = 'Europe/Istanbul';
Sau đó mọi thao tác với TIMESTAMPTZ sẽ dựa trên múi giờ này.
Ví dụ: thêm và lấy dữ liệu
-- Set múi giờ
SET TIMEZONE = 'Europe/Istanbul';
-- Thêm dữ liệu
INSERT INTO flight_schedule (departure_time_with_tz)
VALUES ('2023-10-25 10:00:00+00');
-- Kiểm tra dữ liệu
SELECT departure_time_with_tz FROM flight_schedule;
Kết quả ở múi giờ Europe/Istanbul:
| departure_time_with_tz |
|---|
| 2023-10-25 13:00:00+03 |
PostgreSQL sẽ tự động chuyển đổi thời gian từ UTC sang múi giờ bạn đã chọn.
Ví dụ thực tế
Xử lý múi giờ cho lịch trình. Giả sử bạn có bảng lịch trình chuyến bay, mỗi bản ghi lưu giờ khởi hành ở UTC. Bạn muốn hiển thị giờ khởi hành cho từng chuyến theo giờ địa phương.
SELECT
flight_id,
departure_time_with_tz AT TIME ZONE 'America/New_York' AS local_time
FROM flight_schedule;
So sánh dữ liệu thời gian từ các múi giờ khác nhau. Giả sử bạn so sánh hai sự kiện diễn ra ở hai thành phố khác nhau. PostgreSQL cho phép làm điều này, tự động chuyển dữ liệu về cùng một múi giờ.
SELECT
'2023-10-25 10:00:00+03'::TIMESTAMPTZ > '2023-10-25 07:00:00+00'::TIMESTAMPTZ AS event_one_later;
Kết quả:
| event_one_later |
|---|
| t |
So sánh trả về true, vì 10:00+03 tương đương với 07:00+00.
Mẹo và những lỗi hay gặp
Làm việc với thời gian — đúng kiểu dễ dính bẫy. Đây là những lỗi hay gặp nhất:
- Dùng
TIMESTAMPthay vìTIMESTAMPTZ, rồi ngạc nhiên vì giờ không khớp — do múi giờ bị bỏ qua luôn. - Không biết server đang chạy ở múi giờ nào, nên dữ liệu thêm vào một giờ, đọc ra lại thành giờ khác.
- Gõ sai tên múi giờ khi dùng
AT TIME ZONE— và nhận lỗi hoặc ra giờ sai bét.
Để tránh bị troll:
- Gần như luôn luôn dùng
TIMESTAMPTZ, nhất là khi dữ liệu có thể phụ thuộc vào múi giờ. - Lưu thời gian ở UTC, còn khi hiển thị thì chuyển sang múi giờ user cần.
- Nếu muốn tìm hiểu sâu hơn, đây là tài liệu chính thức PostgreSQL về thời gian và múi giờ — cực kỳ hữu ích luôn.
GO TO FULL VERSION