@lichta/core 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants/heavenly-stems.ts","../src/constants/earthly-branches.ts","../src/core/lunar.ts","../src/constants/five-elements.ts","../src/core/feng-shui.ts","../src/utils/format.ts","../src/constants/i18n.ts"],"sourcesContent":["// ============================================================\n// Lichta Core — Public API (Root Entry Point)\n// ============================================================\n\n// Xuất bản phần lõi TypeScript (Toán học lịch)\nexport { LichTa, jdFromDate, jdToDate } from './core/lunar.js';\nexport type { LunarDate, SolarDate } from './core/types.js';\n\n// Xuất bản phần phong thủy\nexport {\n getYearDetails,\n getDayCanChi,\n getMonthCanChi,\n getHourCanChi,\n getAuspiciousHours,\n} from './core/feng-shui.js';\n\n// Xuất bản phần format/tiện ích\nexport { formatLunarDate, formatTraditional, getMonthName, getDayName } from './utils/format.js';\n\n// Xuất bản phần i18n\nexport { t, getZodiacAnimal } from './constants/i18n.js';\nexport type { Locale } from './constants/i18n.js';\n","/**\n * Heavenly Stems (Thiên Can)\n * Giáp (Dương) | Wood\n * Ất (Âm) | Wood\n * Bính (Dương) | Fire\n * Đinh (Âm) | Fire\n * Mậu (Dương) | Earth\n * Kỷ (Âm) | Earth\n * Canh (Dương) | Metal\n * Tân (Âm) | Metal\n * Nhâm (Dương) | Water\n * Quý (Âm) | Water\n */\nexport const HEAVENLY_STEMS = [\n 'Giáp', 'Ất', 'Bính', 'Đinh', 'Mậu',\n 'Kỷ', 'Canh', 'Tân', 'Nhâm', 'Quý'\n] as const;\n\n/**\n * Tính trọng số của Thiên Can (dùng để tính Ngũ Hành)\n * Index: 0, 1 -> 1 | 2, 3 -> 2 | ... | 8, 9 -> 5\n */\nexport function getStemWeight(index: number): number {\n return Math.floor(index / 2) + 1;\n}","/**\n * Earthly Branches (Địa Chi - 12 con giáp)\n * Tý | Rat (Chuột)\n * Sửu | Ox (Trâu)\n * Dần | Tiger (Cọp)\n * Mão | Rabbit (Thỏ)\n * Thìn | Dragon (Rồng)\n * Tỵ | Snake (Rắn)\n * Ngọ | Horse (Ngựa)\n * Mùi | Goat (Dê)\n * Thân | Monkey (Khỉ)\n * Dậu | Rooster (Gà)\n * Tuất | Dog (Chó)\n * Hợi | Pig (Heo)\n */\nexport const EARTHLY_BRANCHES = [\n 'Tý', 'Sửu', 'Dần', 'Mão', 'Thìn', 'Tỵ',\n 'Ngọ', 'Mùi', 'Thân', 'Dậu', 'Tuất', 'Hợi'\n] as const;\n\n/**\n * Tính trọng số của Địa Chi (dùng để tính Ngũ Hành)\n * Chu kỳ lặp lại sau mỗi 6 Chi (index % 6)\n */\nexport function getBranchWeight(index: number): number {\n const normalizedIndex = index % 6;\n return Math.floor(normalizedIndex / 2);\n}\n\n/**\n * Tính Dương - Âm (Yin - Yang) cho Địa Chi\n * Index: 0 -> 1 (Dương) | 1 -> 0 (Âm) | ...\n */\nexport function getBranchYinYang(index: number): 1 | 0 {\n return (index % 2 === 0) ? 1 : 0;\n}\n\n/**\n * Tính Ngũ Hành cho Địa Chi\n * Tý: 1 (Thủy Dương), Sửu: 12 (Thổ Âm), Dần: 3 (Mộc Dương), ...\n */\nexport function getBranchElement(index: number): number {\n const weight = Math.floor(index / 2) + 1;\n return weight === 5 ? 1 : weight;\n}","import type { LunarDate, SolarDate } from './types.js';\nimport { HEAVENLY_STEMS } from '../constants/heavenly-stems.js';\nimport { EARTHLY_BRANCHES } from '../constants/earthly-branches.js';\n\n/**\n * Thuật toán chuyển đổi Dương lịch ↔ Âm lịch Việt Nam\n * Dựa trên công trình của Hồ Ngọc Đức (Đại học Leipzig)\n * Tham khảo: Jean Meeus, \"Astronomical Algorithms\" (1998)\n *\n * Copyright (c) 2006 Ho Ngoc Duc. All Rights Reserved.\n * TypeScript adaptation for Lichta library.\n */\n\nconst PI = Math.PI;\n\nfunction INT(d: number): number {\n return Math.floor(d);\n}\n\n// ============================================================\n// Julian Day Number\n// ============================================================\n\n/**\n * Chuyển ngày Gregorian sang Julian Day Number.\n *\n * @param dd - Ngày (1-31)\n * @param mm - Tháng (1-12)\n * @param yy - Năm\n */\nexport function jdFromDate(dd: number, mm: number, yy: number): number {\n const a = INT((14 - mm) / 12);\n const y = yy + 4800 - a;\n const m = mm + 12 * a - 3;\n let jd = dd + INT((153 * m + 2) / 5) + 365 * y + INT(y / 4) - INT(y / 100) + INT(y / 400) - 32045;\n if (jd < 2299161) {\n jd = dd + INT((153 * m + 2) / 5) + 365 * y + INT(y / 4) - 32083;\n }\n return jd;\n}\n\n/**\n * Chuyển Julian Day Number sang ngày Gregorian.\n *\n * @param jd - Julian Day Number\n * @returns Tuple [day, month, year]\n */\nexport function jdToDate(jd: number): [number, number, number] {\n let a: number, b: number, c: number;\n if (jd > 2299160) {\n a = jd + 32044;\n b = INT((4 * a + 3) / 146097);\n c = a - INT((b * 146097) / 4);\n } else {\n b = 0;\n c = jd + 32082;\n }\n const d = INT((4 * c + 3) / 1461);\n const e = c - INT((1461 * d) / 4);\n const m = INT((5 * e + 2) / 153);\n const day = e - INT((153 * m + 2) / 5) + 1;\n const month = m + 3 - 12 * INT(m / 10);\n const year = b * 100 + d - 4800 + INT(m / 10);\n return [day, month, year];\n}\n\n// ============================================================\n// Điểm Sóc (New Moon) — Thuật toán Hồ Ngọc Đức / Jean Meeus\n// ============================================================\n\n/**\n * Tính thời điểm điểm Sóc (New Moon) thứ k\n * (đo bằng số ngày Julian kể từ 1/1/4713 BC trưa UTC).\n *\n * k=0 tương ứng New Moon ngày 1/1/1900 13:52 UTC.\n *\n * @param k - Số thứ tự New Moon\n */\nfunction NewMoon(k: number): number {\n const T = k / 1236.85;\n const T2 = T * T;\n const T3 = T2 * T;\n const dr = PI / 180;\n\n let Jd1 = 2415020.75933 + 29.53058868 * k + 0.0001178 * T2 - 0.000000155 * T3;\n Jd1 = Jd1 + 0.00033 * Math.sin((166.56 + 132.87 * T - 0.009173 * T2) * dr);\n\n // Mean anomaly của Mặt Trời\n const M = 359.2242 + 29.10535608 * k - 0.0000333 * T2 - 0.00000347 * T3;\n // Mean anomaly của Mặt Trăng\n const Mpr = 306.0253 + 385.81691806 * k + 0.0107306 * T2 + 0.00001236 * T3;\n // Argument of latitude của Mặt Trăng\n const F = 21.2964 + 390.67050646 * k - 0.0016528 * T2 - 0.00000239 * T3;\n\n // Hiệu chỉnh\n let C1 = (0.1734 - 0.000393 * T) * Math.sin(M * dr) + 0.0021 * Math.sin(2 * dr * M);\n C1 = C1 - 0.4068 * Math.sin(Mpr * dr) + 0.0161 * Math.sin(dr * 2 * Mpr);\n C1 = C1 - 0.0004 * Math.sin(dr * 3 * Mpr);\n C1 = C1 + 0.0104 * Math.sin(dr * 2 * F) - 0.0051 * Math.sin(dr * (M + Mpr));\n C1 = C1 - 0.0074 * Math.sin(dr * (M - Mpr)) + 0.0004 * Math.sin(dr * (2 * F + M));\n C1 = C1 - 0.0004 * Math.sin(dr * (2 * F - M)) - 0.0006 * Math.sin(dr * (2 * F + Mpr));\n C1 = C1 + 0.001 * Math.sin(dr * (2 * F - Mpr)) + 0.0005 * Math.sin(dr * (2 * Mpr + M));\n\n const JdNew = Jd1 + C1;\n return JdNew;\n}\n\n/**\n * Tính ngày Julian (integer) của điểm Sóc thứ k theo múi giờ.\n *\n * @param k - Số thứ tự New Moon\n * @param timeZone - Múi giờ (7 = GMT+7)\n */\nexport function getNewMoonDay(k: number, timeZone: number): number {\n return INT(NewMoon(k) + 0.5 + timeZone / 24);\n}\n\n// ============================================================\n// Kinh độ Mặt Trời (Sun Longitude)\n// ============================================================\n\n/**\n * Tính kinh độ Mặt Trời tại ngày Julian cho trước.\n * Trả về index 0-11 (mỗi 30°), dùng để xác định Trung khí.\n *\n * Index 9 = Đông Chí (270°-300°)\n *\n * @param jdn - Julian Day Number\n * @param timeZone - Múi giờ\n */\nexport function getSunLongitude(jdn: number, timeZone: number): number {\n const T = (jdn - 2451545.5 - timeZone / 24) / 36525;\n const T2 = T * T;\n const dr = PI / 180;\n\n // Mean longitude\n const M = 357.5291 + 35999.0503 * T - 0.0001559 * T2 - 0.00000048 * T * T2;\n\n // Mean longitude\n const L0 = 280.46645 + 36000.76983 * T + 0.0003032 * T2;\n\n let DL = (1.9146 - 0.004817 * T - 0.000014 * T2) * Math.sin(dr * M);\n DL = DL + (0.019993 - 0.000101 * T) * Math.sin(dr * 2 * M) + 0.00029 * Math.sin(dr * 3 * M);\n\n let L = L0 + DL;\n // Chuẩn hóa\n L = L - 360 * INT(L / 360);\n return INT(L / 30);\n}\n\n// ============================================================\n// Tháng 11 âm lịch & Tháng nhuận\n// ============================================================\n\n/**\n * Tìm JDN của mùng 1 tháng 11 âm lịch (tháng chứa Đông Chí).\n */\nfunction getLunarMonth11(yy: number, timeZone: number): number {\n const off = jdFromDate(31, 12, yy) - 2415021.076998695;\n const k = INT(off / 29.530588853);\n let nm = getNewMoonDay(k, timeZone);\n const sunLong = getSunLongitude(nm, timeZone);\n if (sunLong >= 9) {\n nm = getNewMoonDay(k - 1, timeZone);\n }\n return nm;\n}\n\n/**\n * Tìm offset tháng nhuận (tháng đầu tiên không chứa Trung khí\n * giữa 2 tháng 11 âm lịch liên tiếp).\n */\nfunction getLeapMonthOffset(a11: number, timeZone: number): number {\n const k = INT((a11 - 2415021.076998695) / 29.530588853 + 0.5);\n let last: number;\n let i = 1;\n let arc = getSunLongitude(getNewMoonDay(k + i, timeZone), timeZone);\n last = arc;\n while (i < 14) {\n i++;\n arc = getSunLongitude(getNewMoonDay(k + i, timeZone), timeZone);\n if (arc !== last) {\n last = arc;\n } else {\n // 2 tháng liên tiếp cùng Trung khí → tháng trước đó là tháng nhuận\n break;\n }\n }\n return i - 1;\n}\n\n// ============================================================\n// Can Chi helpers\n// ============================================================\n\nfunction getYearCanChi(lunarYear: number): string {\n const stemIndex = (lunarYear + 6) % 10;\n const branchIndex = (lunarYear + 8) % 12;\n return `${HEAVENLY_STEMS[stemIndex]} ${EARTHLY_BRANCHES[branchIndex]}`;\n}\n\nfunction getMonthCanChiInternal(lunarMonth: number, lunarYear: number): string {\n const yearStemIndex = (lunarYear + 6) % 10;\n const monthStemOffset = (yearStemIndex % 5) * 2 + 2;\n const monthStemIndex = (monthStemOffset + lunarMonth - 1) % 10;\n const monthBranchIndex = (lunarMonth + 1) % 12;\n return `${HEAVENLY_STEMS[monthStemIndex]} ${EARTHLY_BRANCHES[monthBranchIndex]}`;\n}\n\nfunction getDayCanChiInternal(jd: number): string {\n const stemIndex = (jd + 9) % 10;\n const branchIndex = (jd + 1) % 12;\n return `${HEAVENLY_STEMS[stemIndex]} ${EARTHLY_BRANCHES[branchIndex]}`;\n}\n\n// ============================================================\n// LichTa — Public API\n// ============================================================\n\nexport class LichTa {\n /**\n * Chuyển đổi ngày Dương lịch sang Âm lịch.\n *\n * Sử dụng thuật toán Hồ Ngọc Đức để tìm Điểm Sóc và Tiết Khí,\n * từ đó xác định ngày, tháng, năm âm lịch tương ứng.\n *\n * @param day - Ngày dương lịch (1-31)\n * @param month - Tháng dương lịch (1-12)\n * @param year - Năm dương lịch (1800-2199)\n * @param timeZone - Múi giờ (mặc định: 7 cho GMT+7 Việt Nam)\n * @returns Đối tượng LunarDate chứa thông tin ngày âm lịch\n * @throws {RangeError} Khi input ngoài phạm vi hợp lệ\n *\n * @example\n * ```typescript\n * const lunar = LichTa.toLunar(10, 2, 2024);\n * // → { day: 1, month: 1, year: 2024, isLeap: false, yearCanChi: 'Giáp Thìn' }\n * ```\n */\n static toLunar(day: number, month: number, year: number, timeZone: number = 7): LunarDate {\n if (year < 1800 || year > 2199) {\n throw new RangeError(`Lichta: Year must be between 1800 and 2199, got ${year}`);\n }\n if (month < 1 || month > 12) {\n throw new RangeError(`Lichta: Month must be between 1 and 12, got ${month}`);\n }\n if (day < 1 || day > 31) {\n throw new RangeError(`Lichta: Day must be between 1 and 31, got ${day}`);\n }\n\n const dayNumber = jdFromDate(day, month, year);\n const k = INT((dayNumber - 2415021.076998695) / 29.530588853);\n\n let monthStart = getNewMoonDay(k + 1, timeZone);\n if (monthStart > dayNumber) {\n monthStart = getNewMoonDay(k, timeZone);\n }\n\n let a11 = getLunarMonth11(year, timeZone);\n let b11 = a11;\n let lunarYear: number;\n if (a11 >= monthStart) {\n lunarYear = year;\n a11 = getLunarMonth11(year - 1, timeZone);\n } else {\n lunarYear = year + 1;\n b11 = getLunarMonth11(year + 1, timeZone);\n }\n\n const lunarDay = dayNumber - monthStart + 1;\n const diff = INT((monthStart - a11) / 29);\n\n // Kiểm tra tháng nhuận: một năm âm lịch nhuận có 13 tháng (~384 ngày)\n // thay vì 12 tháng (~354 ngày) giữa 2 mốc tháng 11.\n let lunarLeap = false;\n let lunarMonth = diff + 11;\n if (b11 - a11 > 365) {\n const leapMonthDiff = getLeapMonthOffset(a11, timeZone);\n if (diff >= leapMonthDiff) {\n lunarMonth = diff + 10;\n if (diff === leapMonthDiff) {\n lunarLeap = true;\n }\n }\n }\n if (lunarMonth > 12) {\n lunarMonth -= 12;\n }\n if (lunarMonth >= 11 && diff < 4) {\n lunarYear -= 1;\n }\n\n const yearCanChi = getYearCanChi(lunarYear);\n const monthCanChi = getMonthCanChiInternal(lunarMonth, lunarYear);\n const dayCanChi = getDayCanChiInternal(dayNumber);\n\n return {\n day: lunarDay,\n month: lunarMonth,\n year: lunarYear,\n isLeap: lunarLeap,\n jd: dayNumber,\n dayCanChi,\n monthCanChi,\n yearCanChi,\n };\n }\n\n /**\n * Chuyển đổi ngày Âm lịch sang Dương lịch.\n *\n * @param lunarDay - Ngày âm lịch\n * @param lunarMonth - Tháng âm lịch\n * @param lunarYear - Năm âm lịch\n * @param isLeap - Có phải tháng nhuận không\n * @param timeZone - Múi giờ (mặc định: 7 cho GMT+7 Việt Nam)\n * @returns Đối tượng SolarDate\n * @throws {RangeError} Khi năm ngoài phạm vi 1800-2199\n *\n * @example\n * ```typescript\n * const solar = LichTa.toSolar(1, 1, 2024, false);\n * // → { day: 10, month: 2, year: 2024 }\n * ```\n */\n static toSolar(\n lunarDay: number,\n lunarMonth: number,\n lunarYear: number,\n isLeap: boolean = false,\n timeZone: number = 7\n ): SolarDate {\n if (lunarYear < 1800 || lunarYear > 2199) {\n throw new RangeError(`Lichta: Year must be between 1800 and 2199, got ${lunarYear}`);\n }\n\n let a11: number, b11: number;\n\n if (lunarMonth < 11) {\n a11 = getLunarMonth11(lunarYear - 1, timeZone);\n b11 = getLunarMonth11(lunarYear, timeZone);\n } else {\n a11 = getLunarMonth11(lunarYear, timeZone);\n b11 = getLunarMonth11(lunarYear + 1, timeZone);\n }\n\n const k = INT(0.5 + (a11 - 2415021.076998695) / 29.530588853);\n let off = lunarMonth - 11;\n if (off < 0) {\n off += 12;\n }\n\n // Xử lý tháng nhuận: nếu năm có tháng nhuận (13 tháng, ~384 ngày giữa\n // 2 mốc tháng 11), cần dịch offset thêm 1 cho tháng nhuận và các tháng\n // sau nó, để phân biệt với tháng thường cùng số.\n if (b11 - a11 > 365) {\n const leapOff = getLeapMonthOffset(a11, timeZone);\n let leapMonth = leapOff - 2;\n if (leapMonth < 0) leapMonth += 12;\n\n if (isLeap && lunarMonth !== leapMonth) {\n throw new RangeError(\n `Lichta: Month ${lunarMonth} is not a leap month in lunar year ${lunarYear}`\n );\n } else if (isLeap || off >= leapOff) {\n off += 1;\n }\n }\n\n const nm = getNewMoonDay(k + off, timeZone);\n const jd = nm + lunarDay - 1;\n const [day, month, year] = jdToDate(jd);\n\n return { day, month, year };\n }\n}","/**\n * Five Elements (Ngũ Hành)\n * Kim | Metal\n * Mộc | Wood\n * Thủy | Water\n * Hỏa | Fire\n * Thổ | Earth\n */\nexport const FIVE_ELEMENTS = ['Kim', 'Mộc', 'Thủy', 'Hỏa', 'Thổ'];","import { HEAVENLY_STEMS, getStemWeight } from '../constants/heavenly-stems.js';\nimport { EARTHLY_BRANCHES, getBranchWeight } from '../constants/earthly-branches.js';\nimport { FIVE_ELEMENTS } from '../constants/five-elements.js';\n\n/**\n * Lấy ra chuỗi mô tả toàn bộ Can, Chi và Mệnh dựa vào số năm (Ví dụ: 2024)\n * Theo quy ước: Năm 4 (sau CN) là năm Giáp Tý (Can index 0, Chi index 0)\n */\nexport function getYearDetails(year: number) {\n // Tính index của Can và Chi dựa trên mốc năm gốc\n // Năm 1984, 2044 là Giáp Tý -> Offset để tính toán dễ nhất là lấy (year - 4) % hệ cơ số\n const stemIndex = (year - 4) % 10;\n const branchIndex = (year - 4) % 12;\n\n // Công thức vàng tính Ngũ Hành\n const stemWeight = getStemWeight(stemIndex);\n const branchWeight = getBranchWeight(branchIndex);\n \n let elementValue = stemWeight + branchWeight;\n if (elementValue > 5) {\n elementValue -= 5;\n }\n\n // Value ra từ 1-5, nên trừ 1 để lấy index của mảng FIVE_ELEMENTS (0-4)\n const elementIndex = elementValue - 1;\n\n return {\n can: HEAVENLY_STEMS[stemIndex],\n chi: EARTHLY_BRANCHES[branchIndex],\n menh: FIVE_ELEMENTS[elementIndex],\n fullString: `${HEAVENLY_STEMS[stemIndex]} ${EARTHLY_BRANCHES[branchIndex]} - Mệnh ${FIVE_ELEMENTS[elementIndex]}`\n };\n}\n\n// ============================================================\n// Can Chi cho Ngày, Tháng, Giờ\n// ============================================================\n\n/**\n * Tính Can Chi ngày từ Julian Day Number.\n *\n * @param jd - Julian Day Number\n * @returns Chuỗi Can Chi (ví dụ: \"Giáp Tý\")\n */\nexport function getDayCanChi(jd: number): string {\n const stemIndex = (jd + 9) % 10;\n const branchIndex = (jd + 1) % 12;\n return `${HEAVENLY_STEMS[stemIndex]} ${EARTHLY_BRANCHES[branchIndex]}`;\n}\n\n/**\n * Tính Can Chi tháng từ tháng và năm âm lịch.\n * Can tháng phụ thuộc vào Can của năm (quy tắc Ngũ Hổ Độn):\n * - Năm Giáp/Kỷ → Tháng Giêng bắt đầu bằng Bính (index 2)\n * - Năm Ất/Canh → Tháng Giêng bắt đầu bằng Mậu (index 4)\n * - Năm Bính/Tân → Tháng Giêng bắt đầu bằng Canh (index 6)\n * - Năm Đinh/Nhâm → Tháng Giêng bắt đầu bằng Nhâm (index 8)\n * - Năm Mậu/Quý → Tháng Giêng bắt đầu bằng Giáp (index 0)\n *\n * @param lunarMonth - Tháng âm lịch (1-12)\n * @param lunarYear - Năm âm lịch\n */\nexport function getMonthCanChi(lunarMonth: number, lunarYear: number): string {\n const yearStemIndex = (lunarYear + 6) % 10;\n const monthStemOffset = (yearStemIndex % 5) * 2 + 2;\n const monthStemIndex = (monthStemOffset + lunarMonth - 1) % 10;\n const monthBranchIndex = (lunarMonth + 1) % 12;\n return `${HEAVENLY_STEMS[monthStemIndex]} ${EARTHLY_BRANCHES[monthBranchIndex]}`;\n}\n\n/**\n * Tính Can Chi giờ từ giờ (0-23) và Julian Day Number của ngày.\n *\n * 12 canh giờ (mỗi canh = 2 tiếng):\n * Tý: 23h-01h, Sửu: 01h-03h, Dần: 03h-05h, ...\n *\n * Can giờ Tý phụ thuộc Can ngày (quy tắc Ngũ Thử Độn):\n * - Ngày Giáp/Kỷ → Giờ Tý bắt đầu bằng Giáp\n * - Ngày Ất/Canh → Giờ Tý bắt đầu bằng Bính\n * - Ngày Bính/Tân → Giờ Tý bắt đầu bằng Mậu\n * - Ngày Đinh/Nhâm → Giờ Tý bắt đầu bằng Canh\n * - Ngày Mậu/Quý → Giờ Tý bắt đầu bằng Nhâm\n *\n * @param hour - Giờ (0-23)\n * @param dayJd - Julian Day Number của ngày\n */\nexport function getHourCanChi(hour: number, dayJd: number): string {\n // Tính Địa Chi của giờ: Tý từ 23h, mỗi canh 2 tiếng\n const hourBranchIndex = Math.floor((hour + 1) / 2) % 12;\n\n // Tính Thiên Can của giờ dựa vào Can ngày\n const dayStemIndex = (dayJd + 9) % 10;\n const hourStemOffset = (dayStemIndex % 5) * 2;\n const hourStemIndex = (hourStemOffset + hourBranchIndex) % 10;\n\n return `${HEAVENLY_STEMS[hourStemIndex]} ${EARTHLY_BRANCHES[hourBranchIndex]}`;\n}\n\n// ============================================================\n// Giờ Hoàng Đạo\n// ============================================================\n\n/**\n * Bảng giờ Hoàng Đạo theo Chi của ngày.\n * Mỗi hàng tương ứng với 2 Chi (Tý-Ngọ, Sửu-Mùi, ...) → 6 nhóm.\n * Giá trị là index các Địa Chi tốt (6 giờ tốt trong 12 canh).\n */\nconst AUSPICIOUS_HOURS_TABLE: readonly (readonly number[])[] = [\n [0, 1, 3, 6, 7, 9], // Ngày Tý (0) / Ngọ (6)\n [2, 3, 5, 8, 9, 11], // Ngày Sửu (1) / Mùi (7)\n [0, 1, 4, 5, 7, 10], // Ngày Dần (2) / Thân (8)\n [1, 2, 4, 7, 8, 10], // Ngày Mão (3) / Dậu (9)\n [0, 3, 4, 6, 9, 10], // Ngày Thìn (4) / Tuất (10)\n [2, 5, 6, 8, 11, 0], // Ngày Tỵ (5) / Hợi (11)\n] as const;\n\n/**\n * Lấy danh sách 6 giờ Hoàng Đạo trong ngày.\n * Giờ Hoàng Đạo phụ thuộc vào Địa Chi của ngày.\n *\n * @param dayJd - Julian Day Number của ngày\n * @returns Mảng 6 chuỗi tên Địa Chi (ví dụ: [\"Tý\", \"Sửu\", \"Mão\", \"Ngọ\", \"Mùi\", \"Dậu\"])\n */\nexport function getAuspiciousHours(dayJd: number): string[] {\n const branchIndex = (dayJd + 1) % 12;\n // Nhóm theo 2 Chi (Tý-Ngọ, Sửu-Mùi, ...)\n const groupIndex = branchIndex % 6;\n const hourIndices = AUSPICIOUS_HOURS_TABLE[groupIndex];\n return hourIndices.map((i) => EARTHLY_BRANCHES[i] as string);\n}","import type { LunarDate } from '../core/types.js';\n\n// ============================================================\n// Tên tháng & ngày truyền thống\n// ============================================================\n\n/** Tên tháng âm lịch truyền thống */\nconst MONTH_NAMES = [\n 'Giêng', 'Hai', 'Ba', 'Tư', 'Năm', 'Sáu',\n 'Bảy', 'Tám', 'Chín', 'Mười', 'Một', 'Chạp',\n] as const;\n\n/** Tên số đếm truyền thống */\nconst DAY_UNITS = [\n '', 'Một', 'Hai', 'Ba', 'Bốn', 'Năm',\n 'Sáu', 'Bảy', 'Tám', 'Chín', 'Mười',\n] as const;\n\n/**\n * Lấy tên tháng âm lịch dạng chữ truyền thống.\n *\n * @param month - Tháng âm lịch (1-12)\n * @returns Tên tháng (ví dụ: 1 → \"Giêng\", 12 → \"Chạp\")\n *\n * @example\n * ```typescript\n * getMonthName(1); // \"Giêng\"\n * getMonthName(12); // \"Chạp\"\n * ```\n */\nexport function getMonthName(month: number): string {\n if (month < 1 || month > 12) {\n throw new RangeError(`Lichta: Month must be between 1 and 12, got ${month}`);\n }\n return MONTH_NAMES[month - 1];\n}\n\n/**\n * Lấy tên ngày âm lịch theo cách gọi truyền thống Việt Nam.\n *\n * - 1-10: \"Mùng Một\", \"Mùng Hai\", ..., \"Mùng Mười\"\n * - 11-20: \"Mười Một\", ..., \"Hai Mươi\"\n * - 21-30: \"Hăm Mốt\", \"Hăm Hai\", ..., \"Ba Mươi\"\n *\n * @param day - Ngày âm lịch (1-30)\n *\n * @example\n * ```typescript\n * getDayName(1); // \"Mùng Một\"\n * getDayName(15); // \"Rằm\"\n * getDayName(30); // \"Ba Mươi\"\n * ```\n */\nexport function getDayName(day: number): string {\n if (day < 1 || day > 30) {\n throw new RangeError(`Lichta: Day must be between 1 and 30, got ${day}`);\n }\n\n // Ngày Rằm\n if (day === 15) return 'Rằm';\n\n // Mùng 1-10\n if (day <= 10) {\n return `Mùng ${DAY_UNITS[day]}`;\n }\n\n // 11-19\n if (day < 20) {\n if (day === 11) return 'Mười Một';\n if (day === 14) return 'Mười Bốn';\n return `Mười ${DAY_UNITS[day - 10]}`;\n }\n\n // 20\n if (day === 20) return 'Hai Mươi';\n\n // 21-29\n if (day < 30) {\n if (day === 21) return 'Hăm Mốt';\n if (day === 24) return 'Hăm Tư';\n if (day === 25) return 'Hăm Lăm';\n return `Hăm ${DAY_UNITS[day - 20]}`;\n }\n\n // 30\n return 'Ba Mươi';\n}\n\n// ============================================================\n// Format functions\n// ============================================================\n\n/**\n * Format ngày âm lịch theo pattern.\n *\n * **Patterns hỗ trợ:**\n * | Token | Mô tả | Ví dụ |\n * |-------|------------------------------------------|----------------|\n * | `dd` | Ngày 2 chữ số | 01, 15, 30 |\n * | `d` | Ngày | 1, 15, 30 |\n * | `MM` | Tháng 2 chữ số | 01, 12 |\n * | `M` | Tháng | 1, 12 |\n * | `yyyy`| Năm 4 chữ số | 2024 |\n * | `yy` | Năm 2 chữ số cuối | 24 |\n * | `L` | \"Nhuận\" nếu tháng nhuận, \"\" nếu không | Nhuận |\n * | `CC` | Can Chi năm | Giáp Thìn |\n * | `DC` | Can Chi ngày | Giáp Tý |\n * | `MC` | Can Chi tháng | Bính Dần |\n *\n * @param lunar - Đối tượng LunarDate\n * @param pattern - Chuỗi pattern\n *\n * @example\n * ```typescript\n * formatLunarDate(lunar, 'dd/MM/yyyy');\n * // → \"01/01/2024\"\n *\n * formatLunarDate(lunar, 'Ngày d tháng M năm CC');\n * // → \"Ngày 1 tháng 1 năm Giáp Thìn\"\n * ```\n */\nexport function formatLunarDate(lunar: LunarDate, pattern: string): string {\n let result = pattern;\n\n // Thay thế theo thứ tự dài → ngắn để tránh xung đột (yyyy trước yy, dd trước d, MM trước M)\n result = result.replace(/yyyy/g, String(lunar.year));\n result = result.replace(/yy/g, String(lunar.year).slice(-2));\n result = result.replace(/dd/g, String(lunar.day).padStart(2, '0'));\n result = result.replace(/MM/g, String(lunar.month).padStart(2, '0'));\n result = result.replace(/CC/g, lunar.yearCanChi ?? '');\n result = result.replace(/DC/g, lunar.dayCanChi ?? '');\n result = result.replace(/MC/g, lunar.monthCanChi ?? '');\n result = result.replace(/L/g, lunar.isLeap ? 'Nhuận' : '');\n\n // Thay d và M cuối cùng (sau khi dd, MM đã được xử lý, tránh double-replace)\n // Dùng regex negative lookbehind để không match các token đã được thay\n result = result.replace(/(?<![0-9])d(?![0-9d])/g, String(lunar.day));\n result = result.replace(/(?<![0-9])M(?![0-9MC])/g, String(lunar.month));\n\n return result.trim();\n}\n\n/**\n * Format ngày âm lịch theo kiểu truyền thống Việt Nam.\n *\n * @param lunar - Đối tượng LunarDate\n * @returns Chuỗi truyền thống (ví dụ: \"Mùng Một tháng Giêng năm Giáp Thìn\")\n *\n * @example\n * ```typescript\n * formatTraditional(lunar);\n * // → \"Mùng Một tháng Giêng năm Giáp Thìn\"\n * ```\n */\nexport function formatTraditional(lunar: LunarDate): string {\n const dayName = getDayName(lunar.day);\n const monthName = getMonthName(lunar.month);\n const leapSuffix = lunar.isLeap ? ' Nhuận' : '';\n const yearCanChi = lunar.yearCanChi ?? '';\n\n return `${dayName} tháng ${monthName}${leapSuffix} năm ${yearCanChi}`.trim();\n}\n","/**\n * Internationalization — Hỗ trợ song ngữ Việt-Anh\n */\nexport type Locale = 'vi' | 'en' | 'ja' | 'ko';\n\nconst translations = {\n vi: {\n heavenlyStems: ['Giáp', 'Ất', 'Bính', 'Đinh', 'Mậu', 'Kỷ', 'Canh', 'Tân', 'Nhâm', 'Quý'],\n earthlyBranches: ['Tý', 'Sửu', 'Dần', 'Mão', 'Thìn', 'Tỵ', 'Ngọ', 'Mùi', 'Thân', 'Dậu', 'Tuất', 'Hợi'],\n fiveElements: ['Kim', 'Mộc', 'Thủy', 'Hỏa', 'Thổ'],\n zodiacAnimals: ['Chuột', 'Trâu', 'Cọp', 'Thỏ', 'Rồng', 'Rắn', 'Ngựa', 'Dê', 'Khỉ', 'Gà', 'Chó', 'Heo'],\n monthNames: ['Giêng', 'Hai', 'Ba', 'Tư', 'Năm', 'Sáu', 'Bảy', 'Tám', 'Chín', 'Mười', 'Một', 'Chạp'],\n weekDays: ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'],\n leapLabel: 'Nhuận',\n yearLabel: 'Năm',\n monthLabel: 'Tháng',\n destiny: 'Mệnh',\n },\n en: {\n heavenlyStems: ['Giáp', 'Ất', 'Bính', 'Đinh', 'Mậu', 'Kỷ', 'Canh', 'Tân', 'Nhâm', 'Quý'],\n earthlyBranches: ['Tý', 'Sửu', 'Dần', 'Mão', 'Thìn', 'Tỵ', 'Ngọ', 'Mùi', 'Thân', 'Dậu', 'Tuất', 'Hợi'],\n fiveElements: ['Metal', 'Wood', 'Water', 'Fire', 'Earth'],\n zodiacAnimals: ['Rat', 'Ox', 'Tiger', 'Rabbit', 'Dragon', 'Snake', 'Horse', 'Goat', 'Monkey', 'Rooster', 'Dog', 'Pig'],\n monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],\n weekDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],\n leapLabel: 'Leap',\n yearLabel: 'Year',\n monthLabel: 'Month',\n destiny: 'Destiny',\n },\n ja: {\n heavenlyStems: ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'],\n earthlyBranches: ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'],\n fiveElements: ['金', '木', '水', '火', '土'],\n zodiacAnimals: ['鼠', '牛', '虎', '兎', '竜', '蛇', '馬', '羊', '猿', '鶏', '犬', '猪'],\n monthNames: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],\n weekDays: ['日', '月', '火', '水', '木', '金', '土'],\n leapLabel: '閏',\n yearLabel: '年',\n monthLabel: '月',\n destiny: '命',\n },\n ko: {\n heavenlyStems: ['갑', '을', '병', '정', '무', '기', '경', '신', '임', '계'],\n earthlyBranches: ['자', '축', '인', '묘', '진', '사', '오', '미', '신', '유', '술', '해'],\n fiveElements: ['금', '목', '수', '화', '토'],\n zodiacAnimals: ['쥐', '소', '호랑이', '토끼', '용', '뱀', '말', '양', '원숭이', '닭', '개', '돼지'],\n monthNames: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],\n weekDays: ['일', '월', '화', '수', '목', '금', '토'],\n leapLabel: '윤',\n yearLabel: '년',\n monthLabel: '월',\n destiny: '명',\n },\n} as const;\n\n/**\n * Lấy bộ dịch theo locale\n * @param locale - Ngôn ngữ ('vi' | 'en' | 'ja' | 'ko')\n */\nexport function t(locale: Locale) {\n return translations[locale];\n}\n\n/**\n * Lấy tên con giáp theo locale\n * @param branchIndex - Index Địa Chi (0-11)\n * @param locale - Ngôn ngữ\n */\nexport function getZodiacAnimal(branchIndex: number, locale: Locale = 'vi'): string {\n return translations[locale].zodiacAnimals[branchIndex % 12];\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constants/heavenly-stems.ts","../src/constants/earthly-branches.ts","../src/core/lunar.ts","../src/constants/five-elements.ts","../src/constants/i18n.ts","../src/core/feng-shui.ts","../src/utils/format.ts","../src/utils/calendar-grid.ts"],"sourcesContent":["// ============================================================\n// Lichta Core — Public API (Root Entry Point)\n// ============================================================\n\n// Xuất bản phần lõi TypeScript (Toán học lịch)\nexport { LichTa, jdFromDate, jdToDate } from './core/lunar.js';\nexport type { LunarDate, SolarDate } from './core/types.js';\n\n// Xuất bản phần phong thủy\nexport {\n getYearDetails,\n getDayCanChi,\n getMonthCanChi,\n getHourCanChi,\n getAuspiciousHours,\n getAuspiciousHourIndices,\n} from './core/feng-shui.js';\nexport type { YearDetails } from './core/feng-shui.js';\n\n// Xuất bản phần format/tiện ích\nexport { formatLunarDate, formatTraditional, getMonthName, getDayName } from './utils/format.js';\nexport { getCalendarGrid } from './utils/calendar-grid.js';\nexport type { CalendarDayCell } from './utils/calendar-grid.js';\n\n// Xuất bản phần i18n\nexport { t, getZodiacAnimal } from './constants/i18n.js';\nexport type { Locale } from './constants/i18n.js';\n","/**\n * Heavenly Stems (Thiên Can)\n * Giáp (Dương) | Wood\n * Ất (Âm) | Wood\n * Bính (Dương) | Fire\n * Đinh (Âm) | Fire\n * Mậu (Dương) | Earth\n * Kỷ (Âm) | Earth\n * Canh (Dương) | Metal\n * Tân (Âm) | Metal\n * Nhâm (Dương) | Water\n * Quý (Âm) | Water\n */\nexport const HEAVENLY_STEMS = [\n 'Giáp', 'Ất', 'Bính', 'Đinh', 'Mậu',\n 'Kỷ', 'Canh', 'Tân', 'Nhâm', 'Quý'\n] as const;\n\n/**\n * Tính trọng số của Thiên Can (dùng để tính Ngũ Hành)\n * Index: 0, 1 -> 1 | 2, 3 -> 2 | ... | 8, 9 -> 5\n */\nexport function getStemWeight(index: number): number {\n return Math.floor(index / 2) + 1;\n}","/**\n * Earthly Branches (Địa Chi - 12 con giáp)\n * Tý | Rat (Chuột)\n * Sửu | Ox (Trâu)\n * Dần | Tiger (Cọp)\n * Mão | Rabbit (Thỏ)\n * Thìn | Dragon (Rồng)\n * Tỵ | Snake (Rắn)\n * Ngọ | Horse (Ngựa)\n * Mùi | Goat (Dê)\n * Thân | Monkey (Khỉ)\n * Dậu | Rooster (Gà)\n * Tuất | Dog (Chó)\n * Hợi | Pig (Heo)\n */\nexport const EARTHLY_BRANCHES = [\n 'Tý', 'Sửu', 'Dần', 'Mão', 'Thìn', 'Tỵ',\n 'Ngọ', 'Mùi', 'Thân', 'Dậu', 'Tuất', 'Hợi'\n] as const;\n\n/**\n * Tính trọng số của Địa Chi (dùng để tính Ngũ Hành)\n * Chu kỳ lặp lại sau mỗi 6 Chi (index % 6)\n */\nexport function getBranchWeight(index: number): number {\n const normalizedIndex = index % 6;\n return Math.floor(normalizedIndex / 2);\n}\n\n/**\n * Tính Dương - Âm (Yin - Yang) cho Địa Chi\n * Index: 0 -> 1 (Dương) | 1 -> 0 (Âm) | ...\n */\nexport function getBranchYinYang(index: number): 1 | 0 {\n return (index % 2 === 0) ? 1 : 0;\n}\n\n/**\n * Tính Ngũ Hành cho Địa Chi\n * Tý: 1 (Thủy Dương), Sửu: 12 (Thổ Âm), Dần: 3 (Mộc Dương), ...\n */\nexport function getBranchElement(index: number): number {\n const weight = Math.floor(index / 2) + 1;\n return weight === 5 ? 1 : weight;\n}","import type { LunarDate, SolarDate } from './types.js';\nimport { HEAVENLY_STEMS } from '../constants/heavenly-stems.js';\nimport { EARTHLY_BRANCHES } from '../constants/earthly-branches.js';\n\n/**\n * Thuật toán chuyển đổi Dương lịch ↔ Âm lịch Việt Nam\n * Dựa trên công trình của Hồ Ngọc Đức (Đại học Leipzig)\n * Tham khảo: Jean Meeus, \"Astronomical Algorithms\" (1998)\n *\n * Copyright (c) 2006 Ho Ngoc Duc. All Rights Reserved.\n * TypeScript adaptation for Lichta library.\n */\n\nconst PI = Math.PI;\n\nfunction INT(d: number): number {\n return Math.floor(d);\n}\n\n// ============================================================\n// Julian Day Number\n// ============================================================\n\n/**\n * Chuyển ngày Gregorian sang Julian Day Number.\n *\n * @param dd - Ngày (1-31)\n * @param mm - Tháng (1-12)\n * @param yy - Năm\n */\nexport function jdFromDate(dd: number, mm: number, yy: number): number {\n const a = INT((14 - mm) / 12);\n const y = yy + 4800 - a;\n const m = mm + 12 * a - 3;\n let jd = dd + INT((153 * m + 2) / 5) + 365 * y + INT(y / 4) - INT(y / 100) + INT(y / 400) - 32045;\n if (jd < 2299161) {\n jd = dd + INT((153 * m + 2) / 5) + 365 * y + INT(y / 4) - 32083;\n }\n return jd;\n}\n\n/**\n * Chuyển Julian Day Number sang ngày Gregorian.\n *\n * @param jd - Julian Day Number\n * @returns Tuple [day, month, year]\n */\nexport function jdToDate(jd: number): [number, number, number] {\n let a: number, b: number, c: number;\n if (jd > 2299160) {\n a = jd + 32044;\n b = INT((4 * a + 3) / 146097);\n c = a - INT((b * 146097) / 4);\n } else {\n b = 0;\n c = jd + 32082;\n }\n const d = INT((4 * c + 3) / 1461);\n const e = c - INT((1461 * d) / 4);\n const m = INT((5 * e + 2) / 153);\n const day = e - INT((153 * m + 2) / 5) + 1;\n const month = m + 3 - 12 * INT(m / 10);\n const year = b * 100 + d - 4800 + INT(m / 10);\n return [day, month, year];\n}\n\n// ============================================================\n// Điểm Sóc (New Moon) — Thuật toán Hồ Ngọc Đức / Jean Meeus\n// ============================================================\n\n/**\n * Tính thời điểm điểm Sóc (New Moon) thứ k\n * (đo bằng số ngày Julian kể từ 1/1/4713 BC trưa UTC).\n *\n * k=0 tương ứng New Moon ngày 1/1/1900 13:52 UTC.\n *\n * @param k - Số thứ tự New Moon\n */\nfunction NewMoon(k: number): number {\n const T = k / 1236.85;\n const T2 = T * T;\n const T3 = T2 * T;\n const dr = PI / 180;\n\n let Jd1 = 2415020.75933 + 29.53058868 * k + 0.0001178 * T2 - 0.000000155 * T3;\n Jd1 = Jd1 + 0.00033 * Math.sin((166.56 + 132.87 * T - 0.009173 * T2) * dr);\n\n // Mean anomaly của Mặt Trời\n const M = 359.2242 + 29.10535608 * k - 0.0000333 * T2 - 0.00000347 * T3;\n // Mean anomaly của Mặt Trăng\n const Mpr = 306.0253 + 385.81691806 * k + 0.0107306 * T2 + 0.00001236 * T3;\n // Argument of latitude của Mặt Trăng\n const F = 21.2964 + 390.67050646 * k - 0.0016528 * T2 - 0.00000239 * T3;\n\n // Hiệu chỉnh\n let C1 = (0.1734 - 0.000393 * T) * Math.sin(M * dr) + 0.0021 * Math.sin(2 * dr * M);\n C1 = C1 - 0.4068 * Math.sin(Mpr * dr) + 0.0161 * Math.sin(dr * 2 * Mpr);\n C1 = C1 - 0.0004 * Math.sin(dr * 3 * Mpr);\n C1 = C1 + 0.0104 * Math.sin(dr * 2 * F) - 0.0051 * Math.sin(dr * (M + Mpr));\n C1 = C1 - 0.0074 * Math.sin(dr * (M - Mpr)) + 0.0004 * Math.sin(dr * (2 * F + M));\n C1 = C1 - 0.0004 * Math.sin(dr * (2 * F - M)) - 0.0006 * Math.sin(dr * (2 * F + Mpr));\n C1 = C1 + 0.001 * Math.sin(dr * (2 * F - Mpr)) + 0.0005 * Math.sin(dr * (2 * Mpr + M));\n\n const JdNew = Jd1 + C1;\n return JdNew;\n}\n\n/**\n * Tính ngày Julian (integer) của điểm Sóc thứ k theo múi giờ.\n *\n * @param k - Số thứ tự New Moon\n * @param timeZone - Múi giờ (7 = GMT+7)\n */\nexport function getNewMoonDay(k: number, timeZone: number): number {\n return INT(NewMoon(k) + 0.5 + timeZone / 24);\n}\n\n// ============================================================\n// Kinh độ Mặt Trời (Sun Longitude)\n// ============================================================\n\n/**\n * Tính kinh độ Mặt Trời tại ngày Julian cho trước.\n * Trả về index 0-11 (mỗi 30°), dùng để xác định Trung khí.\n *\n * Index 9 = Đông Chí (270°-300°)\n *\n * @param jdn - Julian Day Number\n * @param timeZone - Múi giờ\n */\nexport function getSunLongitude(jdn: number, timeZone: number): number {\n const T = (jdn - 2451545.5 - timeZone / 24) / 36525;\n const T2 = T * T;\n const dr = PI / 180;\n\n // Mean longitude\n const M = 357.5291 + 35999.0503 * T - 0.0001559 * T2 - 0.00000048 * T * T2;\n\n // Mean longitude\n const L0 = 280.46645 + 36000.76983 * T + 0.0003032 * T2;\n\n let DL = (1.9146 - 0.004817 * T - 0.000014 * T2) * Math.sin(dr * M);\n DL = DL + (0.019993 - 0.000101 * T) * Math.sin(dr * 2 * M) + 0.00029 * Math.sin(dr * 3 * M);\n\n let L = L0 + DL;\n // Chuẩn hóa\n L = L - 360 * INT(L / 360);\n return INT(L / 30);\n}\n\n// ============================================================\n// Tháng 11 âm lịch & Tháng nhuận\n// ============================================================\n\n/**\n * Tìm JDN của mùng 1 tháng 11 âm lịch (tháng chứa Đông Chí).\n */\nfunction getLunarMonth11(yy: number, timeZone: number): number {\n const off = jdFromDate(31, 12, yy) - 2415021.076998695;\n const k = INT(off / 29.530588853);\n let nm = getNewMoonDay(k, timeZone);\n const sunLong = getSunLongitude(nm, timeZone);\n if (sunLong >= 9) {\n nm = getNewMoonDay(k - 1, timeZone);\n }\n return nm;\n}\n\n/**\n * Tìm offset tháng nhuận (tháng đầu tiên không chứa Trung khí\n * giữa 2 tháng 11 âm lịch liên tiếp).\n */\nfunction getLeapMonthOffset(a11: number, timeZone: number): number {\n const k = INT((a11 - 2415021.076998695) / 29.530588853 + 0.5);\n let last: number;\n let i = 1;\n let arc = getSunLongitude(getNewMoonDay(k + i, timeZone), timeZone);\n last = arc;\n while (i < 14) {\n i++;\n arc = getSunLongitude(getNewMoonDay(k + i, timeZone), timeZone);\n if (arc !== last) {\n last = arc;\n } else {\n // 2 tháng liên tiếp cùng Trung khí → tháng trước đó là tháng nhuận\n break;\n }\n }\n return i - 1;\n}\n\n// ============================================================\n// Can Chi helpers\n// ============================================================\n\nfunction getYearCanChi(lunarYear: number): string {\n const stemIndex = (lunarYear + 6) % 10;\n const branchIndex = (lunarYear + 8) % 12;\n return `${HEAVENLY_STEMS[stemIndex]} ${EARTHLY_BRANCHES[branchIndex]}`;\n}\n\nfunction getMonthCanChiInternal(lunarMonth: number, lunarYear: number): string {\n const yearStemIndex = (lunarYear + 6) % 10;\n const monthStemOffset = (yearStemIndex % 5) * 2 + 2;\n const monthStemIndex = (monthStemOffset + lunarMonth - 1) % 10;\n const monthBranchIndex = (lunarMonth + 1) % 12;\n return `${HEAVENLY_STEMS[monthStemIndex]} ${EARTHLY_BRANCHES[monthBranchIndex]}`;\n}\n\nfunction getDayCanChiInternal(jd: number): string {\n const stemIndex = (jd + 9) % 10;\n const branchIndex = (jd + 1) % 12;\n return `${HEAVENLY_STEMS[stemIndex]} ${EARTHLY_BRANCHES[branchIndex]}`;\n}\n\n// ============================================================\n// LichTa — Public API\n// ============================================================\n\nexport class LichTa {\n /**\n * Chuyển đổi ngày Dương lịch sang Âm lịch.\n *\n * Sử dụng thuật toán Hồ Ngọc Đức để tìm Điểm Sóc và Tiết Khí,\n * từ đó xác định ngày, tháng, năm âm lịch tương ứng.\n *\n * @param day - Ngày dương lịch (1-31)\n * @param month - Tháng dương lịch (1-12)\n * @param year - Năm dương lịch (1800-2199)\n * @param timeZone - Múi giờ (mặc định: 7 cho GMT+7 Việt Nam)\n * @returns Đối tượng LunarDate chứa thông tin ngày âm lịch\n * @throws {RangeError} Khi input ngoài phạm vi hợp lệ\n *\n * @example\n * ```typescript\n * const lunar = LichTa.toLunar(10, 2, 2024);\n * // → { day: 1, month: 1, year: 2024, isLeap: false, yearCanChi: 'Giáp Thìn' }\n * ```\n */\n static toLunar(day: number, month: number, year: number, timeZone: number = 7): LunarDate {\n if (year < 1800 || year > 2199) {\n throw new RangeError(`Lichta: Year must be between 1800 and 2199, got ${year}`);\n }\n if (month < 1 || month > 12) {\n throw new RangeError(`Lichta: Month must be between 1 and 12, got ${month}`);\n }\n if (day < 1 || day > 31) {\n throw new RangeError(`Lichta: Day must be between 1 and 31, got ${day}`);\n }\n\n const dayNumber = jdFromDate(day, month, year);\n const k = INT((dayNumber - 2415021.076998695) / 29.530588853);\n\n let monthStart = getNewMoonDay(k + 1, timeZone);\n if (monthStart > dayNumber) {\n monthStart = getNewMoonDay(k, timeZone);\n }\n\n let a11 = getLunarMonth11(year, timeZone);\n let b11 = a11;\n let lunarYear: number;\n if (a11 >= monthStart) {\n lunarYear = year;\n a11 = getLunarMonth11(year - 1, timeZone);\n } else {\n lunarYear = year + 1;\n b11 = getLunarMonth11(year + 1, timeZone);\n }\n\n const lunarDay = dayNumber - monthStart + 1;\n const diff = INT((monthStart - a11) / 29);\n\n // Kiểm tra tháng nhuận: một năm âm lịch nhuận có 13 tháng (~384 ngày)\n // thay vì 12 tháng (~354 ngày) giữa 2 mốc tháng 11.\n let lunarLeap = false;\n let lunarMonth = diff + 11;\n if (b11 - a11 > 365) {\n const leapMonthDiff = getLeapMonthOffset(a11, timeZone);\n if (diff >= leapMonthDiff) {\n lunarMonth = diff + 10;\n if (diff === leapMonthDiff) {\n lunarLeap = true;\n }\n }\n }\n if (lunarMonth > 12) {\n lunarMonth -= 12;\n }\n if (lunarMonth >= 11 && diff < 4) {\n lunarYear -= 1;\n }\n\n const yearCanChi = getYearCanChi(lunarYear);\n const monthCanChi = getMonthCanChiInternal(lunarMonth, lunarYear);\n const dayCanChi = getDayCanChiInternal(dayNumber);\n\n return {\n day: lunarDay,\n month: lunarMonth,\n year: lunarYear,\n isLeap: lunarLeap,\n jd: dayNumber,\n dayCanChi,\n monthCanChi,\n yearCanChi,\n };\n }\n\n /**\n * Chuyển đổi ngày Âm lịch sang Dương lịch.\n *\n * @param lunarDay - Ngày âm lịch\n * @param lunarMonth - Tháng âm lịch\n * @param lunarYear - Năm âm lịch\n * @param isLeap - Có phải tháng nhuận không\n * @param timeZone - Múi giờ (mặc định: 7 cho GMT+7 Việt Nam)\n * @returns Đối tượng SolarDate\n * @throws {RangeError} Khi năm ngoài phạm vi 1800-2199\n *\n * @example\n * ```typescript\n * const solar = LichTa.toSolar(1, 1, 2024, false);\n * // → { day: 10, month: 2, year: 2024 }\n * ```\n */\n static toSolar(\n lunarDay: number,\n lunarMonth: number,\n lunarYear: number,\n isLeap: boolean = false,\n timeZone: number = 7\n ): SolarDate {\n if (lunarYear < 1800 || lunarYear > 2199) {\n throw new RangeError(`Lichta: Year must be between 1800 and 2199, got ${lunarYear}`);\n }\n\n let a11: number, b11: number;\n\n if (lunarMonth < 11) {\n a11 = getLunarMonth11(lunarYear - 1, timeZone);\n b11 = getLunarMonth11(lunarYear, timeZone);\n } else {\n a11 = getLunarMonth11(lunarYear, timeZone);\n b11 = getLunarMonth11(lunarYear + 1, timeZone);\n }\n\n const k = INT(0.5 + (a11 - 2415021.076998695) / 29.530588853);\n let off = lunarMonth - 11;\n if (off < 0) {\n off += 12;\n }\n\n // Xử lý tháng nhuận: nếu năm có tháng nhuận (13 tháng, ~384 ngày giữa\n // 2 mốc tháng 11), cần dịch offset thêm 1 cho tháng nhuận và các tháng\n // sau nó, để phân biệt với tháng thường cùng số.\n if (b11 - a11 > 365) {\n const leapOff = getLeapMonthOffset(a11, timeZone);\n let leapMonth = leapOff - 2;\n if (leapMonth < 0) leapMonth += 12;\n\n if (isLeap && lunarMonth !== leapMonth) {\n throw new RangeError(\n `Lichta: Month ${lunarMonth} is not a leap month in lunar year ${lunarYear}`\n );\n } else if (isLeap || off >= leapOff) {\n off += 1;\n }\n }\n\n const nm = getNewMoonDay(k + off, timeZone);\n const jd = nm + lunarDay - 1;\n const [day, month, year] = jdToDate(jd);\n\n return { day, month, year };\n }\n}","/**\n * Five Elements (Ngũ Hành)\n * Kim | Metal\n * Mộc | Wood\n * Thủy | Water\n * Hỏa | Fire\n * Thổ | Earth\n */\nexport const FIVE_ELEMENTS = ['Kim', 'Mộc', 'Thủy', 'Hỏa', 'Thổ'];","/**\n * Internationalization — Hỗ trợ song ngữ Việt-Anh\n */\nexport type Locale = 'vi' | 'en' | 'ja' | 'ko';\n\nconst translations = {\n vi: {\n heavenlyStems: ['Giáp', 'Ất', 'Bính', 'Đinh', 'Mậu', 'Kỷ', 'Canh', 'Tân', 'Nhâm', 'Quý'],\n earthlyBranches: ['Tý', 'Sửu', 'Dần', 'Mão', 'Thìn', 'Tỵ', 'Ngọ', 'Mùi', 'Thân', 'Dậu', 'Tuất', 'Hợi'],\n fiveElements: ['Kim', 'Mộc', 'Thủy', 'Hỏa', 'Thổ'],\n zodiacAnimals: ['Chuột', 'Trâu', 'Cọp', 'Thỏ', 'Rồng', 'Rắn', 'Ngựa', 'Dê', 'Khỉ', 'Gà', 'Chó', 'Heo'],\n // @deprecated Ngữ nghĩa không nhất quán giữa các locale (xem lunarMonthNames/solarMonthNames).\n monthNames: ['Giêng', 'Hai', 'Ba', 'Tư', 'Năm', 'Sáu', 'Bảy', 'Tám', 'Chín', 'Mười', 'Một', 'Chạp'],\n lunarMonthNames: ['Giêng', 'Hai', 'Ba', 'Tư', 'Năm', 'Sáu', 'Bảy', 'Tám', 'Chín', 'Mười', 'Một', 'Chạp'],\n solarMonthNames: ['Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5', 'Tháng 6', 'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11', 'Tháng 12'],\n weekDays: ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'],\n leapLabel: 'Nhuận',\n yearLabel: 'Năm',\n monthLabel: 'Tháng',\n destiny: 'Mệnh',\n },\n en: {\n heavenlyStems: ['Giáp', 'Ất', 'Bính', 'Đinh', 'Mậu', 'Kỷ', 'Canh', 'Tân', 'Nhâm', 'Quý'],\n earthlyBranches: ['Tý', 'Sửu', 'Dần', 'Mão', 'Thìn', 'Tỵ', 'Ngọ', 'Mùi', 'Thân', 'Dậu', 'Tuất', 'Hợi'],\n fiveElements: ['Metal', 'Wood', 'Water', 'Fire', 'Earth'],\n zodiacAnimals: ['Rat', 'Ox', 'Tiger', 'Rabbit', 'Dragon', 'Snake', 'Horse', 'Goat', 'Monkey', 'Rooster', 'Dog', 'Pig'],\n // @deprecated Ngữ nghĩa không nhất quán giữa các locale (xem lunarMonthNames/solarMonthNames).\n monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],\n lunarMonthNames: ['Month 1', 'Month 2', 'Month 3', 'Month 4', 'Month 5', 'Month 6', 'Month 7', 'Month 8', 'Month 9', 'Month 10', 'Month 11', 'Month 12'],\n solarMonthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],\n weekDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],\n leapLabel: 'Leap',\n yearLabel: 'Year',\n monthLabel: 'Month',\n destiny: 'Destiny',\n },\n ja: {\n heavenlyStems: ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'],\n earthlyBranches: ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'],\n fiveElements: ['金', '木', '水', '火', '土'],\n zodiacAnimals: ['鼠', '牛', '虎', '兎', '竜', '蛇', '馬', '羊', '猿', '鶏', '犬', '猪'],\n // @deprecated Ngữ nghĩa không nhất quán giữa các locale (xem lunarMonthNames/solarMonthNames).\n monthNames: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],\n lunarMonthNames: ['旧暦1月', '旧暦2月', '旧暦3月', '旧暦4月', '旧暦5月', '旧暦6月', '旧暦7月', '旧暦8月', '旧暦9月', '旧暦10月', '旧暦11月', '旧暦12月'],\n solarMonthNames: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],\n weekDays: ['日', '月', '火', '水', '木', '金', '土'],\n leapLabel: '閏',\n yearLabel: '年',\n monthLabel: '月',\n destiny: '命',\n },\n ko: {\n heavenlyStems: ['갑', '을', '병', '정', '무', '기', '경', '신', '임', '계'],\n earthlyBranches: ['자', '축', '인', '묘', '진', '사', '오', '미', '신', '유', '술', '해'],\n fiveElements: ['금', '목', '수', '화', '토'],\n zodiacAnimals: ['쥐', '소', '호랑이', '토끼', '용', '뱀', '말', '양', '원숭이', '닭', '개', '돼지'],\n // @deprecated Ngữ nghĩa không nhất quán giữa các locale (xem lunarMonthNames/solarMonthNames).\n monthNames: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],\n lunarMonthNames: ['음력 1월', '음력 2월', '음력 3월', '음력 4월', '음력 5월', '음력 6월', '음력 7월', '음력 8월', '음력 9월', '음력 10월', '음력 11월', '음력 12월'],\n solarMonthNames: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],\n weekDays: ['일', '월', '화', '수', '목', '금', '토'],\n leapLabel: '윤',\n yearLabel: '년',\n monthLabel: '월',\n destiny: '명',\n },\n} as const;\n\n/**\n * Lấy bộ dịch theo locale\n * @param locale - Ngôn ngữ ('vi' | 'en' | 'ja' | 'ko')\n */\nexport function t(locale: Locale) {\n return translations[locale];\n}\n\n/**\n * Lấy tên con giáp theo locale\n * @param branchIndex - Index Địa Chi (0-11)\n * @param locale - Ngôn ngữ\n */\nexport function getZodiacAnimal(branchIndex: number, locale: Locale = 'vi'): string {\n return translations[locale].zodiacAnimals[branchIndex % 12];\n}\n","import { HEAVENLY_STEMS, getStemWeight } from '../constants/heavenly-stems.js';\nimport { EARTHLY_BRANCHES, getBranchWeight } from '../constants/earthly-branches.js';\nimport { FIVE_ELEMENTS } from '../constants/five-elements.js';\nimport { t, type Locale } from '../constants/i18n.js';\n\n/** Kết quả trả về của {@link getYearDetails}. */\nexport interface YearDetails {\n can: string;\n chi: string;\n menh: string;\n fullString: string;\n}\n\n/**\n * Modulo luôn trả về số không âm (khác toán tử `%` gốc của JS, vốn trả về số âm\n * khi vế trái âm — ví dụ `-1 % 10 === -1`, trong khi `mod(-1, 10) === 9`).\n */\nfunction mod(n: number, m: number): number {\n return ((n % m) + m) % m;\n}\n\n/**\n * Lấy ra chuỗi mô tả toàn bộ Can, Chi và Mệnh dựa vào số năm (Ví dụ: 2024)\n * Theo quy ước: Năm 4 (sau CN) là năm Giáp Tý (Can index 0, Chi index 0)\n *\n * Công thức Can Chi là chu kỳ 60 năm lặp lại vô hạn nên nhận mọi số năm nguyên,\n * kể cả năm 0 hoặc âm (trước Công Nguyên) — không giới hạn phạm vi như\n * {@link LichTa.toLunar} (phạm vi đó là do thuật toán tìm Điểm Sóc, không áp\n * dụng cho công thức Can Chi thuần).\n *\n * @param year - Năm (số nguyên bất kỳ, kể cả 0 hoặc âm)\n *\n * @example\n * ```typescript\n * getYearDetails(2024);\n * // → { can: 'Giáp', chi: 'Thìn', menh: 'Thủy', fullString: 'Giáp Thìn - Mệnh Thủy' }\n * ```\n */\nexport function getYearDetails(year: number): YearDetails {\n // Tính index của Can và Chi dựa trên mốc năm gốc\n // Năm 1984, 2044 là Giáp Tý -> Offset để tính toán dễ nhất là lấy (year - 4) % hệ cơ số\n const stemIndex = mod(year - 4, 10);\n const branchIndex = mod(year - 4, 12);\n\n // Công thức vàng tính Ngũ Hành\n const stemWeight = getStemWeight(stemIndex);\n const branchWeight = getBranchWeight(branchIndex);\n\n let elementValue = stemWeight + branchWeight;\n if (elementValue > 5) {\n elementValue -= 5;\n }\n\n // Value ra từ 1-5, nên trừ 1 để lấy index của mảng FIVE_ELEMENTS (0-4)\n const elementIndex = elementValue - 1;\n\n return {\n can: HEAVENLY_STEMS[stemIndex],\n chi: EARTHLY_BRANCHES[branchIndex],\n menh: FIVE_ELEMENTS[elementIndex],\n fullString: `${HEAVENLY_STEMS[stemIndex]} ${EARTHLY_BRANCHES[branchIndex]} - Mệnh ${FIVE_ELEMENTS[elementIndex]}`\n };\n}\n\n// ============================================================\n// Can Chi cho Ngày, Tháng, Giờ\n// ============================================================\n\n/**\n * Tính Can Chi ngày từ Julian Day Number.\n *\n * @param jd - Julian Day Number\n * @param locale - Ngôn ngữ hiển thị (mặc định: 'vi', giữ nguyên hành vi cũ)\n * @returns Chuỗi Can Chi (ví dụ: \"Giáp Tý\")\n */\nexport function getDayCanChi(jd: number, locale: Locale = 'vi'): string {\n const stemIndex = (jd + 9) % 10;\n const branchIndex = (jd + 1) % 12;\n const { heavenlyStems, earthlyBranches } = t(locale);\n return `${heavenlyStems[stemIndex]} ${earthlyBranches[branchIndex]}`;\n}\n\n/**\n * Tính Can Chi tháng từ tháng và năm âm lịch.\n * Can tháng phụ thuộc vào Can của năm (quy tắc Ngũ Hổ Độn):\n * - Năm Giáp/Kỷ → Tháng Giêng bắt đầu bằng Bính (index 2)\n * - Năm Ất/Canh → Tháng Giêng bắt đầu bằng Mậu (index 4)\n * - Năm Bính/Tân → Tháng Giêng bắt đầu bằng Canh (index 6)\n * - Năm Đinh/Nhâm → Tháng Giêng bắt đầu bằng Nhâm (index 8)\n * - Năm Mậu/Quý → Tháng Giêng bắt đầu bằng Giáp (index 0)\n *\n * @param lunarMonth - Tháng âm lịch (1-12)\n * @param lunarYear - Năm âm lịch\n * @param locale - Ngôn ngữ hiển thị (mặc định: 'vi', giữ nguyên hành vi cũ)\n */\nexport function getMonthCanChi(lunarMonth: number, lunarYear: number, locale: Locale = 'vi'): string {\n const yearStemIndex = (lunarYear + 6) % 10;\n const monthStemOffset = (yearStemIndex % 5) * 2 + 2;\n const monthStemIndex = (monthStemOffset + lunarMonth - 1) % 10;\n const monthBranchIndex = (lunarMonth + 1) % 12;\n const { heavenlyStems, earthlyBranches } = t(locale);\n return `${heavenlyStems[monthStemIndex]} ${earthlyBranches[monthBranchIndex]}`;\n}\n\n/**\n * Tính Can Chi giờ từ giờ (0-23) và Julian Day Number của ngày.\n *\n * 12 canh giờ (mỗi canh = 2 tiếng):\n * Tý: 23h-01h, Sửu: 01h-03h, Dần: 03h-05h, ...\n *\n * Can giờ Tý phụ thuộc Can ngày (quy tắc Ngũ Thử Độn):\n * - Ngày Giáp/Kỷ → Giờ Tý bắt đầu bằng Giáp\n * - Ngày Ất/Canh → Giờ Tý bắt đầu bằng Bính\n * - Ngày Bính/Tân → Giờ Tý bắt đầu bằng Mậu\n * - Ngày Đinh/Nhâm → Giờ Tý bắt đầu bằng Canh\n * - Ngày Mậu/Quý → Giờ Tý bắt đầu bằng Nhâm\n *\n * @param hour - Giờ (0-23)\n * @param dayJd - Julian Day Number của ngày\n * @param locale - Ngôn ngữ hiển thị (mặc định: 'vi', giữ nguyên hành vi cũ)\n */\nexport function getHourCanChi(hour: number, dayJd: number, locale: Locale = 'vi'): string {\n // Tính Địa Chi của giờ: Tý từ 23h, mỗi canh 2 tiếng\n const hourBranchIndex = Math.floor((hour + 1) / 2) % 12;\n\n // Tính Thiên Can của giờ dựa vào Can ngày\n const dayStemIndex = (dayJd + 9) % 10;\n const hourStemOffset = (dayStemIndex % 5) * 2;\n const hourStemIndex = (hourStemOffset + hourBranchIndex) % 10;\n\n const { heavenlyStems, earthlyBranches } = t(locale);\n return `${heavenlyStems[hourStemIndex]} ${earthlyBranches[hourBranchIndex]}`;\n}\n\n// ============================================================\n// Giờ Hoàng Đạo\n// ============================================================\n\n/**\n * Bảng giờ Hoàng Đạo theo Chi của ngày.\n * Mỗi hàng tương ứng với 2 Chi (Tý-Ngọ, Sửu-Mùi, ...) → 6 nhóm.\n * Giá trị là index các Địa Chi tốt (6 giờ tốt trong 12 canh).\n */\nconst AUSPICIOUS_HOURS_TABLE: readonly (readonly number[])[] = [\n [0, 1, 3, 6, 7, 9], // Ngày Tý (0) / Ngọ (6)\n [2, 3, 5, 8, 9, 11], // Ngày Sửu (1) / Mùi (7)\n [0, 1, 4, 5, 7, 10], // Ngày Dần (2) / Thân (8)\n [1, 2, 4, 7, 8, 10], // Ngày Mão (3) / Dậu (9)\n [0, 3, 4, 6, 9, 10], // Ngày Thìn (4) / Tuất (10)\n [2, 5, 6, 8, 11, 0], // Ngày Tỵ (5) / Hợi (11)\n] as const;\n\n/**\n * Lấy danh sách 6 giờ Hoàng Đạo trong ngày.\n * Giờ Hoàng Đạo phụ thuộc vào Địa Chi của ngày.\n *\n * @param dayJd - Julian Day Number của ngày\n * @param locale - Ngôn ngữ hiển thị (mặc định: 'vi', giữ nguyên hành vi cũ)\n * @returns Mảng 6 chuỗi tên Địa Chi (ví dụ: [\"Tý\", \"Sửu\", \"Mão\", \"Ngọ\", \"Mùi\", \"Dậu\"])\n */\nexport function getAuspiciousHours(dayJd: number, locale: Locale = 'vi'): string[] {\n const branchIndex = (dayJd + 1) % 12;\n // Nhóm theo 2 Chi (Tý-Ngọ, Sửu-Mùi, ...)\n const groupIndex = branchIndex % 6;\n const hourIndices = AUSPICIOUS_HOURS_TABLE[groupIndex];\n const { earthlyBranches } = t(locale);\n return hourIndices.map((i) => earthlyBranches[i] as string);\n}\n\n/**\n * Lấy index (0-11, theo thứ tự Địa Chi Tý→Hợi) của 6 giờ Hoàng Đạo trong ngày.\n *\n * Dùng khi cần một khoá ổn định, không phụ thuộc ngôn ngữ — ví dụ để map sang\n * icon riêng, hoặc tự tra bảng dịch của bạn thay vì dùng chuỗi tên có sẵn từ\n * {@link getAuspiciousHours}. Cả hai hàm dùng chung thứ tự dữ liệu nên\n * `getAuspiciousHourIndices(jd)[i]` luôn tương ứng với `getAuspiciousHours(jd)[i]`.\n *\n * @param dayJd - Julian Day Number của ngày\n * @returns Mảng 6 index Địa Chi (ví dụ: [0, 1, 3, 6, 7, 9])\n */\nexport function getAuspiciousHourIndices(dayJd: number): number[] {\n const branchIndex = (dayJd + 1) % 12;\n const groupIndex = branchIndex % 6;\n return [...AUSPICIOUS_HOURS_TABLE[groupIndex]];\n}\n","import type { LunarDate } from '../core/types.js';\nimport { t, type Locale } from '../constants/i18n.js';\n\n// ============================================================\n// Tên tháng & ngày truyền thống\n// ============================================================\n\n/** Tên tháng âm lịch truyền thống */\nconst MONTH_NAMES = [\n 'Giêng', 'Hai', 'Ba', 'Tư', 'Năm', 'Sáu',\n 'Bảy', 'Tám', 'Chín', 'Mười', 'Một', 'Chạp',\n] as const;\n\n/** Tên số đếm truyền thống */\nconst DAY_UNITS = [\n '', 'Một', 'Hai', 'Ba', 'Bốn', 'Năm',\n 'Sáu', 'Bảy', 'Tám', 'Chín', 'Mười',\n] as const;\n\n/**\n * Lấy tên tháng âm lịch dạng chữ truyền thống.\n *\n * @param month - Tháng âm lịch (1-12)\n * @returns Tên tháng (ví dụ: 1 → \"Giêng\", 12 → \"Chạp\")\n *\n * @example\n * ```typescript\n * getMonthName(1); // \"Giêng\"\n * getMonthName(12); // \"Chạp\"\n * ```\n */\nexport function getMonthName(month: number): string {\n if (month < 1 || month > 12) {\n throw new RangeError(`Lichta: Month must be between 1 and 12, got ${month}`);\n }\n return MONTH_NAMES[month - 1];\n}\n\n/**\n * Lấy tên ngày âm lịch theo cách gọi truyền thống Việt Nam.\n *\n * - 1-10: \"Mùng Một\", \"Mùng Hai\", ..., \"Mùng Mười\"\n * - 11-20: \"Mười Một\", ..., \"Hai Mươi\"\n * - 21-30: \"Hăm Mốt\", \"Hăm Hai\", ..., \"Ba Mươi\"\n *\n * @param day - Ngày âm lịch (1-30)\n *\n * @example\n * ```typescript\n * getDayName(1); // \"Mùng Một\"\n * getDayName(15); // \"Rằm\"\n * getDayName(30); // \"Ba Mươi\"\n * ```\n */\nexport function getDayName(day: number): string {\n if (day < 1 || day > 30) {\n throw new RangeError(`Lichta: Day must be between 1 and 30, got ${day}`);\n }\n\n // Ngày Rằm\n if (day === 15) return 'Rằm';\n\n // Mùng 1-10\n if (day <= 10) {\n return `Mùng ${DAY_UNITS[day]}`;\n }\n\n // 11-19\n if (day < 20) {\n if (day === 11) return 'Mười Một';\n if (day === 14) return 'Mười Bốn';\n return `Mười ${DAY_UNITS[day - 10]}`;\n }\n\n // 20\n if (day === 20) return 'Hai Mươi';\n\n // 21-29\n if (day < 30) {\n if (day === 21) return 'Hăm Mốt';\n if (day === 24) return 'Hăm Tư';\n if (day === 25) return 'Hăm Lăm';\n return `Hăm ${DAY_UNITS[day - 20]}`;\n }\n\n // 30\n return 'Ba Mươi';\n}\n\n// ============================================================\n// Format functions\n// ============================================================\n\n/**\n * Format ngày âm lịch theo pattern.\n *\n * **Patterns hỗ trợ:**\n * | Token | Mô tả | Ví dụ |\n * |-------|------------------------------------------|----------------|\n * | `dd` | Ngày 2 chữ số | 01, 15, 30 |\n * | `d` | Ngày | 1, 15, 30 |\n * | `MM` | Tháng 2 chữ số | 01, 12 |\n * | `M` | Tháng | 1, 12 |\n * | `yyyy`| Năm 4 chữ số | 2024 |\n * | `yy` | Năm 2 chữ số cuối | 24 |\n * | `L` | Nhãn tháng nhuận theo `locale` (xem tham số `locale`), \"\" nếu không phải tháng nhuận | Nhuận |\n * | `CC` | Can Chi năm | Giáp Thìn |\n * | `DC` | Can Chi ngày | Giáp Tý |\n * | `MC` | Can Chi tháng | Bính Dần |\n *\n * @param lunar - Đối tượng LunarDate\n * @param pattern - Chuỗi pattern\n * @param locale - Ngôn ngữ cho token `L` (mặc định: 'vi', giữ nguyên hành vi cũ — \"Nhuận\")\n *\n * @example\n * ```typescript\n * formatLunarDate(lunar, 'dd/MM/yyyy');\n * // → \"01/01/2024\"\n *\n * formatLunarDate(lunar, 'Ngày d tháng M năm CC');\n * // → \"Ngày 1 tháng 1 năm Giáp Thìn\"\n * ```\n */\nexport function formatLunarDate(lunar: LunarDate, pattern: string, locale: Locale = 'vi'): string {\n let result = pattern;\n\n // Thay thế theo thứ tự dài → ngắn để tránh xung đột (yyyy trước yy, dd trước d, MM trước M)\n result = result.replace(/yyyy/g, String(lunar.year));\n result = result.replace(/yy/g, String(lunar.year).slice(-2));\n result = result.replace(/dd/g, String(lunar.day).padStart(2, '0'));\n result = result.replace(/MM/g, String(lunar.month).padStart(2, '0'));\n result = result.replace(/CC/g, lunar.yearCanChi ?? '');\n result = result.replace(/DC/g, lunar.dayCanChi ?? '');\n result = result.replace(/MC/g, lunar.monthCanChi ?? '');\n result = result.replace(/L/g, lunar.isLeap ? t(locale).leapLabel : '');\n\n // Thay d và M cuối cùng (sau khi dd, MM đã được xử lý, tránh double-replace)\n // Dùng regex negative lookbehind để không match các token đã được thay\n result = result.replace(/(?<![0-9])d(?![0-9d])/g, String(lunar.day));\n result = result.replace(/(?<![0-9])M(?![0-9MC])/g, String(lunar.month));\n\n return result.trim();\n}\n\n/**\n * Format ngày âm lịch theo kiểu truyền thống Việt Nam.\n *\n * @param lunar - Đối tượng LunarDate\n * @returns Chuỗi truyền thống (ví dụ: \"Mùng Một tháng Giêng năm Giáp Thìn\")\n *\n * @example\n * ```typescript\n * formatTraditional(lunar);\n * // → \"Mùng Một tháng Giêng năm Giáp Thìn\"\n * ```\n */\nexport function formatTraditional(lunar: LunarDate): string {\n const dayName = getDayName(lunar.day);\n const monthName = getMonthName(lunar.month);\n const leapSuffix = lunar.isLeap ? ' Nhuận' : '';\n const yearCanChi = lunar.yearCanChi ?? '';\n\n return `${dayName} tháng ${monthName}${leapSuffix} năm ${yearCanChi}`.trim();\n}\n","import { LichTa } from '../core/lunar.js';\nimport type { LunarDate } from '../core/types.js';\n\n/** Một ô ngày trong lưới lịch tháng. */\nexport interface CalendarDayCell {\n solar: Date;\n lunar: LunarDate;\n isToday: boolean;\n isSelected: boolean;\n isCurrentMonth: boolean;\n}\n\nfunction dateKey(date: Date): string {\n return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;\n}\n\n/**\n * Dựng lưới 42 ô (6 tuần x 7 ngày) cho 1 tháng dương lịch, gồm ngày tràn từ\n * tháng trước/sau để lấp đầy tuần đầu/cuối, mỗi ô kèm ngày âm tương ứng.\n *\n * Logic này trước đây bị lặp lại độc lập ở Calendar.tsx/vue/svelte — tách ra\n * đây để có 1 nguồn tính toán duy nhất (tránh 3 bản có thể lệch nhau khi sửa\n * riêng lẻ), dùng chung cho cả Calendar và DatePicker ở mọi framework.\n *\n * @param month - Tháng dương lịch (1-12)\n * @param year - Năm dương lịch\n * @param selectedDate - Ngày đang được chọn (nếu có), dùng để đánh dấu `isSelected`\n */\nexport function getCalendarGrid(\n month: number,\n year: number,\n selectedDate?: Date | null\n): CalendarDayCell[] {\n const grid: CalendarDayCell[] = [];\n const firstDay = new Date(year, month - 1, 1);\n const lastDay = new Date(year, month, 0);\n const startingDayOfWeek = firstDay.getDay();\n const daysInMonth = lastDay.getDate();\n const todayKey = dateKey(new Date());\n const selectedKey = selectedDate ? dateKey(selectedDate) : '';\n const prevMonthLastDay = new Date(year, month - 1, 0).getDate();\n\n const pushCell = (solar: Date, d: number, m: number, y: number, isCurrentMonth: boolean) => {\n grid.push({\n solar,\n lunar: LichTa.toLunar(d, m, y),\n isToday: dateKey(solar) === todayKey,\n isSelected: dateKey(solar) === selectedKey,\n isCurrentMonth,\n });\n };\n\n for (let i = startingDayOfWeek - 1; i >= 0; i--) {\n const d = prevMonthLastDay - i;\n const m = month - 1 < 1 ? 12 : month - 1;\n const y = month - 1 < 1 ? year - 1 : year;\n pushCell(new Date(y, m - 1, d), d, m, y, false);\n }\n\n for (let d = 1; d <= daysInMonth; d++) {\n pushCell(new Date(year, month - 1, d), d, month, year, true);\n }\n\n const remaining = 42 - grid.length;\n for (let d = 1; d <= remaining; d++) {\n const m = month + 1 > 12 ? 1 : month + 1;\n const y = month + 1 > 12 ? year + 1 : year;\n pushCell(new Date(y, m - 1, d), d, m, y, false);\n }\n\n return grid;\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -282,10 +282,83 @@ var LichTa = class {
282
282
  // src/constants/five-elements.ts
