Hôm nay tụi mình lại nói về lỗi nữa. Bởi vì làm việc với ngày giờ giống như đi trên bãi mìn vậy: mọi thứ đều ổn cho đến khi bạn bước nhầm một phát.
Lỗi khi chọn kiểu dữ liệu
Đây thường là nguồn gốc của mọi rắc rối. Chọn sai kiểu dữ liệu có thể làm công sức xử lý ngày giờ của bạn đổ sông đổ biển.
Tình huống 1: Dùng DATE thay vì TIMESTAMP
Khi bạn lưu lại một sự kiện mà có cả ngày lẫn giờ, chỉ dùng DATE thôi thì sẽ mất thông tin quan trọng.
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
order_date DATE -- chỉ có ngày thôi
);
Thiết kế như này thì bạn sẽ không biết được hai đơn hàng đặt vào buổi sáng hay buổi tối. Tại sao lại bỏ lỡ cơ hội vừa uống cà phê vừa ngắm timestamp đẹp nhỉ?
Tình huống 2: Quên mất múi giờ
Nếu app của bạn phục vụ người dùng quốc tế, mà bạn chỉ lưu ngày giờ bằng TIMESTAMP mà không quan tâm đến múi giờ, thì dữ liệu của bạn sẽ thành kiểu "vô gia cư". TIMESTAMPTZ sẽ giải quyết vụ này cho bạn.
CREATE TABLE events (
event_time TIMESTAMP -- không có múi giờ
);
Không ai muốn nhầm event buổi tối ở New York thành buổi sáng ở Tokyo đâu. Dùng TIMESTAMPTZ đi nhé!
Lỗi khi dùng function
Tình huống 1: Định dạng sai trong TO_CHAR()
Rắc rối bắt đầu nếu bạn chỉ định sai định dạng. Ví dụ:
SELECT TO_CHAR(NOW(), 'YYYY-DD-MM'); -- Ôi, nhầm tháng với ngày rồi
Ở đây thay vì năm-tháng-ngày quen thuộc, bạn lại ra năm-ngày-tháng. Điều này có thể dẫn đến những tình huống hài hước (nhưng không phải lúc nào cũng vui) cho user của bạn. Luôn kiểm tra lại định dạng nhé.
Tình huống 2: Lỗi khi dùng TO_DATE()
Ngược lại, nếu bạn cố chuyển string thành ngày mà định dạng không khớp, PostgreSQL sẽ báo lỗi.
SELECT TO_DATE('10/31/2023', 'YYYY-MM-DD'); -- Lỗi! Định dạng không khớp.
Định dạng của string phải khớp hoàn toàn với cái bạn chỉ định. Ví dụ:
SELECT TO_DATE('2023-10-31', 'YYYY-MM-DD'); -- Chuẩn luôn.
Lỗi với khoảng thời gian (interval)
Tình huống 1: Ép kiểu ngầm không rõ ràng
Đôi khi bạn quên mất đặc điểm của việc ép kiểu. Ví dụ:
SELECT NOW() + '1'; -- LỖI! Không rõ '1' là gì.
PostgreSQL không hiểu bạn muốn cộng thêm một ngày. Cách đúng là:
SELECT NOW() + INTERVAL '1 day';
Tình huống 2: Lẫn lộn khi trừ interval
Cẩn thận khi cộng hoặc trừ interval nhé:
SELECT NOW() - INTERVAL '-1 day'; -- Cái này lại cộng thêm một ngày thay vì trừ!
Ở đây hai dấu trừ tạo ra hiệu ứng ngược lại. Tốt nhất là tránh kiểu này.
Lỗi khi làm tròn và cắt dữ liệu
Tình huống 1: Cắt sai với DATE_TRUNC()
Khi dùng DATE_TRUNC() để group dữ liệu, luôn kiểm tra xem bạn đã chọn đúng mức chưa. Ví dụ:
SELECT DATE_TRUNC('hour', NOW()); -- Cắt về đầu giờ
SELECT DATE_TRUNC('minute', NOW()); -- Cắt về đầu phút
Nếu bạn mong đợi một kết quả mà lại ra cái khác, có thể bạn đã chọn sai mức rồi.
Tình huống 2: Quên múi giờ với DATE_TRUNC()
Nếu bạn làm việc với thời gian ở nhiều múi giờ khác nhau, kết quả có thể bất ngờ lắm:
SELECT DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC');
Hãy chắc chắn bạn chỉ định đúng múi giờ, không là lạc trôi trong thời gian luôn đó (theo nghĩa đen).
Unix-time: những giây bị mất
Unix-time (EPOCH) — tiện thật nhưng cũng tricky lắm. Lỗi phổ biến nhất là nhầm giữa giây và mili giây.
SELECT TO_TIMESTAMP(1680000000); -- Đúng rồi (giây).
SELECT TO_TIMESTAMP(1680000000000); -- Sai nhé! Nhiều số 0 quá.
Kiểm tra kỹ đơn vị timestamp của bạn, kẻo lại lưu thừa cả triệu giây.
Lỗi với múi giờ
Tình huống 1: Múi giờ rối rắm
Khi bạn làm việc với user ở nhiều múi giờ khác nhau, dữ liệu có thể bị lẫn lộn. Ví dụ:
SELECT TIMESTAMP '2023-10-01 10:00:00' AT TIME ZONE 'UTC';
Hãy chắc chắn bạn hiểu rõ dữ liệu của mình đang ở múi giờ nào nhé.
Tình huống 2: Nhân đôi múi giờ
Lưu ngày giờ rồi lại cố gắng xử lý múi giờ lần nữa — không nên đâu:
SELECT TIMESTAMP '2023-10-01 10:00:00 UTC' AT TIME ZONE 'UTC'; -- Đừng làm vậy!
Cái này dễ dẫn đến tính toán sai lắm.
Khuyến nghị để tránh lỗi
Chọn đúng kiểu dữ liệu. Nếu bạn làm việc với dữ liệu thời gian quốc tế, hãy dùng TIMESTAMPTZ. Nếu chỉ cần ngày thôi thì DATE là đủ.
Test query của bạn. Đảm bảo kết quả đúng như mong đợi, nhất là khi làm với interval, định dạng hoặc làm tròn.
Lưu dữ liệu thời gian theo UTC. Đây là cách tốt nhất để tránh rối múi giờ.
Kiểm tra định dạng. Đảm bảo định dạng trong TO_CHAR() và TO_DATE() khớp với dữ liệu của bạn.
Dùng function cẩn thận. Đọc kỹ docs PostgreSQL về function thời gian để tránh bất ngờ không mong muốn.
Làm việc với dữ liệu thời gian không phải lúc nào cũng dễ, nhưng nếu chú ý và làm đúng cách thì mọi thứ sẽ mượt mà thôi. Ngày giờ là phần quan trọng của app, quên nó nguy hiểm như quên đặt báo thức sáng thứ hai vậy đó!
GO TO FULL VERSION