欢迎来到奇葩栖息地!欢迎加入Discord服务器:XfrfHCzfbW请先至特殊:参数设置验证邮箱后再进行编辑。特殊:参数设置挑选自己想要使用的小工具!不会编辑?请至这里学习Wikitext语法。

模块:Chinese calendar:修订间差异

来自奇葩栖息地
添加的内容 删除的内容
无编辑摘要
(//Edit via InPageEdit)
 
(未显示同一用户的21个中间版本)
第1行: 第1行:
local p = {}
local p = {}


-- 农历数据表
local function extract(num, pos, len)
local lunarInfo = {
local shifted = math.floor(num / (2 ^ pos))
0x04bd8,
local result = shifted % (2 ^ len)
return result
0x04ae0,
0x0a570,
end
0x054d5,
0x0d260,
0x0d950,
0x16554,
0x056a0,
0x09ad0,
0x055d2,
0x04ae0,
0x0a5b6,
0x0a4d0,
0x0d250,
0x1d255,
0x0b540,
0x0d6a0,
0x0ada2,
0x095b0,
0x14977,
0x04970,
0x0a4b0,
0x0b4b5,
0x06a50,
0x06d40,
0x1ab54,
0x02b60,
0x09570,
0x052f2,
0x04970,
0x06566,
0x0d4a0,
0x0ea50,
0x06e95,
0x05ad0,
0x02b60,
0x186e3,
0x092e0,
0x1c8d7,
0x0c950,
0x0d4a0,
0x1d8a6,
0x0b550,
0x056a0,
0x1a5b4,
0x025d0,
0x092d0,
0x0d2b2,
0x0a950,
0x0b557,
0x06ca0,
0x0b550,
0x15355,
0x04da0,
0x0a5b0,
0x14573,
0x052b0,
0x0a9a8,
0x0e950,
0x06aa0,
0x0aea6,
0x0ab50,
0x04b60,
0x0aae4,
0x0a570,
0x05260,
0x0f263,
0x0d950,
0x05b57,
0x056a0,
0x096d0,
0x04dd5,
0x04ad0,
0x0a4d0,
0x0d4d4,
0x0d250,
0x0d558,
0x0b540,
0x0b6a0,
0x195a6,
0x095b0,
0x049b0,
0x0a974,
0x0a4b0,
0x0b27a,
0x06a50,
0x06d40,
0x0af46,
0x0ab60,
0x09570,
0x04af5,
0x04970,
0x064b0,
0x074a3,
0x0ea50,
0x06b58,
0x055c0,
0x0ab60,
0x096d5,
0x092e0,
0x0c960,
0x0d954,
0x0d4a0,
0x0da50,
0x07552,
0x056a0,
0x0abb7,
0x025d0,
0x092d0,
0x0cab5,
0x0a950,
0x0b4a0,
0x0baa4,
0x0ad50,
0x055d9,
0x04ba0,
0x0a5b0,
0x15176,
0x052b0,
0x0a930,
0x07954,
0x06aa0,
0x0ad50,
0x05b52,
0x04b60,
0x0a6e6,
0x0a4e0,
0x0d260,
0x0ea65,
0x0d530,
0x05aa0,
0x076a3,
0x096d0,
0x04afb,
0x04ad0,
0x0a4d0,
0x1d0b6,
0x0d250,
0x0d520,
0x0dd45,
0x0b5a0,
0x056d0,
0x055b2,
0x049b0,
0x0a577,
0x0a4b0,
0x0aa50,
0x1b255,
0x06d20,
0x0ada0,
0x14b63,
0x09370,
0x049f8,
0x04970,
0x064b0,
0x168a6,
0x0ea50,
0x06b20,
0x1a6c4,
0x0aae0,
0x0a2e0,
0x0d2e3,
0x0c960,
0x0d557,
0x0d4a0,
0x0da50,
0x05d55,
0x056a0,
0x0a6d0,
0x055d4,
0x052d0,
0x0a9b8,
0x0a950,
0x0b4a0,
0x0b6a6,
0x0ad50,
0x055a0,
0x0aba4,
0x0a5b0,
0x052b0,
0x0b273,
0x06930,
0x07337,
0x06aa0,
0x0ad50,
0x14b55,
0x04b60,
0x0a570,
0x054e4,
0x0d160,
0x0e968,
0x0d520,
0x0daa0,
0x16aa6,
0x056d0,
0x04ae0,
0x0a9d4,
0x0a2d0,
0x0d150,
0x0f252,
0x0d520
}


-- 天干
-- 天干
local HEAVENLY_STEMS = { '', '', '', '', '', '', '', '', '', '' }
local Gan = { "", "", "", "", "", "", "", "", "", "" }

-- 地支
-- 地支
local EARTHLY_BRANCHES = { '', '', '', '', '', '', '', '', '', '', '', '' }
local Zhi = { "", "", "", "", "", "", "", "", "", "", "", "" }

-- 月份
-- 月份
local LUNAR_MONTHS = { '', '', '', '', '', '', '', '', '', '', '', '' }
local nStr3 = { "", "", "", "", "", "", "", "", "", "", "", "" }

-- 日期
-- 日期
local LUNAR_DAYS = {
local nStr2 = { "初", "十", "廿", "卅" }
'初', '初', '初', '初', '初', '初', '初', '初', '初', '初',
local nStr1 = { "日", "", "", "", "", "", "", "", "", "", "" }
'十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十',
'廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十'
}


-- 实现位运算函数
-- 农历数据 (1900-2100)
local LUNAR_INFO = {
local function band(a, b)
if not a or not b then
0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
return 0
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,
end
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8, 0x0e950, 0x06aa0,
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6,
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,
0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0
}


-- Localize math.floor for performance
-- 节气数据(每个节气的日期偏移量)
local SOLAR_TERMS_OFFSET = {
local floor = math.floor
local result = 0
4, 19, 3, 18, 4, 19, 4, 19, 4, 20, 4, 20, 6, 22, 6, 22,
local bitval = 1
6, 22, 7, 22, 6, 21, 6, 21
}


-- 处理负数或0的情况
-- 节气名称
if a <= 0 or b <= 0 then
local SOLAR_TERMS = {
return 0
'小寒', '大寒', '立春', '雨水', '惊蛰', '春分',
end
'清明', '谷雨', '立夏', '小满', '芒种', '夏至',
'小暑', '大暑', '立秋', '处暑', '白露', '秋分',
'寒露', '霜降', '立冬', '小雪', '大雪', '冬至'
}


while a > 0 and b > 0 do
-- 检查日期是否合法
if a % 2 == 1 and b % 2 == 1 then
local function isValidDate(year, month, day)
local days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
result = result + bitval
end
if (year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0) then

days[2] = 29
bitval = bitval * 2
a = floor(a/2)
b = floor(b/2)
end
end

return day <= days[month]
return result
end
end


local function rshift(a, b)
-- 计算指定公历日期是一年中的第几天
if not a or not b then
local function getDayOfYear(year, month, day)
return 0
local days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
if (year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0) then
days[2] = 29
end
end


local dayOfYear = day
if a <= 0 then
return 0
for i = 1, month - 1 do
dayOfYear = dayOfYear + days[i]
end
end
return dayOfYear
end


if b < 0 then
-- 获取某年农历数据中的月份大小信息
return a
local function getMonthSizes(lunarCode)
local monthSizes = {}
for i = 1, 12 do
monthSizes[i] = extract(lunarCode, 12 - i, 1) == 1 and 30 or 29
end
end
return monthSizes
end


return math.floor(a / (2^b))
-- 获取闰月信息
local function getLeapMonth(lunarCode)
return extract(lunarCode, 12, 4)
end
end


-- 计算农历闰月月份
-- 获取春节日期
local function getSpringFestival(lunarCode)
local function leapMonth(y)
if y < 1900 or y > 2100 then
local month = extract(lunarCode, 16, 2) + 1
return 0
local day = extract(lunarCode, 18, 5)
end
return month, day

-- Lua table index starts from 1
local index = y - 1900 + 1

-- Lua table index starts from 1
if index <= 0 or index > #lunarInfo then
return 0
end

return band(lunarInfo[index], 0xf)
end
end


-- 计算节气
-- 计算农历闰月天数
local function getSolarTerm(year, month, day)
local function leapDays(y)
local baseYear = 1900
if y < 1900 or y > 2100 then
return 0
local baseDay = 6 -- 1900年的小寒基准日
end
local yearDays = 365.242 -- 回归年长度


local m = leapMonth(y)
-- 计算与基准年份的差距
local yearDiff = year - baseYear
local termIndex = (month - 1) * 2


if m == 0 then
-- 计算节气理论日期
return 0
local theoreticalDay = math.floor(yearDiff * yearDays + baseDay + SOLAR_TERMS_OFFSET[termIndex + 1])
end


-- Lua table index starts from 1
-- 如果当前日期恰好是节气日期
if day == theoreticalDay % 31 then
if band(lunarInfo[y - 1900 + 1], 0x10000) ~= 0 then
return SOLAR_TERMS[termIndex + 1]
return 30
end
end


return nil
return 29
end
end


-- 计算农历日期
-- 计算农历年天数
local function lYearDays(y)
function calculateLunarDate(yearCode, year, month, day)
if y < 1900 or y > 2100 then
-- 获取春节日期
return 0
local sfMonth, sfDay = getSpringFestival(yearCode)
end


local sum = 348
-- 计算目标日期和春节的天数差
local targetDayOfYear = getDayOfYear(year, month, day)
local i = 0x8000
-- Lua table index starts from 1
local springFestivalDayOfYear = getDayOfYear(year, sfMonth, sfDay)
local dayDiff = targetDayOfYear - springFestivalDayOfYear
local idx = y - 1900 + 1


-- Lua table index starts from 1
-- 如果在春节前,则属于上一个农历年
if dayDiff < 0 then
if idx <= 0 or idx > #lunarInfo then
return 0
if year > 1900 then
local prevYearCode = LUNAR_INFO[year - 1900]
local prevMonthSizes = getMonthSizes(prevYearCode)
local totalDays = dayDiff + prevMonthSizes[12]
return 12, totalDays + 1, false, year - 1
else
return 12, 30 + dayDiff, false, year - 1
end
end
end


while i > 0x8 do
-- 获取当年农历月份大小
if band(lunarInfo[idx], i) ~= 0 then
local monthSizes = getMonthSizes(yearCode)
sum = sum + 1
local leapMonth = getLeapMonth(yearCode)
end


i = rshift(i, 1)
-- 计算农历月份和日期
end
local days = dayDiff
local lunarMonth = 1
local isLeapMonth = false


return sum + leapDays(y)
while days >= monthSizes[lunarMonth] do
end
days = days - monthSizes[lunarMonth]


-- 计算农历月份天数
if lunarMonth == leapMonth then
local function monthDays(y, m)
if not isLeapMonth then
isLeapMonth = true
if y < 1900 or y > 2100 then
return 0
if days >= monthSizes[lunarMonth] then
end
days = days - monthSizes[lunarMonth]
lunarMonth = lunarMonth + 1
end
else
lunarMonth = lunarMonth + 1
isLeapMonth = false
end
else
lunarMonth = lunarMonth + 1
end


if lunarMonth > 12 then
if m > 12 or m < 1 then
return 1, days + 1, false, year + 1
return 0
end
end

-- Lua table index starts from 1
if band(lunarInfo[y - 1900 + 1], rshift(0x10000, m)) ~= 0 then
return 30
end
end


return lunarMonth, days + 1, isLeapMonth, year
return 29
end
end


-- 将公历日期转换为农历日期
-- 主要的转换函数
function p.toLunar(frame)
function p.toLunar(frame)
-- 获取输入参数
-- 解析输入日期
local date = frame.args[1]
local date = frame.args[1] or frame:getParent().args.date
local showSolarTerm = frame.args.showSolarTerm or false


local year = tonumber(date:sub(1, 4))
if not date then
return "日期参数错误"
local month = tonumber(date:sub(6, 7))
end
local day = tonumber(date:sub(9, 10))


local y, m, d = date:match("^(%d%d%d%d)-(%d%d?)-(%d%d?)$")
-- 输入范围验证

if year < 1900 or year > 2100 then
if not y or not m or not d then
return '错误:年份超出范围(1900-2100)'
return "日期格式错误"
end
end

if month < 1 or month > 12 then
y, m, d = tonumber(y), tonumber(m), tonumber(d)
return '错误:月份无效'

-- 验证日期范围
if y < 1900 or y > 2100 then
return "年份超出范围(1900-2100)"
end
end

if not isValidDate(year, month, day) then
return '错误:日期无效'
if m < 1 or m > 12 then
return "月份错误"
end
end

if d < 1 or d > 31 then
return "日期错误"
end

-- 计算距离1900年1月31日的天数
local baseDate = os.time({ year = 1900, month = 1, day = 31 })
local targetDate = os.time({ year = y, month = m, day = d })
local offset = math.floor((targetDate - baseDate) / (24 * 60 * 60))


-- 计算农历年
-- 计算农历年
local yearCode = LUNAR_INFO[year - 1900 + 1]
local ly = 1900
local temp = 0


while ly < 2101 and offset > 0 do
-- 计算农历月份和日期
temp = lYearDays(ly)
local lunarMonth, lunarDay, isLeapMonth, lunarYear = calculateLunarDate(yearCode, year, month, day)
offset = offset - temp
ly = ly + 1
end


if offset < 0 then
-- 获取天干地支
offset = offset + temp
local stemIndex = (lunarYear - 4) % 10
local branchIndex = (lunarYear - 4) % 12
ly = ly - 1
end


-- 构造输出字符串
-- 计算农历月日
local lm = 1
local result = HEAVENLY_STEMS[stemIndex + 1] .. EARTHLY_BRANCHES[branchIndex + 1] .. '年'
if isLeapMonth then
local ld = 1
result = result .. '闰'
local isLeap = false
local leap = leapMonth(ly)

-- 计算月
while offset > 0 and lm < 13 do
if leap > 0 and lm == leap + 1 and not isLeap then
lm = lm - 1
isLeap = true
temp = leapDays(ly)
else
temp = monthDays(ly, lm)
end

offset = offset - temp

if isLeap and lm == leap + 1 then
isLeap = false
end

lm = lm + 1
end
end
result = result .. LUNAR_MONTHS[lunarMonth] .. '月' .. LUNAR_DAYS[lunarDay]


-- 如果需要显示节气
-- 计算日
if showSolarTerm then
if offset == 0 and leap > 0 and lm == leap + 1 then
if isLeap then
local solarTerm = getSolarTerm(year, month, day)
if solarTerm then
isLeap = false
else
result = result .. ' (' .. solarTerm .. ')'
isLeap = true
lm = lm - 1
end
end
end
end


if offset < 0 then
return result
offset = offset + temp
lm = lm - 1
end

ld = offset + 1

-- 格式化输出
local gzYear = Gan[((ly - 4) % 10) + 1] .. Zhi[((ly - 4) % 12) + 1]
local lMonth = nStr3[lm]
local lDay

if ld == 10 then
lDay = "初十"
elseif ld == 20 then
lDay = "二十"
elseif ld == 30 then
lDay = "三十"
else
lDay = nStr2[math.floor(ld / 10) + 1] .. nStr1[ld % 10 + 1]
end

return gzYear .. "年" .. lMonth .. "月" .. lDay
end
end



2025年1月9日 (四) 11:55的最新版本

[创建 | 历史 | 清除缓存]文档页面
此模块没有文档页面。如果你知道此模块的使用方法,请帮助为其创建文档页面。
local p = {}

-- 农历数据表
local lunarInfo = {
    0x04bd8,
    0x04ae0,
    0x0a570,
    0x054d5,
    0x0d260,
    0x0d950,
    0x16554,
    0x056a0,
    0x09ad0,
    0x055d2,
    0x04ae0,
    0x0a5b6,
    0x0a4d0,
    0x0d250,
    0x1d255,
    0x0b540,
    0x0d6a0,
    0x0ada2,
    0x095b0,
    0x14977,
    0x04970,
    0x0a4b0,
    0x0b4b5,
    0x06a50,
    0x06d40,
    0x1ab54,
    0x02b60,
    0x09570,
    0x052f2,
    0x04970,
    0x06566,
    0x0d4a0,
    0x0ea50,
    0x06e95,
    0x05ad0,
    0x02b60,
    0x186e3,
    0x092e0,
    0x1c8d7,
    0x0c950,
    0x0d4a0,
    0x1d8a6,
    0x0b550,
    0x056a0,
    0x1a5b4,
    0x025d0,
    0x092d0,
    0x0d2b2,
    0x0a950,
    0x0b557,
    0x06ca0,
    0x0b550,
    0x15355,
    0x04da0,
    0x0a5b0,
    0x14573,
    0x052b0,
    0x0a9a8,
    0x0e950,
    0x06aa0,
    0x0aea6,
    0x0ab50,
    0x04b60,
    0x0aae4,
    0x0a570,
    0x05260,
    0x0f263,
    0x0d950,
    0x05b57,
    0x056a0,
    0x096d0,
    0x04dd5,
    0x04ad0,
    0x0a4d0,
    0x0d4d4,
    0x0d250,
    0x0d558,
    0x0b540,
    0x0b6a0,
    0x195a6,
    0x095b0,
    0x049b0,
    0x0a974,
    0x0a4b0,
    0x0b27a,
    0x06a50,
    0x06d40,
    0x0af46,
    0x0ab60,
    0x09570,
    0x04af5,
    0x04970,
    0x064b0,
    0x074a3,
    0x0ea50,
    0x06b58,
    0x055c0,
    0x0ab60,
    0x096d5,
    0x092e0,
    0x0c960,
    0x0d954,
    0x0d4a0,
    0x0da50,
    0x07552,
    0x056a0,
    0x0abb7,
    0x025d0,
    0x092d0,
    0x0cab5,
    0x0a950,
    0x0b4a0,
    0x0baa4,
    0x0ad50,
    0x055d9,
    0x04ba0,
    0x0a5b0,
    0x15176,
    0x052b0,
    0x0a930,
    0x07954,
    0x06aa0,
    0x0ad50,
    0x05b52,
    0x04b60,
    0x0a6e6,
    0x0a4e0,
    0x0d260,
    0x0ea65,
    0x0d530,
    0x05aa0,
    0x076a3,
    0x096d0,
    0x04afb,
    0x04ad0,
    0x0a4d0,
    0x1d0b6,
    0x0d250,
    0x0d520,
    0x0dd45,
    0x0b5a0,
    0x056d0,
    0x055b2,
    0x049b0,
    0x0a577,
    0x0a4b0,
    0x0aa50,
    0x1b255,
    0x06d20,
    0x0ada0,
    0x14b63,
    0x09370,
    0x049f8,
    0x04970,
    0x064b0,
    0x168a6,
    0x0ea50,
    0x06b20,
    0x1a6c4,
    0x0aae0,
    0x0a2e0,
    0x0d2e3,
    0x0c960,
    0x0d557,
    0x0d4a0,
    0x0da50,
    0x05d55,
    0x056a0,
    0x0a6d0,
    0x055d4,
    0x052d0,
    0x0a9b8,
    0x0a950,
    0x0b4a0,
    0x0b6a6,
    0x0ad50,
    0x055a0,
    0x0aba4,
    0x0a5b0,
    0x052b0,
    0x0b273,
    0x06930,
    0x07337,
    0x06aa0,
    0x0ad50,
    0x14b55,
    0x04b60,
    0x0a570,
    0x054e4,
    0x0d160,
    0x0e968,
    0x0d520,
    0x0daa0,
    0x16aa6,
    0x056d0,
    0x04ae0,
    0x0a9d4,
    0x0a2d0,
    0x0d150,
    0x0f252,
    0x0d520
}

-- 天干
local Gan = { "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" }

-- 地支
local Zhi = { "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" }

-- 月份
local nStr3 = { "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊" }

-- 日期
local nStr2 = { "初", "十", "廿", "卅" }
local nStr1 = { "日", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十" }

-- 实现位运算函数
local function band(a, b)
    if not a or not b then
    	return 0
    end

    -- Localize math.floor for performance
    local floor = math.floor
    local result = 0
    local bitval = 1

    -- 处理负数或0的情况
    if a <= 0 or b <= 0 then
    	return 0
    end

    while a > 0 and b > 0 do
        if a % 2 == 1 and b % 2 == 1 then
            result = result + bitval
        end

        bitval = bitval * 2
        a = floor(a/2)
        b = floor(b/2)
    end

    return result
end

local function rshift(a, b)
    if not a or not b then
    	return 0
    end

    if a <= 0 then
    	return 0
    end

    if b < 0 then
    	return a
    end

    return math.floor(a / (2^b))
end

-- 计算农历闰月月份
local function leapMonth(y)
    if y < 1900 or y > 2100 then
    	return 0
    end

	-- Lua table index starts from 1
    local index = y - 1900 + 1

	-- Lua table index starts from 1
    if index <= 0 or index > #lunarInfo then
    	return 0
    end

    return band(lunarInfo[index], 0xf)
end

-- 计算农历闰月天数
local function leapDays(y)
    if y < 1900 or y > 2100 then
    	return 0
    end

    local m = leapMonth(y)

    if m == 0 then
    	return 0
    end

	-- Lua table index starts from 1
    if band(lunarInfo[y - 1900 + 1], 0x10000) ~= 0 then
        return 30
    end

    return 29
end

-- 计算农历年天数
local function lYearDays(y)
    if y < 1900 or y > 2100 then
    	return 0
    end

    local sum = 348
    local i = 0x8000
	-- Lua table index starts from 1
    local idx = y - 1900 + 1

	-- Lua table index starts from 1
    if idx <= 0 or idx > #lunarInfo then
    	return 0
    end

    while i > 0x8 do
        if band(lunarInfo[idx], i) ~= 0 then
            sum = sum + 1
        end

        i = rshift(i, 1)
    end

    return sum + leapDays(y)
end

-- 计算农历月份天数
local function monthDays(y, m)
    if y < 1900 or y > 2100 then
    	return 0
    end

    if m > 12 or m < 1 then
    	return 0
    end

	-- Lua table index starts from 1
    if band(lunarInfo[y - 1900 + 1], rshift(0x10000, m)) ~= 0 then
        return 30
    end

    return 29
end

-- 主要的转换函数
function p.toLunar(frame)
    -- 解析输入日期
    local date = frame.args[1] or frame:getParent().args.date

    if not date then
    	return "日期参数错误"
    end

    local y, m, d = date:match("^(%d%d%d%d)-(%d%d?)-(%d%d?)$")

    if not y or not m or not d then
        return "日期格式错误"
    end

    y, m, d = tonumber(y), tonumber(m), tonumber(d)

    -- 验证日期范围
    if y < 1900 or y > 2100 then
        return "年份超出范围(1900-2100)"
    end

    if m < 1 or m > 12 then
        return "月份错误"
    end

    if d < 1 or d > 31 then
        return "日期错误"
    end

    -- 计算距离1900年1月31日的天数
    local baseDate = os.time({ year = 1900, month = 1, day = 31 })
    local targetDate = os.time({ year = y, month = m, day = d })
    local offset = math.floor((targetDate - baseDate) / (24 * 60 * 60))

    -- 计算农历年
    local ly = 1900
    local temp = 0

    while ly < 2101 and offset > 0 do
        temp = lYearDays(ly)
        offset = offset - temp
        ly = ly + 1
    end

    if offset < 0 then
        offset = offset + temp
        ly = ly - 1
    end

    -- 计算农历月日
    local lm = 1
    local ld = 1
    local isLeap = false
    local leap = leapMonth(ly)

    -- 计算月
    while offset > 0 and lm < 13 do
        if leap > 0 and lm == leap + 1 and not isLeap then
            lm = lm - 1
            isLeap = true
            temp = leapDays(ly)
        else
            temp = monthDays(ly, lm)
        end

        offset = offset - temp

        if isLeap and lm == leap + 1 then
        	isLeap = false
        end

        lm = lm + 1
    end

    -- 计算日
    if offset == 0 and leap > 0 and lm == leap + 1 then
        if isLeap then
            isLeap = false
        else
            isLeap = true
            lm = lm - 1
        end
    end

    if offset < 0 then
        offset = offset + temp
        lm = lm - 1
    end

    ld = offset + 1

    -- 格式化输出
    local gzYear = Gan[((ly - 4) % 10) + 1] .. Zhi[((ly - 4) % 12) + 1]
    local lMonth = nStr3[lm]
    local lDay

    if ld == 10 then
        lDay = "初十"
    elseif ld == 20 then
        lDay = "二十"
    elseif ld == 30 then
        lDay = "三十"
    else
        lDay = nStr2[math.floor(ld / 10) + 1] .. nStr1[ld % 10 + 1]
    end

    return gzYear .. "年" .. lMonth .. "月" .. lDay
end

return p