Chuyển tới nội dung chính

🐍 Ngày 38 - Python 365 ngày: Làm việc với thời gian (datetime)

🎯 Mục tiêu​

Tìm hiểu cách sử dụng mô-đun datetime trong Python để xử lý thời gian: ngày, giờ, tính toán khoảng cách giữa hai thời điểm, v.v.


📚 Kiến thức cần biết​

Python cung cấp mô-đun datetime để làm việc với:

  • Ngày (date)
  • Thời gian (time)
  • Ngày giờ (datetime)
  • Khoảng thời gian (timedelta)

📌 Một số ví dụ quan trọng​

📅 Lấy thời gian hiện tại​

from datetime import datetime

now = datetime.now()
print("⏰ Thời gian hiện tại:", now)
print("📅 Ngày:", now.date())
print("🕒 Giờ:", now.time())

🗓️ Tạo một đối tượng datetime cụ thể​

from datetime import datetime

new_year = datetime(2025, 1, 1)
print("🎉 Năm mới:", new_year)

➖ Tính khoảng cách giữa hai thời điểm​

from datetime import datetime

today = datetime.now()
new_year = datetime(2025, 1, 1)

delta = new_year - today
print("⏳ Còn lại:", delta.days, "ngày")

🎯 Bài tập thực hành

Bài 1: Tính tuổi​

Yêu cầu:
Nhập ngày sinh của bạn và in ra tuổi hiện tại.

Gợi ý:

  • Sử dụng input() và datetime.strptime() để chuyển đổi ngày tháng nhập vào.
  • Tính hiệu giữa ngày hiện tại và ngày sinh.

Ví dụ kết quả:

from datetime import datetime, date

def tinh_tuoi(ngay_sinh_str):
ngay_sinh = datetime.strptime(ngay_sinh_str, "%d/%m/%Y").date()
hom_nay = date.today()
tuoi = hom_nay.year - ngay_sinh.year - ((hom_nay.month, hom_nay.day) < (ngay_sinh.month, ngay_sinh.day))
return tuoi

print("Tuổi của bạn là:", tinh_tuoi("10/10/2000"))

✅ Kết quả minh họa: Tuổi của bạn là: 24


Bài 2: Đếm ngược đến Tết​

Yêu cầu:
Tính số ngày còn lại đến ngày Tết Nguyên Đán 2025 (29/01/2025).

Gợi ý:

  • Sử dụng datetime.strptime() để lấy ngày Tết.
  • Tính hiệu giữa ngày hiện tại và ngày Tết.

Ví dụ kết quả:

def dem_nguoc_tet(tet_str="29/01/2025"):
ngay_tet = datetime.strptime(tet_str, "%d/%m/%Y").date()
hom_nay = date.today()
so_ngay_con_lai = (ngay_tet - hom_nay).days
return so_ngay_con_lai

print("Số ngày còn lại đến Tết: ", dem_nguoc_tet())

✅ Kết quả minh họa: Số ngày còn lại đến Tết: -80 (Tức là Tết đã qua 80 ngày tính đến hôm nay)


⏰ Bảng method datetime hay dùng​

MethodÝ nghĩaVí dụ
.now()Thời điểm hiện tạidatetime.now()
.date()Chỉ lấy phần ngày2026-10-02
.time()Chỉ lấy phần giờ14:30:15.123
.strftime(fmt)Đối tượng → chuỗi.strftime("%d/%m/%Y")
.strptime(s, fmt)Chuỗi → đối tượngdatetime.strptime("02/10/2026", "%d/%m/%Y")
.timestamp()→ số giây Unix1759400215.0
.weekday()Thứ, thứ Hai = 04 (thứ Sáu)
.isoweekday()Thứ, thứ Hai = 15
.replace(...)Tạo bản sao với phần thay đổi.replace(year=2027)

Bảng ký tự định dạng:

Ký tựÝ nghĩaVí dụ
%dNgày (01–31)02
%mTháng (01–12)10
%YNăm 4 chữ số2026
%yNăm 2 chữ số26
%HGiờ 24h14
%IGiờ 12h02
%MPhút30
%SGiây15
%pAM/PMPM
%A / %aTên thứ đầy đủ / viết tắtFriday / Fri
%B / %bTên thángOctober / Oct

⚠️ %d/%m/%Y vs %m/%d/%Y là lỗi nguy hiểm nhất khi xử lý ngày. 03/04/2026 là 3 tháng 4 ở Việt Nam, nhưng 4 tháng 3 ở Mỹ — và Python không báo lỗi vì cả hai đều hợp lệ. Luôn xác nhận định dạng của dữ liệu nguồn.

🕐 timedelta — tính toán khoảng thời gian​

from datetime import datetime, timedelta, date

hom_nay = date(2026, 10, 2)

# --- Cộng trừ ---
print("Hôm qua:", hom_nay - timedelta(days=1)) # 2026-10-01
print("Tuần sau:", hom_nay + timedelta(weeks=1)) # 2026-10-09
print("3 giờ nữa:", datetime.now() + timedelta(hours=3))

# --- Khoảng cách giữa hai ngày ---
tet_2027 = date(2027, 2, 17)
con_lai = (tet_2027 - hom_nay).days
print(f"Còn {con_lai} ngày đến Tết 2027")

# --- Phân rã khoảng thời gian ---
delta = timedelta(days=45, hours=7, minutes=20)
print(f"{delta.days} ngày, {delta.seconds // 3600} giờ")

