Culture studies‎ > ‎

Reading Am lich

Âm lịch Việt Nam qua các thời kỳ lịch sử

Hồ Ngọc Đức

http://www.informatik.uni-leipzig.de/~duc/amlich/
Âm lịch dùng tại Việt Nam (mà đúng hơn phải gọi là âm-dương lịch) là loại lịch có nguồn gốc tại Trung Quốc. Các hệ thống âm lịch đều được tính dựa trên những nguyên tắc cơ bản giống nhau [2, 6]: tháng âm lịch bắt đầu vào ngày Sóc; tháng Nhuận đặt vào tháng không có Trung khí. Trong lịch sử có nhiều giai đoạn người Việt sử dụng lịch của Trung Quốc nhưng cũng có nhiều thời kỳ chúng ta tự tính âm lịch cho mình dùng. Tuy dựa vào các nguyên tắc chung nhưng vì cách áp dụng nguyên tắc khi tính lịch có thể khác nhau (chẳng hạn, xác định Sóc và Trung khí dựa trên chuyển động thực hay chuyển động trung bình; hoặc tính lịch theo các múi giờ khác nhau) nên âm lịch Việt Nam và Trung Quốc có nhiều điểm khác biệt. Khi đọc các tác phẩm lịch sử Việt Nam cổ chúng ta có thể nhận thấy nhiều khi ngày tháng ghi trong đó khác với ngày tháng trong âm lịch Trung Quốc. Nhiều tháng trong sử ta ghi là tháng nhuận nhưng trong lịch Tàu thì tháng đó không nhuận. Có tháng theo lịch ta là tháng thiếu nhưng theo lịch Tàu lại là tháng đủ và ngược lại.

Âm lịch dùng tại Việt Nam là lịch nào?

Dựa vào kết quả của một công trình nghiên cứu công phu về lịch và lịch Việt Nam của giáo sư Hoàng Xuân Hãn [2], chúng ta biết được trước năm 1945 tại Việt Nam dùng lịch nào và lịch đó khác với lịch Trung Quốc ở những giai đoạn nào.

Thời Bắc thuộc: Lịch Trung Quốc được sử dụng tại Việt Nam.

Từ nhà Ngô đến đầu nhà Lý (khoảng 939-1078): Có lẽ các vương triều đầu tiên của nước Việt Nam độc lập vẫn dùng lịch Tàu.

Nhà Lý và nhà Trần (1080-1300): Việt Nam tự tính lịch riêng (theo một phép lịch thời nhà Tống bên Trung Quốc). Có nhiều điểm khác biệt giữa lịch ta và lịch Trung Quốc trong giai đoạn này. Đáng tiếc là không có đủ tài liệu lịch sử để phục hồi lịch này.

Nhà Trần, Hồ và Lê (1306-1644): Thời kỳ này Việt Nam sử dụng lịch giống như lịch nhà Nguyên và Minh dùng tại Trung Quốc (có thể người Việt đã học được phép lịch Thụ Thời khi đi sứ nhà Nguyên khoảng 1300 và sau đó có thể tự tính lịch). Ngay cả trong thời kỳ Trịnh-Nguyễn phân tranh (1627 trở đi), các chúa Nguyễn ở miền Nam vẫn dùng lịch giống nhà Lê-Trịnh. Năn 1384 nhà Minh ở Trung Quốc đổi tên lịch Thụ Thời thành Đại Thống nhưng vẫn giữ nguyên cách tính. Cho đến hết đời Minh (1644) lịch ta và lịch Tàu không khác nhau.

Từ thời Trịnh-Nguyễn phân tranh đến đầu nhà Nguyễn (1645-1812): Việt Nam dùng lịch riêng, tính theo phép lịch Đại thống. Tại Trung Quốc, năm 1644 nhà Thanh lên đã dùng phép lịch mới (lịch Thời Hiến). Lịch ta và lịch Tàu khác nhau nhiều.

Thời Tây Sơn (1789-1801): Không rõ nhà Tây Sơn dùng lịch gì vì các văn kiện thời Tây Sơn sau bị phá hủy hết. Có lẽ Tây Sơn đã chuyển sang dùng lịch giống lịch nhà Thanh bên Trung Quốc (theo [2], tr. 949). Tại vùng chúa Nguyễn kiểm soát trong giai đoạn này vẫn sử dụng lịch của nhà Lê (tính theo phép lịch Đại Thống). Sau khi Gia Long lên ngôi vẫn duy trì lịch cũ (tên là lịch Vạn Toàn) đến 1812.

Thời nhà Nguyễn và thuộc Pháp (1813-1945): Dùng lịch Hiệp Kỷ (tính theo phép lịch Thời Hiến của nhà Thanh). Không có sự khác biệt giữa lịch Việt Nam và lịch Trung Quốc.

Giai đoạn kháng chiến chống Pháp (1946-1954): Có lẽ Việt Nam không còn cơ quan tính lịch riêng nên làm lịch theo sách Vạn niên thư của Trung Quốc. Như thế lịch ta và lịch Tàu không khác nhau.

