Ngày và giờ
Bạn sẽ học được
date,datetime,timedeltavà khi nào dùng cái nào.- Cộng trừ thời gian, tính khoảng cách giữa hai mốc.
- Chuyển chuỗi → ngày và ngày → chuỗi với
strptime/strftime. - Múi giờ: naive, aware, UTC và giờ Việt Nam.
- Các lỗi thời gian thường gặp trong dữ liệu thật.
Khái niệm cốt lõi
Module datetime có ba kiểu chính:
| Kiểu | Biểu diễn | Dùng khi |
|---|---|---|
date | Ngày, không giờ | Ngày sinh, ngày giao dịch |
datetime | Ngày + giờ | Thời điểm log, tạo đơn |
timedelta | Khoảng thời gian | Cộng trừ, tính chênh lệch |
from datetime import date, timedelta
d = date(2026, 1, 31)
print(d + timedelta(days=1))
print(d - timedelta(days=30))2026-02-01
2026-01-01Python tự xử lý số ngày trong tháng, năm nhuận, chuyển tháng — không cần tự tính. timedelta nhận days, hours, minutes, seconds, weeks.
Trừ hai ngày cho timedelta:
print(date(2026, 3, 1) - date(2026, 1, 1))59 days, 0:00:00Năm 2026 không nhuận, nên tháng 1 có 31 ngày, tháng 2 có 28: 31 + 28 = 59. Muốn lấy số ngày nguyên, dùng .days:
delta = date(2026, 3, 1) - date(2026, 1, 1)
print(delta.days)Chuỗi ↔ ngày
strptime (parse) chuyển chuỗi thành datetime theo định dạng; strftime (format) làm ngược lại:
from datetime import datetime
dt = datetime.strptime("2026-01-05 14:30", "%Y-%m-%d %H:%M")
print(dt.year, dt.month, dt.day, dt.hour, dt.minute)
print(dt.strftime("%d/%m/%Y %H:%M"))2026 1 5 14 30
05/01/2026 14:30Các mã định dạng hay dùng:
| Mã | Ý nghĩa | Ví dụ |
|---|---|---|
%Y | Năm 4 chữ số | 2026 |
%m | Tháng 2 chữ số | 01 |
%d | Ngày 2 chữ số | 05 |
%H | Giờ 24h | 14 |
%M | Phút | 30 |
%S | Giây | 00 |
%Y-%m-%d | ISO date | 2026-01-05 |
strptime rất khắt khe: chuỗi không khớp định dạng sẽ ném ValueError. Đây là lý do khi đọc ngày từ CSV, cần biết chính xác định dạng nguồn. Với dữ liệu có nhiều định dạng, thử lần lượt và bắt lỗi:
def doc_ngay(raw):
for fmt in ("%Y-%m-%d", "%d/%m/%Y"):
try:
return datetime.strptime(raw, fmt).date()
except ValueError:
continue
return None
print(doc_ngay("2026-01-05"), doc_ngay("05/01/2026"), doc_ngay("01/05/26"))2026-01-05 2026-01-05 NoneCẩn thận với 05/01/2026: theo định dạng %d/%m/%Y là 5 tháng 1; nếu dữ liệu Mỹ dùng %m/%d/%Y thì thành 1 tháng 5. Luôn xác nhận quy ước của nguồn.
Múi giờ
datetime không có tzinfo là naive — không biết múi giờ. Có tzinfo là aware. So sánh hoặc trừ naive với aware sẽ ném TypeError.
from datetime import timezone, timedelta
utc = datetime(2026, 1, 5, 14, 30, tzinfo=timezone.utc)
vn = timezone(timedelta(hours=7))
print(utc.astimezone(vn).isoformat())2026-01-05T21:30:00+07:00Cùng một thời điểm, biểu diễn theo giờ Việt Nam là 21:30. Quy tắc thực dụng:
- Lưu trữ và trao đổi: UTC (aware hoặc chuỗi ISO có offset).
- Hiển thị: chuyển sang múi giờ người dùng.
- Tính toán: dùng aware để tránh nhầm.
Lấy thời điểm hiện tại:
from datetime import datetime, timezone
now_utc = datetime.now(timezone.utc)
today = date.today()Không dùng datetime.now() trần cho dữ liệu sẽ lưu trữ — nó phụ thuộc múi giờ máy chạy và không có offset.
Làm việc với ngày trong dữ liệu
Mẫu phổ biến: nhóm doanh thu theo tháng. Chuỗi ISO "2026-01-05" có thể cắt 7 ký tự đầu, nhưng cách chắc chắn hơn là parse rồi format:
raw = "2026-01-05"
d = datetime.strptime(raw, "%Y-%m-%d").date()
print(d.strftime("%Y-%m"))2026-01Với dữ liệu lớn, parse một lần khi đọc và giữ kiểu date/datetime trong bộ nhớ, thay vì parse lại mỗi lần dùng.
Thực hành
- Cộng 1 ngày vào
2026-01-31và kiểm tra chuyển tháng. - Trừ 30 ngày, in kết quả.
- Tính số ngày giữa
2026-01-01và2026-03-01. - Parse
"2026-01-05 14:30"và format lại thành05/01/2026 14:30. - Viết hàm đọc ngày theo hai định dạng, trả
Nonenếu không khớp. - Chuyển
14:30 UTCsang giờ Việt Nam và in ISO có offset.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
ValueError khi parse | Chuỗi khớp sai định dạng | Kiểm tra %d/%m, thử nhiều format |
TypeError: can't subtract offset-naive and offset-aware | Trộn naive với aware | Chuẩn hoá về UTC aware |
| Giờ lệch 7 tiếng | Lưu giờ địa phương không offset | Lưu UTC, hiển thị theo múi giờ |
| Ngày đảo tháng/ngày | Nhầm %d/%m với %m/%d | Xác nhận quy ước nguồn |
strftime in sai | Mã định dạng sai | Đối chiếu bảng mã |
timedelta hiện 59 days, 0:00:00 | Chưa lấy .days | Dùng .days |
| So sánh ngày lỗi | Một bên là chuỗi | Parse trước khi so sánh |
Tóm tắt
datecho ngày,datetimecho thời điểm,timedeltacho khoảng.- Cộng trừ tự xử lý tháng, năm nhuận.
strptimechuỗi → ngày;strftimengày → chuỗi; sai format làValueError.- Lưu UTC, hiển thị theo múi giờ; không trộn naive với aware.
- Parse ngày ngay khi đọc dữ liệu, đừng so sánh chuỗi ngày.
Dùng ← → để chuyển bài