Ứng dụng: đếm ngược thời gian​

from datetime import datetime, date


def dem_nguoc(ngay_dich: date) -> str:
"""Đếm ngược đến một ngày, xử lý cả trường hợp đã qua."""
delta = (ngay_dich - date.today()).days

if delta > 0:
return f"Còn {delta} ngày đến {ngay_dich:%d/%m/%Y}"
if delta == 0:
return f"🎉 Hôm nay là {ngay_dich:%d/%m/%Y}!"
return f"{ngay_dich:%d/%m/%Y} đã qua {-delta} ngày"


print(dem_nguoc(date(2027, 2, 17)))
print(dem_nguoc(date(2026, 1, 1)))

💡 Sửa lỗi trong code gốc của bài học: ví dụ Số ngày còn lại đến Tết cho kết quả âm vì Tết đã qua. Hàm dem_nguoc ở trên xử lý cả ba trường hợp — đó là cách viết code thực tế: luôn nghĩ đến trường hợp biên.

🌍 Múi giờ — nguồn gốc của mọi lỗi ngày tháng​

from datetime import datetime, timezone, timedelta

# ❌ datetime.now() trả về giờ MÁY, không có thông tin múi giờ
gio_may = datetime.now()
print(gio_may.tzinfo) # None — không biết múi giờ nào

# ✅ Dùng timezone-aware khi làm việc qua hệ thống
VN = timezone(timedelta(hours=7))
gio_vn = datetime.now(VN)
print(gio_vn.strftime("%d/%m/%Y %H:%M:%S %Z"))

# ✅ UTC là chuẩn khi lưu database
gio_utc = datetime.now(timezone.utc)
print(gio_utc.isoformat())

# So sánh thời gian ở hai múi giờ
tokyo = timezone(timedelta(hours=9))
print(f"VN: {datetime.now(VN):%H:%M}")
print(f"Tokyo: {datetime.now(tokyo):%H:%M}")

tuong_lai = gio_vn + timedelta(hours=2)
print(f"Sau 2 giờ: {tuong_lai:%H:%M}")
Loại datetimeCó tzinfo?Dùng khi
NaiveKhôngChỉ xử lý giờ địa phương, không so sánh
AwareCóLưu DB, API, hệ thống nhiều múi giờ

⚠️ Quy tắc vàng: lưu database bằng UTC, hiển thị bằng giờ địa phương. Nếu bạn lưu giờ địa phương, ứng dụng sẽ sai ngay khi có người dùng ở múi giờ khác hoặc khi chuyển sang giờ mùa hè.

🐼 Làm việc với ngày tháng bằng pandas​

import pandas as pd

chuoi = pd.Series(["2026-10-02", "2026-10-03", "02/10/2026", "không phải ngày"])

# errors="coerce" → giá trị không parse được thành NaT thay vì crash
gay = pd.to_datetime(chuoi, errors="coerce", dayfirst=True)
print(ngay)
print(f"Số giá trị lỗi: {ngay.isna().sum()}")

if ngay.notna().any():
print(f"Sớm nhất: {ngay.min():%d/%m/%Y}")
print(f"Muộn nhất: {ngay.max():%d/%m/%Y}")

Bốn thao tác ngày tháng trên DataFrame:

df = pd.DataFrame({
"ngay": pd.date_range("2026-01-01", periods=200, freq="D"),
"doanh_thu": range(200),
})

# 1. Lọc theo khoảng thời gian
df.loc["2026-03-01":"2026-03-31"]

# 2. Tách thành cột
for ten, gia_tri in [("nam", df["ngay"].dt.year),
("thang", df["ngay"].dt.month),
("thu", df["ngay"].dt.day_name()),
("quy", df["ngay"].dt.quarter)]:
df[ten] = gia_tri

# 3. Nhóm theo tháng
df.groupby(df["ngay"].dt.to_period("M"))["doanh_thu"].sum()

# 4. Dịch chuyển thời gian
df["doanh_thu_hom_qua"] = df["doanh_thu"].shift(1)
df["tang_truong"] = df["doanh_thu"] - df["doanh_thu"].shift(7)

print(df.head(10))

📝 Bài tập mở rộng​

Bài 1: Đếm ngày làm việc​

Đếm số ngày làm việc (thứ Hai – thứ Sáu) trong một tháng bất kỳ, không tính ngày lễ.

Bài 2: Tuổi chính xác​

Tính tuổi theo năm, tháng và ngày từ ngày sinh cho trước.

Bài 3: Lịch trực​

Cho danh sách 5 người, tạo lịch trực luân phiên mỗi ngày trong 30 ngày tới.

Bài 4: Phân tích file access log​

Đọc file log có timestamp, đếm số request theo từng giờ, tìm giờ cao điểm.

🎯 Tóm tắt ngày 38​

✅ Dùng strftime (đối tượng → chuỗi) và strptime (chuỗi → đối tượng) ✅ Nhớ bảng ký tự định dạng: %d %m %Y %H %M %S ✅ Tính toán khoảng thời gian bằng timedelta ✅ Hiểu khác biệt naive vs timezone-aware ✅ Lưu DB bằng UTC, hiển thị bằng giờ địa phương ✅ Dùng pd.to_datetime(..., errors="coerce") xử lý dữ liệu bẩn


🎥 Video minh họa​