Thuật toán tính âm lịch Việt Nam không phải một công thức đơn giản, mà là một chuỗi bước dựa trên thiên văn: tìm các thời điểm Sóc và Đông chí theo giờ Việt Nam, xác định tháng 11, kiểm tra năm có tháng nhuận hay không, rồi đánh số các tháng. Có thể hiểu gọn là lịch âm được "đo" từ bầu trời chứ không được "chia đều" như dương lịch.
Bài này viết cho cả người đọc muốn hiểu nguyên lý lẫn lập trình viên muốn tự cài đặt. Nguyên lý được giải thích bằng một ví dụ cụ thể. Phần kỹ thuật có bộ dữ liệu kiểm thử và đoạn mã mẫu đã được chạy thử. Chúng tôi cũng so sánh ba cách tính độc lập để trả lời một câu hỏi thực tế: các thuật toán âm lịch khác nhau ở đâu và mức độ ra sao? Nếu bạn cần phần nền tảng, hãy đọc trước bài Lịch Âm Dương Việt Nam: Nguyên lý, lịch sử và cách tính.
Vì sao không thể tính âm lịch bằng cách chia đều 29,53 ngày?
Đây là cách làm mà nhiều người nghĩ đến đầu tiên: lấy một ngày mùng 1 đã biết rồi cộng dần 29,53 ngày. Nhưng tháng trăng thực tế không đều, vì quỹ đạo Mặt Trăng và Trái Đất đều không tròn. Tính cho giai đoạn 1900–2100, độ dài tháng thay đổi từ 29,27 đến 29,83 ngày, và thời điểm Sóc thực có thể lệch khỏi thời điểm trung bình tới khoảng 14 giờ.
Chúng tôi đã thử cách cộng đều này trên 2.488 tháng. Kết quả là cách này cho sai ngày mùng 1 ở 36% số tháng, và kể cả khi đã căn chỉnh mốc cho tốt nhất thì vẫn sai 28%. Đó là lý do mọi thuật toán nghiêm túc đều phải tính vị trí thật của Mặt Trăng và Mặt Trời.
Năm bước đổi dương lịch sang âm lịch
Hãy theo dõi các bước qua một ví dụ: đổi ngày 5/11/2026 sang âm lịch.
- Tìm hai ngày Đông chí bao quanh ngày cần đổi. Với ví dụ này là 21/12/2025 và 22/12/2026, theo giờ Việt Nam.
- Tìm hai tháng 11. Tháng 11 là tháng chứa Đông chí, nên nó bắt đầu từ ngày Sóc cuối cùng không muộn hơn Đông chí. Ở đây là 20/12/2025 và 9/12/2026.
- Liệt kê các ngày Sóc giữa hai mốc đó. Mỗi ngày Sóc là một ngày mùng 1: 20/12, 19/1, 17/2, 19/3, 17/4, 17/5, 15/6, 14/7, 13/8, 11/9, 10/10, 9/11.
- Kiểm tra tháng nhuận. Giữa hai tháng 11 có 12 tháng nên năm này không nhuận. Nếu có 13 tháng, tháng nhuận sẽ là tháng đầu tiên không chứa trung khí. Bạn có thể xem cách làm trong bài Tháng nhuận âm lịch là gì?
- Đánh số tháng và đổi ngày. Bắt đầu từ tháng 11, các tháng lần lượt là 11, 12, 1, 2 và tiếp tục. Tháng bắt đầu từ 10/10/2026 là tháng 9. Ngày 5/11 cách 10/10 là 26 ngày, nên kết quả là ngày 27 tháng 9 âm lịch.
Về nguyên tắc chỉ có vậy. Phần khó nằm ở chỗ tính chính xác thời điểm Sóc và vị trí Mặt Trời, và luôn phải quy về giờ Việt Nam trước khi lấy ra ngày.
Thời điểm Sóc được tính thế nào?
Có hai hướng chính. Hướng thứ nhất dùng công thức thiên văn rút gọn: lấy thời điểm Sóc trung bình rồi cộng thêm các số hạng hiệu chỉnh theo vị trí Mặt Trời và Mặt Trăng. Thuật toán của Hồ Ngọc Đức, được cộng đồng lập trình Việt Nam dùng rộng rãi, đi theo hướng này. Ưu điểm của nó là rất gọn và không cần thư viện ngoài. Hướng thứ hai dùng thư viện thiên văn chính xác hơn, đổi lại phần cài đặt nặng hơn.
Câu hỏi thực tế là hai hướng này chênh nhau bao nhiêu. Chúng tôi đã tính 2.488 thời điểm Sóc từ 1900 đến 2100 bằng ba phương pháp độc lập, lấy thư viện ephem làm mốc so sánh:
| Phương pháp | Chênh lệch trung bình | Chênh lệch lớn nhất |
|---|---|---|
| Astronomy Engine | 0,7 phút | 1,2 phút |
| Thuật toán Hồ Ngọc Đức | 1,1 phút | 3,9 phút |
Chênh lệch chỉ khoảng một phút, và không có nghĩa bên nào đúng tuyệt đối. Nhưng lịch chỉ quan tâm Sóc thuộc ngày nào, nên một phút thường không ảnh hưởng gì. Trong giai đoạn 1968–2099, thuật toán Hồ Ngọc Đức và cách tính dùng ephem cho kết quả giống nhau ở 1.630 trên 1.632 tháng, kể cả tháng nhuận. Đây là một xác nhận rất tốt cho độ tin cậy của thuật toán này.
Ba tháng tương lai mà các thuật toán không thống nhất
Sai lệch chỉ xảy ra khi Sóc rơi sát nửa đêm. Trong giai đoạn trên, ba phương pháp không thống nhất ở ba tháng sau:
| Thời điểm Sóc (giờ Việt Nam) | Theo ephem | Theo Hồ Ngọc Đức | Theo Astronomy Engine |
|---|---|---|---|
| Tháng 12/2072, khoảng 23:58 | 9/12 | 10/12 | 9/12 |
| Tháng 11/2077, khoảng 23:59 | 15/11 | 16/11 | 16/11 |
| Tháng 10/2085, khoảng 23:59 | 18/10 | 18/10 | 19/10 |
Nói cách khác, với những tháng này, không thuật toán nào có thể khẳng định chắc chắn. Càng xa hiện tại, sai số càng lớn do vận tốc quay của Trái Đất không dự báo chính xác được. Các tháng này còn cách chúng ta khá xa, nên không ảnh hưởng đến việc dùng lịch hiện nay. Đây là lý do vì sao không có cuốn lịch nào thực sự "vạn niên", như chúng tôi đã phân tích ở bài Lịch vạn niên có đáng tin không?
Bộ dữ liệu kiểm thử cho lập trình viên
Nếu bạn tự cài đặt hoặc chọn một thư viện âm lịch, hãy thử với các ngày dưới đây. Chúng được chọn vì đều là những trường hợp dễ sai.
| Dương lịch | Âm lịch đúng | Phát hiện lỗi gì |
|---|---|---|
| 17/2/2007 | 1/1 | Dùng giờ Trung Quốc sẽ ra 30/12 |
| 2/2/2030 | 1/1 | Dùng giờ Trung Quốc sẽ ra 30/12 |
| 18/2/2053 | 1/1 | Dùng giờ Trung Quốc sẽ ra 30/12 |
| 21/1/1985 | 1/1 | Năm Tết lệch một tháng do Đông chí |
| 21/3/1985 | 1/2 nhuận | Tháng nhuận khác Trung Quốc |
| 25/7/2025 | 1/6 nhuận | Nhận diện tháng nhuận gần đây |
| 23/6/2028 | 1/5 nhuận | Nhận diện tháng nhuận sắp tới |
| 21/4/2031 | 1/3 nhuận | Sóc lúc 23:56, kiểm tra biên ngày |
| 23/9/2033 | 1/9 | Tháng không trung khí nhưng không nhuận |
| 22/12/2033 | 1/11 nhuận | Trường hợp nhuận tháng 11 hiếm gặp |
| 20/1/2034 | 1/12 | Tháng ngay sau nhuận tháng 11 |
| 26/9/2034 | 15/8 | Trung thu lệch với giờ Trung Quốc |
Những lỗi thường gặp khi cài đặt thuật toán âm lịch
- Dùng sai múi giờ. Đây là lỗi phổ biến nhất. Nếu lấy thư viện âm lịch Trung Quốc, các ngày lệch sẽ sai âm thầm, vì phần lớn thời gian kết quả vẫn trùng. Bài Vì sao Tết Việt Nam có năm không trùng Tết Trung Quốc? có danh sách các ngày này.
- Quy đổi múi giờ sai thứ tự. Phải đổi thời điểm Sóc sang giờ Việt Nam rồi mới lấy ngày, không được làm tròn ngày theo UTC trước.
- Coi mọi tháng thiếu trung khí là tháng nhuận. Trường hợp 23/9/2033 trong bảng trên cho thấy cách này sẽ sai, vì phải đếm đủ 13 tháng trước.
- Quên xử lý tháng nhuận khi đánh số. Tháng nhuận mang số của tháng trước nó, nhiều chương trình lại đánh số tiếp.
- Áp công thức hiện đại cho năm trước 1968. Kết quả có thể khác lịch đã dùng ở thời điểm đó, nhất là với miền Nam trước năm 1975.
- Lẫn lộn tháng âm và tháng tiết khí. Khi tính Can Chi hay Tứ Trụ, cần xác định rõ đang dùng loại tháng nào.
Mã mẫu Python để đổi dương lịch sang âm lịch
Đoạn mã dưới đây thực hiện đúng năm bước đã nêu, dùng thư viện ephem nên ngắn gọn và dễ đọc. Cài đặt bằng lệnh pip install ephem. Mã được ưu tiên dễ hiểu, không tối ưu về tốc độ, nên nếu dùng trong sản phẩm bạn cần thêm bộ nhớ đệm theo năm.
import math
from datetime import date, timedelta
import ephem
TZ = 7 # Giờ Việt Nam
def local_day(t):
return (ephem.Date(t).datetime() + timedelta(hours=TZ)).date()
def term(d):
# Số thứ tự cung 30 độ của Mặt Trời lúc 0h ngày d (giờ địa phương)
t = ephem.Date(f"{d.year}/{d.month}/{d.day}") - TZ / 24
s = ephem.Sun(t)
return int(math.degrees(ephem.Ecliptic(s, epoch=t).lon) // 30)
def month_11(year):
# Ngày bắt đầu tháng chứa Đông chí của năm dương lịch year
solstice = local_day(ephem.next_solstice(f"{year}/12/1"))
t, last = ephem.Date(f"{year}/10/15"), None
while True:
t = ephem.next_new_moon(t)
d = local_day(t)
if d > solstice:
return last
last, t = d, ephem.Date(t + 1)
def lunar_months(year):
# Các tháng từ tháng 11 năm year đến hết tháng 10 năm sau
start, end = month_11(year), month_11(year + 1)
days = [start]
t = ephem.Date(f"{start.year}/{start.month}/{start.day}") + 1
while days[-1] < end:
t = ephem.next_new_moon(t)
days.append(local_day(t))
t = ephem.Date(t + 1)
leap = None
if len(days) - 1 == 13:
for i in range(1, 13):
if term(days[i]) == term(days[i + 1]):
leap = i
break
months, num = [], 10
for i, d in enumerate(days[:-1]):
if i == leap:
months.append((d, num, True))
else:
num = num % 12 + 1
months.append((d, num, False))
return months
def solar_to_lunar(d):
# Trả về (ngày âm, tháng âm, có phải tháng nhuận hay không)
for y in (d.year, d.year - 1):
months = lunar_months(y)
if months[0][0] <= d:
for s, m, leap in reversed(months):
if s <= d:
return (d - s).days + 1, m, leap
print(solar_to_lunar(date(2026, 11, 5))) # (27, 9, False)
Chúng tôi đã chạy mã này cho toàn bộ giai đoạn 1968–2099 và đối chiếu với một bản lịch dựng riêng: 1.632 tháng đều khớp. Tất cả 12 ngày trong bộ kiểm thử trên cũng cho kết quả đúng. Lưu ý rằng hai bản này cùng dựa trên ephem, nên phép so sánh độc lập thực sự là với thuật toán Hồ Ngọc Đức đã nêu ở trên.
Câu hỏi thường gặp về thuật toán âm lịch
Có công thức nào đổi dương lịch sang âm lịch chỉ bằng một dòng không?
Không có công thức ngắn cho ngày âm chính xác, vì độ dài tháng phụ thuộc vị trí thực của Mặt Trăng và Mặt Trời. Một số app dùng bảng dữ liệu tính sẵn để tra nhanh, nhưng dữ liệu đó vẫn phải được tính bằng thuật toán.
Có thể tính âm lịch trong Excel không?
Khó. Excel phù hợp hơn với Can Chi của ngày, vốn chỉ là phép đếm. Với ngày âm, nên dùng thư viện hoặc bảng tra đã tính sẵn.
Thuật toán Hồ Ngọc Đức có chính xác không?
Có, ở mức đủ tốt cho lịch. So với ephem, chênh lệch trung bình khoảng 1 phút và giống nhau ở 1.630 trên 1.632 tháng trong giai đoạn 1968–2099.
Thuật toán âm lịch có dùng cho năm xa trong tương lai được không?
Được nhưng độ tin cậy giảm dần, đặc biệt ở những tháng có Sóc sát nửa đêm.
Lịch Việt Nam và lịch Trung Quốc dùng chung thuật toán không?
Dùng chung quy tắc, chỉ khác múi giờ. Chỉ cần đổi hằng số múi giờ trong mã mẫu là có thể tính theo giờ khác.
Đọc thêm trong chùm bài về lịch âm dương
Bạn có thể đọc bài trụ Lịch Âm Dương Việt Nam: Nguyên lý, lịch sử và cách tính, bài Tháng nhuận âm lịch là gì?, bài Can Chi là gì? và bài Lịch vạn niên có đáng tin không?
Phương pháp so sánh và nguồn tham khảo
Thời điểm Sóc từ 1900 đến 2100 được tính bằng ba cách: thư viện ephem, thư viện Astronomy Engine và bản cài đặt lại thuật toán Hồ Ngọc Đức bằng Python. Việc lấy ephem làm mốc chỉ để so sánh, không có nghĩa ephem là chuẩn tuyệt đối. Các kết quả tương lai là tính toán theo quy tắc hiện hành, không phải lịch đã ban hành.
Bài viết được biên soạn với sự hỗ trợ của AI, theo định hướng và hậu kiểm của ban biên tập Reviews365. Nếu phát hiện sai sót, xin hãy phản hồi để chúng tôi tính lại.
Để lại bình luận
5