Tháng nhuận trong âm lịch Việt Nam: quy tắc "không trung khí" và những năm dễ tính sai
Nếu bạn từng phải hiển thị ngày âm trong một ứng dụng cho người Việt, có lẽ bạn đã gặp câu hỏi: năm nay có tháng nhuận không, nhuận tháng mấy? Đây là phần khó nhất của bài toán âm lịch, và cũng là chỗ mà copy một hàm trên mạng dễ cho ra kết quả sai nhất. Bài này tóm tắt quy tắc, cách cài đặt và vài năm nên đưa vào bộ test.
Âm lịch Việt Nam là âm dương lịch
Tháng âm bắt đầu vào ngày có điểm Sóc (trăng mới). Một tháng dài 29 hoặc 30 ngày, tùy khoảng cách giữa hai điểm Sóc liên tiếp. 12 tháng âm chỉ khoảng 354 ngày, ngắn hơn năm Mặt Trời chừng 11 ngày. Cứ khoảng 2–3 năm lại phải chèn thêm một tháng để lịch không trôi khỏi mùa. Tháng chèn thêm đó là tháng nhuận.
Quy tắc xác định tháng nhuận
- Tìm tháng 11 âm của năm trước và tháng 11 âm của năm cần tính. Tháng 11 âm là tháng chứa Đông chí.
- Đếm số lần Sóc nằm giữa hai mốc đó. Nếu có 13 tháng âm thay vì 12, năm đó có tháng nhuận.
- Tháng nhuận là tháng đầu tiên sau tháng 11 không chứa trung khí nào. Trung khí là 12 tiết khí ứng với kinh độ Mặt Trời 0°, 30°, 60°… như Xuân phân, Cốc vũ, Hạ chí.
Viết dạng giả mã:
a11 = ngay_bat_dau_thang_11(nam - 1)
b11 = ngay_bat_dau_thang_11(nam)
if (b11 - a11) > 365: # có 13 tháng âm
k = 1
while trung_khi(soc(a11, k)) == trung_khi(soc(a11, k + 1)):
k += 1 # tháng k không có trung khí mới
thang_nhuan = k - 1 # đổi về số thứ tự tháng âm
Chìa khóa là hai hàm soc() (thời điểm trăng mới) và trung_khi() (kinh độ Mặt Trời tại đầu ngày). Cả hai đều phải tính theo giờ Việt Nam, UTC+7.
Vì sao múi giờ lại quan trọng đến vậy
Điểm Sóc là một khoảnh khắc thiên văn. Nếu Sóc rơi vào 23h30 theo giờ Hà Nội thì đó đã là 0h30 ngày hôm sau theo giờ Bắc Kinh. Một tháng âm có thể bắt đầu lệch một ngày. Khi cái lệch đó rơi đúng vào tháng Giêng, Tết cũng lệch theo. Ví dụ năm 2007, Tết Nguyên Đán ở Việt Nam là 17/2/2007, còn Tết theo lịch Trung Quốc là 18/2/2007.
Vì vậy dùng nguyên một thư viện âm lịch Trung Quốc (tính theo UTC+8) cho người dùng Việt là một lỗi kinh điển. Phần lớn ngày vẫn khớp, nên lỗi này hay lọt qua khâu test.
Các năm nên đưa vào test
Mình kiểm các giá trị dưới đây bằng thư viện vnlunar (thuật toán Hồ Ngọc Đức, múi giờ 7.0):
| Năm âm | Tháng nhuận | Mùng 1 tháng nhuận (dương lịch) |
|---|---|---|
| 2025 (Ất Tỵ) | tháng 6 | 25/7/2025 |
| 2028 (Mậu Thân) | tháng 5 | 23/6/2028 |
| 2031 (Tân Hợi) | tháng 3 | 21/4/2031 |
| 2033 (Quý Sửu) | tháng 11 | 22/12/2033 |
Năm 2033 rất đáng test. Tháng nhuận rơi vào cuối năm, sát tháng 11, nên các cài đặt chỉ "đếm tháng" thay vì kiểm trung khí rất dễ sai.
Thêm vài ca biên khác:
- Năm 2026 (Bính Ngọ) có 12 tháng, và tháng Chạp chỉ có 29 ngày. Ngày cuối năm là 29 tháng Chạp (5/2/2027), mùng 1 Tết Đinh Mùi là 6/2/2027.
- Chuyển đổi ngược từ âm sang dương phải nhận thêm cờ
leap. Nếu không, "ngày 10 tháng 6 âm năm 2025" sẽ mơ hồ vì năm đó có hai tháng 6.
Gợi ý cài đặt
- Lưu ngày âm dưới dạng
(ngày, tháng, năm, nhuận), đừng ghép thành chuỗi. - Tính sẵn bảng tháng cho khoảng năm cần dùng (ví dụ 1900–2100) rồi tra bảng, thay vì tính thiên văn mỗi lần render.
- Viết test so sánh với một nguồn lịch Việt đáng tin cho ít nhất 1 ngày/tháng trong vài chục năm.
Nếu muốn đối chiếu kết quả của mình với một lịch Việt đang chạy thật, trang phương pháp của Âm Lịch Việt Nam mô tả rõ thuật toán và múi giờ họ dùng. Lưới tháng trên đó cũng đánh dấu tháng nhuận, nên tiện để kiểm tra chéo.
Bạn từng gặp bug lịch âm nào kỳ lạ? Kể ở phần bình luận nhé.
— Duc Tran