Thời kỳ hai miền chia cắt (1955-1975): Âm lịch tại hai miền Bắc Nam có chỗ khác nhau (và khác với lịch Trung Quốc) do sử dụng các múi giờ khác nhau cho việc tính toán. Ở miền Bắc dùng múi giờ thứ 8 tới năm 1967 và múi giờ thứ 7 từ 1968 trở đi. Tại miền Nam sử dụng múi giờ thứ 8.

Từ 1976 trở đi: Cả nước Việt Nam tính lịch âm theo múi giờ thứ 7. Do khác múi giờ nên có nhiều điểm lịch ta và lịch Tàu khác nhau.

Ghi chú: Từ 1943 đến 1967 có vài lần thay đổi múi giờ chính thức, tuy nhiên có lẽ việc thay đổi múi giờ chỉ liên quan tới việc tính giờ chứ không làm ảnh hưởng tới việc tính ngày tháng âm lịch. Từ 01/01/1943 theo múi giờ thứ 8 (GMT+8, sớm hơn 1h so với giờ chuẩn). Từ 1/4/1945 theo giờ Nhật Bản dùng múi giờ thứ 9. Từ 1/4/1947 quay trở lại dùng múi giờ thứ 8. Ở miền Nam từ 1/7/1955 sử dụng múi giờ thứ 7, sau đó từ 1/1/1960 quay lại dùng múi giờ thứ 8. Ở miền Bắc thì từ 8/8/1967 trở đi dùng múi giờ thứ 7 (trước đó theo múi giờ thứ 8). Phải từ 1968 trở đi âm lịch tại miền Bắc và từ 1976 trong cả nước mới được tính dựa theo múi giờ chuẩn của Việt Nam.

Chương trình tính âm lịch VNCal