283
283
  var FIVE_ELEMENTS = ["Kim", "M\u1ED9c", "Th\u1EE7y", "H\u1ECFa", "Th\u1ED5"];
284
284
 
285
+ // src/constants/i18n.ts
286
+ var translations = {
287
+ vi: {
288
+ heavenlyStems: ["Gi\xE1p", "\u1EA4t", "B\xEDnh", "\u0110inh", "M\u1EADu", "K\u1EF7", "Canh", "T\xE2n", "Nh\xE2m", "Qu\xFD"],
289
+ earthlyBranches: ["T\xFD", "S\u1EEDu", "D\u1EA7n", "M\xE3o", "Th\xECn", "T\u1EF5", "Ng\u1ECD", "M\xF9i", "Th\xE2n", "D\u1EADu", "Tu\u1EA5t", "H\u1EE3i"],
290
+ fiveElements: ["Kim", "M\u1ED9c", "Th\u1EE7y", "H\u1ECFa", "Th\u1ED5"],
291
+ zodiacAnimals: ["Chu\u1ED9t", "Tr\xE2u", "C\u1ECDp", "Th\u1ECF", "R\u1ED3ng", "R\u1EAFn", "Ng\u1EF1a", "D\xEA", "Kh\u1EC9", "G\xE0", "Ch\xF3", "Heo"],
292
+ // @deprecated Ngữ nghĩa không nhất quán giữa các locale (xem lunarMonthNames/solarMonthNames).
293
+ monthNames: ["Gi\xEAng", "Hai", "Ba", "T\u01B0", "N\u0103m", "S\xE1u", "B\u1EA3y", "T\xE1m", "Ch\xEDn", "M\u01B0\u1EDDi", "M\u1ED9t", "Ch\u1EA1p"],
294
+ lunarMonthNames: ["Gi\xEAng", "Hai", "Ba", "T\u01B0", "N\u0103m", "S\xE1u", "B\u1EA3y", "T\xE1m", "Ch\xEDn", "M\u01B0\u1EDDi", "M\u1ED9t", "Ch\u1EA1p"],
295
+ solarMonthNames: ["Th\xE1ng 1", "Th\xE1ng 2", "Th\xE1ng 3", "Th\xE1ng 4", "Th\xE1ng 5", "Th\xE1ng 6", "Th\xE1ng 7", "Th\xE1ng 8", "Th\xE1ng 9", "Th\xE1ng 10", "Th\xE1ng 11", "Th\xE1ng 12"],
296
+ weekDays: ["CN", "T2", "T3", "T4", "T5", "T6", "T7"],
297
+ leapLabel: "Nhu\u1EADn",
298
+ yearLabel: "N\u0103m",
299
+ monthLabel: "Th\xE1ng",
300
+ destiny: "M\u1EC7nh"
301
+ },
302
+ en: {
303
+ heavenlyStems: ["Gi\xE1p", "\u1EA4t", "B\xEDnh", "\u0110inh", "M\u1EADu", "K\u1EF7", "Canh", "T\xE2n", "Nh\xE2m", "Qu\xFD"],
304
+ earthlyBranches: ["T\xFD", "S\u1EEDu", "D\u1EA7n", "M\xE3o", "Th\xECn", "T\u1EF5", "Ng\u1ECD", "M\xF9i", "Th\xE2n", "D\u1EADu", "Tu\u1EA5t", "H\u1EE3i"],
305
+ fiveElements: ["Metal", "Wood", "Water", "Fire", "Earth"],
306
+ zodiacAnimals: ["Rat", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Goat", "Monkey", "Rooster", "Dog", "Pig"],
307
+ // @deprecated Ngữ nghĩa không nhất quán giữa các locale (xem lunarMonthNames/solarMonthNames).
308
+ monthNames: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
309
+ lunarMonthNames: ["Month 1", "Month 2", "Month 3", "Month 4", "Month 5", "Month 6", "Month 7", "Month 8", "Month 9", "Month 10", "Month 11", "Month 12"],
310
+ solarMonthNames: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
311
+ weekDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
312
+ leapLabel: "Leap",
313
+ yearLabel: "Year",
314
+ monthLabel: "Month",
315
+ destiny: "Destiny"
316
+ },
317
+ ja: {
318
+ heavenlyStems: ["\u7532", "\u4E59", "\u4E19", "\u4E01", "\u620A", "\u5DF1", "\u5E9A", "\u8F9B", "\u58EC", "\u7678"],
319
+ earthlyBranches: ["\u5B50", "\u4E11", "\u5BC5", "\u536F", "\u8FB0", "\u5DF3", "\u5348", "\u672A", "\u7533", "\u9149", "\u620C", "\u4EA5"],
320
+ fiveElements: ["\u91D1", "\u6728", "\u6C34", "\u706B", "\u571F"],
321
+ zodiacAnimals: ["\u9F20", "\u725B", "\u864E", "\u514E", "\u7ADC", "\u86C7", "\u99AC", "\u7F8A", "\u733F", "\u9D8F", "\u72AC", "\u732A"],
322
+ // @deprecated Ngữ nghĩa không nhất quán giữa các locale (xem lunarMonthNames/solarMonthNames).
323
+ monthNames: ["1\u6708", "2\u6708", "3\u6708", "4\u6708", "5\u6708", "6\u6708", "7\u6708", "8\u6708", "9\u6708", "10\u6708", "11\u6708", "12\u6708"],
324
+ lunarMonthNames: ["\u65E7\u66A61\u6708", "\u65E7\u66A62\u6708", "\u65E7\u66A63\u6708", "\u65E7\u66A64\u6708", "\u65E7\u66A65\u6708", "\u65E7\u66A66\u6708", "\u65E7\u66A67\u6708", "\u65E7\u66A68\u6708", "\u65E7\u66A69\u6708", "\u65E7\u66A610\u6708", "\u65E7\u66A611\u6708", "\u65E7\u66A612\u6708"],
325
+ solarMonthNames: ["1\u6708", "2\u6708", "3\u6708", "4\u6708", "5\u6708", "6\u6708", "7\u6708", "8\u6708", "9\u6708", "10\u6708", "11\u6708", "12\u6708"],
326
+ weekDays: ["\u65E5", "\u6708", "\u706B", "\u6C34", "\u6728", "\u91D1", "\u571F"],
327
+ leapLabel: "\u958F",
328
+ yearLabel: "\u5E74",
329
+ monthLabel: "\u6708",
330
+ destiny: "\u547D"
331
+ },
332
+ ko: {
333
+ heavenlyStems: ["\uAC11", "\uC744", "\uBCD1", "\uC815", "\uBB34", "\uAE30", "\uACBD", "\uC2E0", "\uC784", "\uACC4"],
334
+ earthlyBranches: ["\uC790", "\uCD95", "\uC778", "\uBB18", "\uC9C4", "\uC0AC", "\uC624", "\uBBF8", "\uC2E0", "\uC720", "\uC220", "\uD574"],
335
+ fiveElements: ["\uAE08", "\uBAA9", "\uC218", "\uD654", "\uD1A0"],
336
+ zodiacAnimals: ["\uC950", "\uC18C", "\uD638\uB791\uC774", "\uD1A0\uB07C", "\uC6A9", "\uBC40", "\uB9D0", "\uC591", "\uC6D0\uC22D\uC774", "\uB2ED", "\uAC1C", "\uB3FC\uC9C0"],
337
+ // @deprecated Ngữ nghĩa không nhất quán giữa các locale (xem lunarMonthNames/solarMonthNames).
338
+ monthNames: ["1\uC6D4", "2\uC6D4", "3\uC6D4", "4\uC6D4", "5\uC6D4", "6\uC6D4", "7\uC6D4", "8\uC6D4", "9\uC6D4", "10\uC6D4", "11\uC6D4", "12\uC6D4"],
339
+ lunarMonthNames: ["\uC74C\uB825 1\uC6D4", "\uC74C\uB825 2\uC6D4", "\uC74C\uB825 3\uC6D4", "\uC74C\uB825 4\uC6D4", "\uC74C\uB825 5\uC6D4", "\uC74C\uB825 6\uC6D4", "\uC74C\uB825 7\uC6D4", "\uC74C\uB825 8\uC6D4", "\uC74C\uB825 9\uC6D4", "\uC74C\uB825 10\uC6D4", "\uC74C\uB825 11\uC6D4", "\uC74C\uB825 12\uC6D4"],
340
+ solarMonthNames: ["1\uC6D4", "2\uC6D4", "3\uC6D4", "4\uC6D4", "5\uC6D4", "6\uC6D4", "7\uC6D4", "8\uC6D4", "9\uC6D4", "10\uC6D4", "11\uC6D4", "12\uC6D4"],
341
+ weekDays: ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"],
342
+ leapLabel: "\uC724",
343
+ yearLabel: "\uB144",
344
+ monthLabel: "\uC6D4",
345
+ destiny: "\uBA85"
346
+ }
347
+ };
348
+ function t(locale) {
349
+ return translations[locale];
350
+ }
351
+ function getZodiacAnimal(branchIndex, locale = "vi") {
352
+ return translations[locale].zodiacAnimals[branchIndex % 12];
353
+ }
354
+
285
355
  // src/core/feng-shui.ts
356
+ function mod(n, m) {
357
+ return (n % m + m) % m;
358
+ }
286
359
  function getYearDetails(year) {
287
- const stemIndex = (year - 4) % 10;
288
- const branchIndex = (year - 4) % 12;
360
+ const stemIndex = mod(year - 4, 10);
361
+ const branchIndex = mod(year - 4, 12);
289
362
  const stemWeight = getStemWeight(stemIndex);
290
363
  const branchWeight = getBranchWeight(branchIndex);
291
364
  let elementValue = stemWeight + branchWeight;
@@ -300,24 +373,27 @@ function getYearDetails(year) {
300
373
  fullString: `${HEAVENLY_STEMS[stemIndex]} ${EARTHLY_BRANCHES[branchIndex]} - M\u1EC7nh ${FIVE_ELEMENTS[elementIndex]}`
301
374
  };
302
375
  }
303
- function getDayCanChi(jd) {
376
+ function getDayCanChi(jd, locale = "vi") {
304
377
  const stemIndex = (jd + 9) % 10;
305
378
  const branchIndex = (jd + 1) % 12;
306
- return `${HEAVENLY_STEMS[stemIndex]} ${EARTHLY_BRANCHES[branchIndex]}`;
379
+ const { heavenlyStems, earthlyBranches } = t(locale);
380
+ return `${heavenlyStems[stemIndex]} ${earthlyBranches[branchIndex]}`;
307
381
  }
308
- function getMonthCanChi(lunarMonth, lunarYear) {
382
+ function getMonthCanChi(lunarMonth, lunarYear, locale = "vi") {
309
383
  const yearStemIndex = (lunarYear + 6) % 10;
310
384
  const monthStemOffset = yearStemIndex % 5 * 2 + 2;
311
385
  const monthStemIndex = (monthStemOffset + lunarMonth - 1) % 10;
312
386
  const monthBranchIndex = (lunarMonth + 1) % 12;
313
- return `${HEAVENLY_STEMS[monthStemIndex]} ${EARTHLY_BRANCHES[monthBranchIndex]}`;
387
+ const { heavenlyStems, earthlyBranches } = t(locale);
388
+ return `${heavenlyStems[monthStemIndex]} ${earthlyBranches[monthBranchIndex]}`;
314
389
  }
315
- function getHourCanChi(hour, dayJd) {
390
+ function getHourCanChi(hour, dayJd, locale = "vi") {
316
391
  const hourBranchIndex = Math.floor((hour + 1) / 2) % 12;
317
392
  const dayStemIndex = (dayJd + 9) % 10;
318
393
  const hourStemOffset = dayStemIndex % 5 * 2;
319
394
  const hourStemIndex = (hourStemOffset + hourBranchIndex) % 10;
320
- return `${HEAVENLY_STEMS[hourStemIndex]} ${EARTHLY_BRANCHES[hourBranchIndex]}`;
395
+ const { heavenlyStems, earthlyBranches } = t(locale);
396
+ return `${heavenlyStems[hourStemIndex]} ${earthlyBranches[hourBranchIndex]}`;
321
397
  }
322
398
  var AUSPICIOUS_HOURS_TABLE = [
323
399
  [0, 1, 3, 6, 7, 9],
@@ -333,11 +409,17 @@ var AUSPICIOUS_HOURS_TABLE = [
333
409
  [2, 5, 6, 8, 11, 0]
334
410
  // Ngày Tỵ (5) / Hợi (11)
335
411
  ];
336
- function getAuspiciousHours(dayJd) {
412
+ function getAuspiciousHours(dayJd, locale = "vi") {
337
413
  const branchIndex = (dayJd + 1) % 12;
338
414
  const groupIndex = branchIndex % 6;
339
415
  const hourIndices = AUSPICIOUS_HOURS_TABLE[groupIndex];
340
- return hourIndices.map((i) => EARTHLY_BRANCHES[i]);
416
+ const { earthlyBranches } = t(locale);
417
+ return hourIndices.map((i) => earthlyBranches[i]);
418
+ }
419
+ function getAuspiciousHourIndices(dayJd) {
420
+ const branchIndex = (dayJd + 1) % 12;
421
+ const groupIndex = branchIndex % 6;
422
+ return [...AUSPICIOUS_HOURS_TABLE[groupIndex]];
341
423
  }
342
424
 
343
425
  // src/utils/format.ts
@@ -396,7 +478,7 @@ function getDayName(day) {
396
478
  }
397
479
  return "Ba M\u01B0\u01A1i";
398
480
  }
399
- function formatLunarDate(lunar, pattern) {
481
+ function formatLunarDate(lunar, pattern, locale = "vi") {
400
482
  let result = pattern;
401
483
  result = result.replace(/yyyy/g, String(lunar.year));
402
484
  result = result.replace(/yy/g, String(lunar.year).slice(-2));
@@ -405,7 +487,7 @@ function formatLunarDate(lunar, pattern) {
405
487
  result = result.replace(/CC/g, lunar.yearCanChi ?? "");
406
488
  result = result.replace(/DC/g, lunar.dayCanChi ?? "");
407
489
  result = result.replace(/MC/g, lunar.monthCanChi ?? "");
408
- result = result.replace(/L/g, lunar.isLeap ? "Nhu\u1EADn" : "");
490
+ result = result.replace(/L/g, lunar.isLeap ? t(locale).leapLabel : "");
409
491
  result = result.replace(/(?<![0-9])d(?![0-9d])/g, String(lunar.day));
410
492
  result = result.replace(/(?<![0-9])M(?![0-9MC])/g, String(lunar.month));
411
493
  return result.trim();
@@ -418,68 +500,52 @@ function formatTraditional(lunar) {
418
500
  return `${dayName} th\xE1ng ${monthName}${leapSuffix} n\u0103m ${yearCanChi}`.trim();
419
501
  }
420
502
 
421
- // src/constants/i18n.ts
422
- var translations = {
423
- vi: {
424
- heavenlyStems: ["Gi\xE1p", "\u1EA4t", "B\xEDnh", "\u0110inh", "M\u1EADu", "K\u1EF7", "Canh", "T\xE2n", "Nh\xE2m", "Qu\xFD"],
425
- earthlyBranches: ["T\xFD", "S\u1EEDu", "D\u1EA7n", "M\xE3o", "Th\xECn", "T\u1EF5", "Ng\u1ECD", "M\xF9i", "Th\xE2n", "D\u1EADu", "Tu\u1EA5t", "H\u1EE3i"],
426
- fiveElements: ["Kim", "M\u1ED9c", "Th\u1EE7y", "H\u1ECFa", "Th\u1ED5"],
427
- zodiacAnimals: ["Chu\u1ED9t", "Tr\xE2u", "C\u1ECDp", "Th\u1ECF", "R\u1ED3ng", "R\u1EAFn", "Ng\u1EF1a", "D\xEA", "Kh\u1EC9", "G\xE0", "Ch\xF3", "Heo"],
428
- monthNames: ["Gi\xEAng", "Hai", "Ba", "T\u01B0", "N\u0103m", "S\xE1u", "B\u1EA3y", "T\xE1m", "Ch\xEDn", "M\u01B0\u1EDDi", "M\u1ED9t", "Ch\u1EA1p"],
429
- weekDays: ["CN", "T2", "T3", "T4", "T5", "T6", "T7"],
430
- leapLabel: "Nhu\u1EADn",
431
- yearLabel: "N\u0103m",
432
- monthLabel: "Th\xE1ng",
433
- destiny: "M\u1EC7nh"
434
- },
435
- en: {
436
- heavenlyStems: ["Gi\xE1p", "\u1EA4t", "B\xEDnh", "\u0110inh", "M\u1EADu", "K\u1EF7", "Canh", "T\xE2n", "Nh\xE2m", "Qu\xFD"],
437
- earthlyBranches: ["T\xFD", "S\u1EEDu", "D\u1EA7n", "M\xE3o", "Th\xECn", "T\u1EF5", "Ng\u1ECD", "M\xF9i", "Th\xE2n", "D\u1EADu", "Tu\u1EA5t", "H\u1EE3i"],
438
- fiveElements: ["Metal", "Wood", "Water", "Fire", "Earth"],
439
- zodiacAnimals: ["Rat", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Goat", "Monkey", "Rooster", "Dog", "Pig"],
440
- monthNames: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
441
- weekDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
442
- leapLabel: "Leap",
443
- yearLabel: "Year",
444
- monthLabel: "Month",
445
- destiny: "Destiny"
446
- },
447
- ja: {
448
- heavenlyStems: ["\u7532", "\u4E59", "\u4E19", "\u4E01", "\u620A", "\u5DF1", "\u5E9A", "\u8F9B", "\u58EC", "\u7678"],
449
- earthlyBranches: ["\u5B50", "\u4E11", "\u5BC5", "\u536F", "\u8FB0", "\u5DF3", "\u5348", "\u672A", "\u7533", "\u9149", "\u620C", "\u4EA5"],
450
- fiveElements: ["\u91D1", "\u6728", "\u6C34", "\u706B", "\u571F"],
451
- zodiacAnimals: ["\u9F20", "\u725B", "\u864E", "\u514E", "\u7ADC", "\u86C7", "\u99AC", "\u7F8A", "\u733F", "\u9D8F", "\u72AC", "\u732A"],
452
- monthNames: ["1\u6708", "2\u6708", "3\u6708", "4\u6708", "5\u6708", "6\u6708", "7\u6708", "8\u6708", "9\u6708", "10\u6708", "11\u6708", "12\u6708"],
453
- weekDays: ["\u65E5", "\u6708", "\u706B", "\u6C34", "\u6728", "\u91D1", "\u571F"],
454
- leapLabel: "\u958F",
455
- yearLabel: "\u5E74",
456
- monthLabel: "\u6708",
457
- destiny: "\u547D"
458
- },
459
- ko: {
460
- heavenlyStems: ["\uAC11", "\uC744", "\uBCD1", "\uC815", "\uBB34", "\uAE30", "\uACBD", "\uC2E0", "\uC784", "\uACC4"],
461
- earthlyBranches: ["\uC790", "\uCD95", "\uC778", "\uBB18", "\uC9C4", "\uC0AC", "\uC624", "\uBBF8", "\uC2E0", "\uC720", "\uC220", "\uD574"],
462
- fiveElements: ["\uAE08", "\uBAA9", "\uC218", "\uD654", "\uD1A0"],
463
- zodiacAnimals: ["\uC950", "\uC18C", "\uD638\uB791\uC774", "\uD1A0\uB07C", "\uC6A9", "\uBC40", "\uB9D0", "\uC591", "\uC6D0\uC22D\uC774", "\uB2ED", "\uAC1C", "\uB3FC\uC9C0"],
464
- monthNames: ["1\uC6D4", "2\uC6D4", "3\uC6D4", "4\uC6D4", "5\uC6D4", "6\uC6D4", "7\uC6D4", "8\uC6D4", "9\uC6D4", "10\uC6D4", "11\uC6D4", "12\uC6D4"],
465
- weekDays: ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"],
466
- leapLabel: "\uC724",
467
- yearLabel: "\uB144",
468
- monthLabel: "\uC6D4",
469
- destiny: "\uBA85"
470
- }
471
- };
472
- function t(locale) {
473
- return translations[locale];
503
+ // src/utils/calendar-grid.ts
504
+ function dateKey(date) {
505
+ return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
474
506
  }
475
- function getZodiacAnimal(branchIndex, locale = "vi") {
476
- return translations[locale].zodiacAnimals[branchIndex % 12];
507
+ function getCalendarGrid(month, year, selectedDate) {
508
+ const grid = [];
509
+ const firstDay = new Date(year, month - 1, 1);
510
+ const lastDay = new Date(year, month, 0);
511
+ const startingDayOfWeek = firstDay.getDay();
512
+ const daysInMonth = lastDay.getDate();
513
+ const todayKey = dateKey(/* @__PURE__ */ new Date());
514
+ const selectedKey = selectedDate ? dateKey(selectedDate) : "";
515
+ const prevMonthLastDay = new Date(year, month - 1, 0).getDate();
516
+ const pushCell = (solar, d, m, y, isCurrentMonth) => {
517
+ grid.push({
518
+ solar,
519
+ lunar: LichTa.toLunar(d, m, y),
520
+ isToday: dateKey(solar) === todayKey,
521
+ isSelected: dateKey(solar) === selectedKey,
522
+ isCurrentMonth
523
+ });
524
+ };
525
+ for (let i = startingDayOfWeek - 1; i >= 0; i--) {
526
+ const d = prevMonthLastDay - i;
527
+ const m = month - 1 < 1 ? 12 : month - 1;
528
+ const y = month - 1 < 1 ? year - 1 : year;
529
+ pushCell(new Date(y, m - 1, d), d, m, y, false);
530
+ }
531
+ for (let d = 1; d <= daysInMonth; d++) {
532
+ pushCell(new Date(year, month - 1, d), d, month, year, true);
533
+ }
534
+ const remaining = 42 - grid.length;
535
+ for (let d = 1; d <= remaining; d++) {
536
+ const m = month + 1 > 12 ? 1 : month + 1;
537
+ const y = month + 1 > 12 ? year + 1 : year;
538
+ pushCell(new Date(y, m - 1, d), d, m, y, false);
539
+ }
540
+ return grid;
477
541
  }
478
542
  export {
479
543
  LichTa,
480
544
  formatLunarDate,
481
545
  formatTraditional,
546
+ getAuspiciousHourIndices,
482
547
  getAuspiciousHours,
548
+ getCalendarGrid,
483
549
  getDayCanChi,
484
550
  getDayName,
485
551
  getHourCanChi,