Chương trình tính âm lịch VNCal mà tôi thiết kế (http://www.informatik.uni-leipzig.de/~duc/amlich/) có chức năng tính lịch pháp định ("Official calendar"). Đây là lịch được chính quyền đương thời phát hành và cho sử dụng. Lịch này cũng có thể gọi là lịch chính thức, lịch hành chính hay lịch lịch sử.
  • Từ 900 đến 1300, chương trình hiển thị ngày tháng âm lịch của lịch Trung Quốc [5]. Những dữ liệu này thật ra không hoàn toàn phù hợp với lịch Việt Nam trong khoảng 1080-1300 như ta đã biết, nhưng vì không có đủ tài liệu để tái tạo lịch nên chưa thể khắc phục được.
  • Từ 1301 đến 1945 âm lịch pháp định của Việt Nam được tính dựa theo nghiên cứu của Hoàng Xuân Hãn [2]. Trong khoảng này có một thời gian dài (1645-1812) lịch Việt Nam và lịch Trung Quốc khác nhau.
  • Từ 1946 trở đi lịch được tính theo các qui tắc hiện đại, áp dụng cho múi giờ chính thức được dùng tại Việt Nam. (Cách tính lịch được miêu tả cụ thể tại [6]).
  • Trong những thời kỳ mà ở hai miền dùng hai loại âm lịch khác nhau (Tây Sơn-Nguyễn 1789-1802 và Bắc-Nam 1968-1975) thì chương trình hiển thị ngày tháng của cả 2 lịch chính thức nếu như những ngày tháng này khác nhau. Trong giai đoạn 1955-1975, ngày âm theo lịch miền Bắc được hiển thị ở góc phải bên dưới (nơi bình thường vẫn in ngày âm lịch) và ngày tháng âm theo lịch miền Nam được in trong ngoặc vuông ở góc phải bên trên (VD: [28/1]). Trong giai đoạn 1789-1802, ngày tháng theo lịch Trung Quốc (mà có thể là nhà Tây Sơn dùng -- điều này còn là nghi vấn) được hiển thị trong ngoặc vuông ở góc phải bên trên.

Tài liệu tham khảo

[1] La Sơn Yên Hồ Hoàng Xuân Hãn, Tập I, NXB Giáo Dục, Hà Nội, 1998

[2] Hoàng Xuân Hãn, Lịch và lịch Việt Nam, trong [1] trang 851--

[3] Bách trúng kinh (bản in, lưu tại thư viện Viện Hán Nôm, Hà Nội). Có lịch Lê - Trịnh từ năm 1624 đến năm 1785. (Trong [2], tr. 940, Hoàng Xuân Hãn nhắc tới việc năm 1945 ông "... thấy một sách viết cũ, mang tên Bách-trúng-kinh, có chép tháng đủ thiếu và tháng nhuận của những năm từ Lê Thần-tông Vĩnh-tộ thứ 6 (1624) đến Tây-sơn Cảnh-thịnh thứ 7 (1799)..." Phải chăng hai bản Bách trúng kinh này khác nhau?)

[4] Khâm định vạn niên thư (bản in, lưu tại Thư viện Quốc gia, Hà Nội). In lịch Lê - Trịnh từ năm 1554 đến năm 1630, lịch của Chúa Nguyễn Đàng trong từ năm 1631 đến năm 1801 và lịch nhà Nguyễn từ năm 1802 đến năm 1903

[5] http://www.sinica.edu.tw/~tdbproj/sinocal/luso.html (Âm lịch dùng tại Trung Quốc qua các thời đại)

[6] Hồ Ngọc Đức, Thuật toán tính âm lịch (http://www.informatik.uni-leipzig.de/~duc/amlich/calrules.html)

Cách xác định 24 tiết khí

Tiết khí là các thời điểm mà kinh độ mặt trời (KĐMT) có các giá trị 0°, 15°, 30°, 45°, 60°, ..., 345°. (0° là Xuân Phân, 15° là Thanh Minh v.v.). Như vậy để xác định tiết khí ta cần tìm xem vào khoảng thời gian nào thì kinh độ mặt trời có các giá trị này.

Tìm ngày chứa tiết khí

Thường thì ta chỉ quan tâm tới tiết khí rơi vào ngày nào chứ không cần chính xác tới giờ/phút. Ngày chứa một tiết khí nhất định có thể được xác định như sau:

  • Chọn một ngày có khả năng chứa tiết khí cần xác định. Ngày có tiết khí chỉ xê dịch trong khoảng 1-2 ngày nên ta có thể chọn khá sát.
  • Tính kinh độ mặt trời lúc 0h sáng ngày hôm đó và 0h sáng ngày hôm sau
  • Nếu kinh độ mặt trời tương ứng với tiết khí cần xác định nằm giữa hai giá trị này thì ngày đã chọn chính là ngày chứa tiết khí, nếu không ta lặp lại việc tìm kiếm này với ngày trước hoặc sau đó.

Tìm thời điểm tiết khí

Để tìm thời điểm chính xác của một tiết khí, sau khi xác định được ngày chứa tiết khí đó ta có thể thực hiện một phép tìm kiếm nhị phân đơn giản để tìm ra ngày giờ của tiết khí này.

  • Chọn mốc trên và dưới là 0h và 24h (tức 0h sáng ngày hôm sau). Tính điểm giữa 2 mốc (12h trưa) và tính KĐMT tại điểm đó.
  • Nếu KĐMT này nhỏ hơn KĐMT của tiết khí, tìm tiếp trong khoảng từ 0h đến 12h, nếu không sẽ tìm trong khoảng từ 12h đến 24h.
  • Lặp lại việc tìm kiếm đến khi KĐMT của hai điểm mốc cách nhau không quá 0.001 độ.

Bước tính toán quan trọng nhất trong việc xác định tiết khí là tìm kinh độ mặt trời tại một thời điểm bất kỳ. Việc tính toán này được thực hiện với 2 bước:

  • Tính niên kỷ Julius của thời điểm đã cho
  • Tính kinh độ mặt trời cho thời điểm đó

Ngày và niên kỷ Julius

Số ngày Julius (Julian Day Number) của một ngày trong lịch Gregory có thể tính bởi các công thức sau, sử dụng năm thiên văn (1 TCN là 0, 2 TCN là −1, 4713 TCN là −4712):

    a = [(14 - tháng)/ 12]
    y = năm + 4800 - a
    m= tháng + 12a - 3
    JDN = ngày + [(153m + 2)/5] + 365y + [y/4] - [y/100] + [y/400] - 32045

Trong các công thức trên [x/y] là phần nguyên của phép chia x/y.

Để tính niên kỷ Julius (Julian date), thêm giờ, phút, giây theo UT (Universal Time):

    JD = JDN + (giờ - 12)/24 + phút/1440 + giây/86400

Nếu giờ, phút, giây được tính theo giờ Hà Nội (UTC+7:00) thì kết quả phải trừ đi 7/24 ngày.

Tính kinh độ mặt trời tại một thời điểm

Để tính kinh độ mặt trời tại thời điểm, trước hết tìm niên kỷ Julius JD của thời điểm đó theo phương pháp trên. Sau đó thực hiện các bước sau:

T = (JD - 2451545.0) / 36525
L0 = 280°.46645 + 36000°.76983*T + 0°.0003032*T2
M = 357°.52910 + 35999°.05030*T - 0°.0001559*T2 - 0°.00000048*T3
C = (1°.914600 - 0°.004817*T - 0°.000014*T2) * sin M + (0°.01993 - 0°.000101*T) * sin 2M + 0°.000290 * sin 3M
theta = L0 + C
lambda = theta - 0.00569 - 0.00478 * sin(125°.04 - 1934°.136*T)
lambda = lambda - 360 * [lambda/360]

Kết quả lambda là kinh độ mặt trời cần tìm. Đó là một góc (tính bằng độ) trong khoảng (0,360).

Ví dụ

Chọn ngày giờ (giờ Hà Nội, UTC+7:00) và nhấn OK để tính kinh độ mặt trời tại thời điểm đó:

      Ngày:    tháng       năm       

Kết quả:

Tìm ngày Đông Chí năm 2008. Kinh độ mặt trời ứng với Đông Chí là 270°. Ngày Đông Chí thường rơi vào khoảng 20/12-22/12 hàng năm. Như vậy trước hết ta thử ngày 20/12/2008. KĐMT lúc 0h sáng ngày 20/12/2008 là 268°.17811 và lúc 0h sáng 21/12/2008 là 269°.19634. Góc 270° nằm sau cả hai giá trị này, như vậy ta phải thử ngày hôm sau. KĐMT lúc 0h sáng ngày 22/12/2008 là 270°.21471, như thế điểm Đông Chí nằm trong ngày 21/12/2008.

Để xác định thời điểm Đông Chí, ta tính KĐMT lúc 12h ngày 21/12/2008, được kết quả 269°.70551, nhỏ hơn 270°, như vậy điểm Đông Chí nằm trong khoảng từ 12h đến 24h. Chọn 18h00 ngày 21/12/2008 sẽ tìm thấy KĐMT 269°.96010, như vậy ta phải tìm tiếp trong khoảng 18h đến 24h. Vào lúc 21h, KĐMT là 270°.08741, như thế khoảng tìm kiếm bây giờ là 18h đến 21h. Lặp lại việc tìm kiếm này thêm khoảng 7 bước nữa sẽ tìm được thời điểm Đông Chí là 18h56. (Kết quả 'chính xác' tính theo lý thuyết VSOP87 là 19h04).

Thuật toán tính âm lịch

Hồ Ngọc Đức

Bài viết sau giới thiệu cách tính âm lịch Việt Nam và mô tả một số thuật toán dùng để chuyển đổi giữa ngày dương lịch và ngày âm lịch. Các thuật toán mô tả ở đây đã được đơn giản hóa nhiều để bạn đọc tiện theo dõi và dễ dàng sử dụng vào việc lập trình, do đó độ chính xác của chúng thấp hơn độ chính xác của chương trình âm lịch trực tuyến tại http://www.informatik.uni-leipzig.de/~duc/amlich/. (Một phiên bản cũ của bài viết này giới thiệu vài thuật toán hơi khác, có thể khó thực hiện hơn một chút. Bản cũ này có thể xem tại đây.)

[If you cannot read Vietnamese: Old version in English]

Quy luật của âm lịch Việt Nam

Âm lịch Việt Nam là một loại lịch thiên văn. Nó được tính toán dựa trên sự chuyển động của mặt trời, trái đất và mặt trăng. Ngày tháng âm lịch được tính dựa theo các nguyên tắc sau:

  1. Ngày đầu tiên của tháng âm lịch là ngày chứa điểm Sóc
  2. Một năm bình thường có 12 tháng âm lịch, một năm nhuận có 13 tháng âm lịch
  3. Đông chí luôn rơi vào tháng 11 âm lịch
  4. Trong một năm nhuận, nếu có 1 tháng không có Trung khí thì tháng đó là tháng nhuận. Nếu nhiều tháng trong năm nhuận đều không có Trung khí thì chỉ tháng đầu tiên sau Đông chí là tháng nhuận
  5. Việc tính toán dựa trên kinh tuyến 105° đông.

Sóc là thời điểm hội diện, đó là khi trái đất, mặt trăng và mặt trời nằm trên một đường thẳng và mặt trăng nằm giữa trái đất và mặt trời. (Như thế góc giữa mặt trăng và mặt trời bằng 0 độ). Gọi là "hội diện" vì mặt trăng và mặt trời ở cùng một hướng đối với trái đất. Chu kỳ của điểm Sóc là khoảng 29,5 ngày. Ngày chứa điểm Sóc được gọi là ngày Sóc, và đó là ngày bắt đầu tháng âm lịch.

Trung khí là các điểm chia đường hoàng đạo thành 12 phần bằng nhau. Trong đó, bốn Trung khí giữa bốn mùa là đặc biệt nhất: Xuân phân (khoảng 20/3), Hạ chí (khoảng 22/6), Thu phân (khoảng 23/9) và Đông chí (khoảng 22/12).

Bởi vì dựa trên cả mặt trời và mặt trăng nên lịch Việt Nam không phải là thuần âm lịch mà là âm-dương-lịch. Theo các nguyên tắc trên, để tính ngày tháng âm lịch cho một năm bất kỳ trước hết chúng ta cần xác định những ngày nào trong năm chứa các thời điểm Sóc (New moon) . Một khi bạn đã tính được ngày Sóc, bạn đã biết được ngày bắt đầu và kết thúc của một tháng âm lịch: ngày mùng một của tháng âm lịch là ngày chứa điểm sóc. Sau khi đã biết ngày bắt đầu/kết thúc các tháng âm lịch, ta tính xem các Trung khí (Major solar term) rơi vào tháng nào để từ đó xác định tên các tháng và tìm tháng nhuận.

Đông chí luôn rơi vào tháng 11 của năm âm lịch. Bởi vậy chúng ta cần tính 2 điểm sóc: Sóc A ngay trước ngày Đông chí thứ nhất và Sóc B ngay trước ngày Đông chí thứ hai. Nếu khoảng cách giữa A và B là dưới 365 ngày thì năm âm lịch có 12 tháng, và những tháng đó có tên là: tháng 11, tháng 12, tháng 1, tháng 2, …, tháng 10. Ngược lại, nếu khoảng cách giữa hai sóc A và B là trên 365 ngày thì năm âm lịch này là năm nhuận, và chúng ta cần tìm xem đâu là tháng nhuận. Để làm việc này ta xem xét tất cả các tháng giữa A và B, tháng đầu tiên không chứa Trung khí sau ngày Đông chí thứ nhất là tháng nhuận. Tháng đó sẽ được mang tên của tháng trước nó kèm chữ "nhuận".

Khi tính ngày Sóc và ngày chứa Trung khí bạn cần lưu ý xem xét chính xác múi giờ. Đây là lý do tại sao có một vài điểm khác nhau giữa lịch Việt Nam và lịch Trung Quốc.Ví dụ, nếu bạn biết thời điểm hội diện là vào lúc yyyy-02-18 16:24:45 GMT thì ngày Sóc của lịch Việt Nam là 18 tháng 2, bởi vì 16:24:45 GMT là 23:24:45 cùng ngày, giờ Hà nội (GMT+7, kinh tuyến 105° đông). Tuy nhiên theo giờ Bắc Kinh (GMT+8, kinh tuyến 120° đông) thì Sóc là lúc 00:24:45 ngày yyyy-02-19, do đó tháng âm lịch của Trung Quốc lại bắt đầu ngày yyyy-02-19, chậm hơn lịch Việt Nam 1 ngày.

Ví dụ 1: Âm lịch năm 1984

Chúng ta áp dụng quy luật trên để tính âm lịch Việt nam năm 1984.

  • Sóc A (ngay trước Đông chí năm 1983) rơi vào ngày 4/12/1983, Sóc B (ngay trước Đông chí năm 1984) vào ngày 23/11/1984.
  • Giữa A và B là khoảng 355 ngày, như thế năm âm lịch 1984 là năm thường. Tháng 11 âm lịch của năm trước kéo dài từ 4/12/1983 đến 2/01/1984, tháng 12 âm từ 3/1/1984 đến 1/2/1984, tháng Giêng từ 2/2/1984 đến 1/3/1984 v.v.

Ví dụ 2: Âm lịch năm 2004

  • Sóc A - điểm sóc cuối cùng trước Đông chí 2003 - rơi vào ngày 23/11/2003. Sóc B (ngay trước Đông chí năm 2004) rơi vào ngày 12/12/2004.
  • Giữa 2 ngày này là khoảng 385 ngày, như vậy năm âm lịch 2004 là năm nhuận. Tháng 11 âm của năm 2003 bắt đầu vào ngày chứa Sóc A, tức ngày 23/11/2003.
  • Tháng âm lịch đầu tiên sau đó mà không chứa Trung khí là tháng từ 21/3/2004 đến 18/4/2004 (Xuân phân rơi vào 20/3/2004, còn Cốc vũ là 19/4/2004). Như thế tháng ấy là tháng nhuận.
  • Từ 23/11/2003 đến 21/3/2004 là khoảng 120 ngày, tức 4 tháng âm lịch: tháng 11, 12, 1 và 2. Như vậy năm 2004 có tháng 2 nhuận.

Thuật toán chuyển đổi giữa ngày dương và âm

Trong tính toán thiên văn người ta lấy ngày 1/1/4713 trước công nguyên của lịch Julius (tức ngày 24/11/4714 trước CN theo lịch Gregorius) làm điểm gốc. Số ngày tính từ điểm gốc này gọi là số ngày Julius (Julian day number) của một thời điểm. Ví dụ, số ngày Julius của 1/1/2000 là 24515455.

Dùng các công thức sau ta có thể chuyển đổi giữa ngày/tháng/năm và số ngày Julius. Phép chia ở 2 công thức sau được hiểu là chia số nguyên, bỏ phần dư: 23/4=5.

Đổi ngày dd/mm/yyyy ra số ngày Julius jd

a = (14 - mm) / 12
y = yy+4800-a
m = mm+12*a-3

Lịch Gregory:

jd = dd + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045

Lịch Julius:

jd = dd + (153*m+2)/5 + 365*y + y/4 - 32083

Đổi số ngày Julius jd ra ngày dd/mm/yyyy

Lịch Gregory (jd lớn hơn 2299160):

a = jd + 32044;
b = (4*a+3)/146097;
c = a - (b*146097)/4;

Lịch Julius:

b = 0;
c = jd + 32082;

Công thức cho cả 2 loại lịch:

d = (4*c+3)/1461;
e = c - (1461*d)/4;
m = (5*e+2)/153;
dd = e - (153*m+2)/5 + 1;
mm = m + 3 - 12*(m/10);
yy = b*100 + d - 4800 + m/10;

Nếu ngôn ngữ lập trình bạn dùng không hỗ trợ phép chia số nguyên bỏ phần dư (VD: JavaScript), bạn có thể định nghĩa một hàm INT(x) để lấy số nguyên lớn nhất không vượt quá x: INT(5)=5, INT(3.2)=3, INT(-5)=-5, INT(-3.2)=-4 v.v. Khi đó, INT(m/10) sẽ trả lại kết quả của phép chia số nguyên. (Nhiều ngôn ngữ có sẵn hàm floor() cho phép làm việc này.)

Các phép chuyển đổi giữa ngày tháng và số ngày Julius có thể được thực hiện với mã JavaScript như sau:

function jdFromDate(dd, mm, yy)

var a, y, m, jd;
a = INT((14 - mm) / 12);
y = yy+4800-a;
m = mm+12*a-3;
jd = dd + INT((153*m+2)/5) + 365*y + INT(y/4) - INT(y/100) + INT(y/400) - 32045;
if (jd < 2299161) {
	jd = dd + INT((153*m+2)/5) + 365*y + INT(y/4) - 32083;
}
return jd;

function jdToDate(jd)

var a, b, c, d, e, m, day, month, year;
if (jd > 2299160) { // After 5/10/1582, Gregorian calendar
	a = jd + 32044;
	b = INT((4*a+3)/146097);
	c = a - INT((b*146097)/4);
} else {
	b = 0;
	c = jd + 32082;
}
d = INT((4*c+3)/1461);
e = c - INT((1461*d)/4);
m = INT((5*e+2)/153);
day = e - INT((153*m+2)/5) + 1;
month = m + 3 - 12*INT(m/10);
year = b*100 + d - 4800 + INT(m/10);
return new Array(day, month, year);

Trong các công thức sau, timeZone là số giờ chênh lệch giữa giờ địa phương và giờ UTC (hay GMT). (Để tính lịch Việt Nam, lấy timeZone = 7.0). Các phương pháp sau được giới thiệu với mã JavaScript. Bạn có thể tải thư viện JavaScript hoặc thư viện PHP hoàn chỉnh để tham khảo.

Tính ngày Sóc

Như trên đã nói, để tính được âm lịch trước hết ta cần xác định các tháng âm lịch bắt đầu vào ngày nào.

Thuật toán sau tính ngày Sóc thứ k kể từ điểm Sóc ngày 1/1/1900. Kết quả trả về là số ngày Julius của ngày Sóc cần tìm.

function getNewMoonDay(k, timeZone)

var T, T2, T3, dr, Jd1, M, Mpr, F, C1, deltat, JdNew;
T = k/1236.85; // Time in Julian centuries from 1900 January 0.5
T2 = T * T;
T3 = T2 * T;
dr = PI/180;
Jd1 = 2415020.75933 + 29.53058868*k + 0.0001178*T2 - 0.000000155*T3;
Jd1 = Jd1 + 0.00033*Math.sin((166.56 + 132.87*T - 0.009173*T2)*dr); // Mean new moon
M = 359.2242 + 29.10535608*k - 0.0000333*T2 - 0.00000347*T3; // Sun's mean anomaly
Mpr = 306.0253 + 385.81691806*k + 0.0107306*T2 + 0.00001236*T3; // Moon's mean anomaly
F = 21.2964 + 390.67050646*k - 0.0016528*T2 - 0.00000239*T3; // Moon's argument of latitude
C1=(0.1734 - 0.000393*T)*Math.sin(M*dr) + 0.0021*Math.sin(2*dr*M);
C1 = C1 - 0.4068*Math.sin(Mpr*dr) + 0.0161*Math.sin(dr*2*Mpr);
C1 = C1 - 0.0004*Math.sin(dr*3*Mpr);
C1 = C1 + 0.0104*Math.sin(dr*2*F) - 0.0051*Math.sin(dr*(M+Mpr));
C1 = C1 - 0.0074*Math.sin(dr*(M-Mpr)) + 0.0004*Math.sin(dr*(2*F+M));
C1 = C1 - 0.0004*Math.sin(dr*(2*F-M)) - 0.0006*Math.sin(dr*(2*F+Mpr));
C1 = C1 + 0.0010*Math.sin(dr*(2*F-Mpr)) + 0.0005*Math.sin(dr*(2*Mpr+M));
if (T < -11) {
	deltat= 0.001 + 0.000839*T + 0.0002261*T2 - 0.00000845*T3 - 0.000000081*T*T3;
} else {
	deltat= -0.000278 + 0.000265*T + 0.000262*T2;
};
JdNew = Jd1 + C1 - deltat;
return INT(JdNew + 0.5 + timeZone/24)

Với hàm này ta có thể tính được tháng âm lịch chứa ngày N bắt đầu vào ngày nào: giữa ngày 1/1/1900 (số ngày Julius: 2415021) và ngày N có khoảng k=INT((N-2415021)/29.530588853) tháng âm lịch, như thế dùng hàm getNewMoonDay sẽ biết ngày đầu tháng âm lịch chứa ngày N, từ đó ta biết ngày N là mùng mấy âm lịch.

Tính tọa độ mặt trời

Để biết Trung khí nào nằm trong tháng âm lịch nào, ta chỉ cần tính xem mặt trời nằm ở khoảng nào trên đường hoàng đạo vào thời điểm bắt đầu một tháng âm lịch. Ta chia đường hoàng đạo làm 12 phần và đánh số các cung này từ 0 đến 11: từ Xuân phân đến Cốc vũ là 0; từ Cốc vũ đến Tiểu mãn là 1; từ Tiểu mãn đến Hạ chí là 2; v.v.. Cho jdn là số ngày Julius của bất kỳ một ngày, phương pháp sau này sẽ trả lại số cung nói trên.

function getSunLongitude(jdn, timeZone)

var T, T2, dr, M, L0, DL, L;
T = (jdn - 2451545.5 - timeZone/24) / 36525; // Time in Julian centuries from 2000-01-01 12:00:00 GMT
T2 = T*T;
dr = PI/180; // degree to radian
M = 357.52910 + 35999.05030*T - 0.0001559*T2 - 0.00000048*T*T2; // mean anomaly, degree
L0 = 280.46645 + 36000.76983*T + 0.0003032*T2; // mean longitude, degree
DL = (1.914600 - 0.004817*T - 0.000014*T2)*Math.sin(dr*M);
DL = DL + (0.019993 - 0.000101*T)*Math.sin(dr*2*M) + 0.000290*Math.sin(dr*3*M);
L = L0 + DL; // true longitude, degree
L = L*dr;
L = L - PI*2*(INT(L/(PI*2))); // Normalize to (0, 2*PI)
return INT(L / PI * 6)

Với hàm này ta biết được một tháng âm lịch chứa Trung khí nào. Giả sử một tháng âm lịch bắt đầu vào ngày N1 và tháng sau đó bắt đầu vào ngày N2 và hàm getSunLongitude cho kết quả là 8 với N1 và 9 với N2. Như vậy tháng âm lịch bắt đầu ngày N1 là tháng chứa Đông chí: trong khoảng từ N1 đến N2 có một ngày mặt trời di chuyển từ cung 8 (sau Tiểu tuyết) sang cung 9 (sau Đông chí). Nếu hàm getSunLongitude trả lại cùng một kết quả cho cả ngày bắt đầu một tháng âm lịch và ngày bắt đầu tháng sau đó thì tháng đó không có Trung khí và như vậy có thể là tháng nhuận.

Tìm ngày bắt đầu tháng 11 âm lịch

Đông chí thường nằm vào khoảng 19/12-22/12, như vậy trước hết ta tìm ngày Sóc trước ngày 31/12. Nếu tháng bắt đầu vào ngày đó không chứa Đông chí thì ta phải lùi lại 1 tháng nữa.

function getLunarMonth11(yy, timeZone)

var k, off, nm, sunLong;
off = jdFromDate(31, 12, yy) - 2415021;
k = INT(off / 29.530588853);
nm = getNewMoonDay(k, timeZone);
sunLong = getSunLongitude(nm, timeZone); // sun longitude at local midnight
if (sunLong >= 9) {
	nm = getNewMoonDay(k-1, timeZone);
}
return nm;

Xác định tháng nhuận

Nếu giữa hai tháng 11 âm lịch (tức tháng có chứa Đông chí) có 13 tháng âm lịch thì năm âm lịch đó có tháng nhuận. Để xác định tháng nhuận, ta sử dụng hàm getSunLongitude như đã nói ở trên. Cho a11 là ngày bắt đầu tháng 11 âm lịch mà một trong 13 tháng sau đó là tháng nhuận. Hàm sau cho biết tháng nhuận nằm ở vị trí nào sau tháng 11 này.

function getLeapMonthOffset(a11, timeZone)

var k, last, arc, i;
k = INT((a11 - 2415021.076998695) / 29.530588853 + 0.5);
last = 0;
i = 1; // We start with the month following lunar month 11
arc = getSunLongitude(getNewMoonDay(k+i, timeZone), timeZone);
do {
	last = arc;
	i++;
	arc = getSunLongitude(getNewMoonDay(k+i, timeZone), timeZone);
} while (arc != last && i < 14);
return i-1;

Giả sử hàm getLeapMonthOffset trả lại giá trị 4, như thế tháng nhuận sẽ là tháng sau tháng 2 thường. (Tháng thứ 4 sau tháng 11 đáng ra là tháng 3, nhưng vì đó là tháng nhuận nên sẽ lấy tên của tháng trước đó tức tháng 2, và tháng thứ 5 sau tháng 11 mới là tháng 3).

Đổi ngày dương dd/mm/yyyy ra ngày âm

Với các phương pháp hỗ trợ trên ta có thể đổi ngày dương dd/mm/yy ra ngày âm dễ dàng. Trước hết ta xem ngày monthStart bắt đầu tháng âm lịch chứa ngày này là ngày nào (dùng hàm getNewMoonDay như trên đã nói). Sau đó, ta tìm các ngày a11 và b11 là ngày bắt đầu các tháng 11 âm lịch trước và sau ngày đang xem xét. Nếu hai ngày này cách nhau dưới 365 ngày thì ta chỉ còn cần xem monthStart và a11 cách nhau bao nhiêu tháng là có thể tính được dd/mm/yy nằm trong tháng mấy âm lịch. Ngược lại, nếu a11 và b11 cách nhau khoảng 13 tháng âm lịch thì ta phải tìm xem tháng nào là tháng nhuận và từ đó suy ra ngày đang tìm nằm trong tháng nào.

function convertSolar2Lunar(dd, mm, yy, timeZone)

var k, dayNumber, monthStart, a11, b11, lunarDay, lunarMonth, lunarYear, lunarLeap;
dayNumber = jdFromDate(dd, mm, yy);
k = INT((dayNumber - 2415021.076998695) / 29.530588853);
monthStart = getNewMoonDay(k+1, timeZone);
if (monthStart > dayNumber) {
	monthStart = getNewMoonDay(k, timeZone);
}
a11 = getLunarMonth11(yy, timeZone);
b11 = a11;
if (a11 >= monthStart) {
	lunarYear = yy;
	a11 = getLunarMonth11(yy-1, timeZone);
} else {
	lunarYear = yy+1;
	b11 = getLunarMonth11(yy+1, timeZone);
}
lunarDay = dayNumber-monthStart+1;
diff = INT((monthStart - a11)/29);
lunarLeap = 0;
lunarMonth = diff+11;
if (b11 - a11 > 365) {
	leapMonthDiff = getLeapMonthOffset(a11, timeZone);
	if (diff >= leapMonthDiff) {
		lunarMonth = diff + 10;
		if (diff == leapMonthDiff) {
			lunarLeap = 1;
		}
	}
}
if (lunarMonth > 12) {
	lunarMonth = lunarMonth - 12;
}
if (lunarMonth >= 11 && diff < 4) {
	lunarYear -= 1;
}

Đổi âm lịch ra dương lịch

Cách làm cũng tương tự như đổi ngày dương sang ngày âm.

function convertLunar2Solar(lunarDay, lunarMonth, lunarYear, lunarLeap, timeZone)

var k, a11, b11, off, leapOff, leapMonth, monthStart;
if (lunarMonth < 11) {
	a11 = getLunarMonth11(lunarYear-1, timeZone);
	b11 = getLunarMonth11(lunarYear, timeZone);
} else {
	a11 = getLunarMonth11(lunarYear, timeZone);
	b11 = getLunarMonth11(lunarYear+1, timeZone);
}
off = lunarMonth - 11;
if (off < 0) {
	off += 12;
}
if (b11 - a11 > 365) {
	leapOff = getLeapMonthOffset(a11, timeZone);
	leapMonth = leapOff - 2;
	if (leapMonth < 0) {
		leapMonth += 12;
	}
	if (lunarLeap != 0 && lunarMonth != leapMonth) {
		return new Array(0, 0, 0);
	} else if (lunarLeap != 0 || off >= leapOff) {
		off += 1;
	}
}
k = INT(0.5 + (a11 - 2415021.076998695) / 29.530588853);
monthStart = getNewMoonDay(k+off, timeZone);
return jdToDate(monthStart+lunarDay-1);

Tính ngày thứ và Can-Chi cho ngày và tháng âm lịch

Ngày thứ lặp lại theo chu kỳ 7 ngày, như thế để biết một ngày d/m/y bất kỳ là thứ mấy ta chỉ việc tìm số dư của số ngày Julius của ngày này cho 7.

Để tính Can của năm Y, tìm số dư của Y+6 chia cho 10. Số dư 0 là Giáp, 1 là Ất v.v. Để tính Chi của năm, chia Y+8 cho 12. Số dư 0 là Tý, 1 là Sửu, 2 là Dần v.v.

Hiệu Can-Chi của ngày lặp lại theo chu kỳ 60 ngày, như thế nó cũng có thể tính được một cách đơn giản. Cho N là số ngày Julius của ngày dd/mm/yyyy. Ta chia N+9 cho 10. Số dư 0 là Giáp, 1 là Ất v.v. Để tìm Chi, chia N+1 cho 12; số dư 0 là Tý, 1 là Sửu v.v.

Trong một năm âm lịch, tháng 11 là tháng Tý, tháng 12 là Sửu, tháng Giêng là tháng Dần v.v. Can của tháng M năm Y âm lịch được tính theo công thức sau: chia Y*12+M+3 cho 10. Số dư 0 là Giáp, 1 là Ất v.v.

Ví dụ, Can-Chi của tháng 3 âm lịch năm Giáp Thân 2004 là Mậu Thìn: tháng 3 âm lịch là tháng Thìn, và (2004*12+3+3) % 10 = 24054 % 10 = 4, như vậy Can của tháng là Mậu.

Một tháng nhuận không có tên riêng mà lấy tên của tháng trước đó kèm thêm chữ "Nhuận", VD: tháng 2 nhuận năm Giáp Thân 2004 là tháng Đinh Mão nhuận.

Tài liệu tham khảo

Comments