rime-ice/lua/lunar.lua

672 lines
13 KiB
Lua
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--[[
Lua 阿拉伯数字转中文实现 https://blog.csdn.net/lp12345678910/article/details/121396243
农历功能复制自 https://github.com/boomker/rime-fast-xhup
--]]
-- 数字转中文:
local numerical_units = {
"",
"",
"",
"",
"",
"",
"",
"",
"亿",
"",
"",
"",
"",
"",
"",
"",
}
local numerical_names = {
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
}
local function convert_arab_to_chinese(number)
local n_number = tonumber(number)
assert(n_number, "传入参数非正确number类型!")
-- 0 ~ 9
if n_number < 10 then
return numerical_names[n_number + 1]
end
-- 一十九 => 十九
if n_number < 20 then
local digit = string.sub(n_number, 2, 2)
if digit == "0" then
return ""
else
return "" .. numerical_names[digit + 1]
end
end
--[[
1. 最大输入9位
超过9位string的len加2位因为有.0的两位)
零 ~ 九亿九千九百九十九万九千九百九十九
0 ~ 999999999
2. 最大输入14位超过14位会四舍五入
零 ~ 九十九兆九千九百九十九亿九千九百九十九万九千九百九十九万
0 ~ 99999999999999
--]]
local len_max = 9
local len_number = string.len(number)
assert(
len_number > 0 and len_number <= len_max,
"传入参数位数" .. len_number .. "必须在(0, " .. len_max .. "]之间!"
)
-- 01数字转成表结构存储
local numerical_tbl = {}
for i = 1, len_number do
numerical_tbl[i] = tonumber(string.sub(n_number, i, i))
end
local pre_zero = false
local result = ""
for index, digit in ipairs(numerical_tbl) do
local curr_unit = numerical_units[len_number - index + 1]
local curr_name = numerical_names[digit + 1]
if digit == 0 then
if not pre_zero then
result = result .. curr_name
end
pre_zero = true
else
result = result .. curr_name .. curr_unit
pre_zero = false
end
end
result = string.gsub(result, "零+$", "")
return result
end
-- 农历:
-- 天干名称
local cTianGan = {
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
}
-- 地支名称
local cDiZhi = {
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
}
-- 属相名称
local cShuXiang = {
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
}
-- 农历日期名
local cDayName = {
"初一",
"初二",
"初三",
"初四",
"初五",
"初六",
"初七",
"初八",
"初九",
"初十",
"十一",
"十二",
"十三",
"十四",
"十五",
"十六",
"十七",
"十八",
"十九",
"二十",
"廿一",
"廿二",
"廿三",
"廿四",
"廿五",
"廿六",
"廿七",
"廿八",
"廿九",
"三十",
}
-- 农历月份名
local cMonName = {
"正月",
"二月",
"三月",
"四月",
"五月",
"六月",
"七月",
"八月",
"九月",
"十月",
"冬月",
"腊月",
}
-- 农历数据
local wNongliData = {
"AB500D2",
"4BD0883",
"4AE00DB",
"A5700D0",
"54D0581",
"D2600D8",
"D9500CC",
"655147D",
"56A00D5",
"9AD00CA",
"55D027A",
"4AE00D2",
"A5B0682",
"A4D00DA",
"D2500CE",
"D25157E",
"B5500D6",
"56A00CC",
"ADA027B",
"95B00D3",
"49717C9",
"49B00DC",
"A4B00D0",
"B4B0580",
"6A500D8",
"6D400CD",
"AB5147C",
"2B600D5",
"95700CA",
"52F027B",
"49700D2",
"6560682",
"D4A00D9",
"EA500CE",
"6A9157E",
"5AD00D6",
"2B600CC",
"86E137C",
"92E00D3",
"C8D1783",
"C9500DB",
"D4A00D0",
"D8A167F",
"B5500D7",
"56A00CD",
"A5B147D",
"25D00D5",
"92D00CA",
"D2B027A",
"A9500D2",
"B550781",
"6CA00D9",
"B5500CE",
"535157F",
"4DA00D6",
"A5B00CB",
"457037C",
"52B00D4",
"A9A0883",
"E9500DA",
"6AA00D0",
"AEA0680",
"AB500D7",
"4B600CD",
"AAE047D",
"A5700D5",
"52600CA",
"F260379",
"D9500D1",
"5B50782",
"56A00D9",
"96D00CE",
"4DD057F",
"4AD00D7",
"A4D00CB",
"D4D047B",
"D2500D3",
"D550883",
"B5400DA",
"B6A00CF",
"95A1680",
"95B00D8",
"49B00CD",
"A97047D",
"A4B00D5",
"B270ACA",
"6A500DC",
"6D400D1",
"AF40681",
"AB600D9",
"93700CE",
"4AF057F",
"49700D7",
"64B00CC",
"74A037B",
"EA500D2",
"6B50883",
"5AC00DB",
"AB600CF",
"96D0580",
"92E00D8",
"C9600CD",
"D95047C",
"D4A00D4",
"DA500C9",
"755027A",
"56A00D1",
"ABB0781",
"25D00DA",
"92D00CF",
"CAB057E",
"A9500D6",
"B4A00CB",
"BAA047B",
"AD500D2",
"55D0983",
"4BA00DB",
"A5B00D0",
"5171680",
"52B00D8",
"A9300CD",
"795047D",
"6AA00D4",
"AD500C9",
"5B5027A",
"4B600D2",
"96E0681",
"A4E00D9",
"D2600CE",
"EA6057E",
"D5300D5",
"5AA00CB",
"76A037B",
"96D00D3",
"4AB0B83",
"4AD00DB",
"A4D00D0",
"D0B1680",
"D2500D7",
"D5200CC",
"DD4057C",
"B5A00D4",
"56D00C9",
"55B027A",
"49B00D2",
"A570782",
"A4B00D9",
"AA500CE",
"B25157E",
"6D200D6",
"ADA00CA",
"4B6137B",
"93700D3",
"49F08C9",
"49700DB",
"64B00D0",
"68A1680",
"EA500D7",
"6AA00CC",
"A6C147C",
"AAE00D4",
"92E00CA",
"D2E0379",
"C9600D1",
"D550781",
"D4A00D9",
"DA400CD",
"5D5057E",
"56A00D6",
"A6C00CB",
"55D047B",
"52D00D3",
"A9B0883",
"A9500DB",
"B4A00CF",
"B6A067F",
"AD500D7",
"55A00CD",
"ABA047C",
"A5A00D4",
"52B00CA",
"B27037A",
"69300D1",
"7330781",
"6AA00D9",
"AD500CE",
"4B5157E",
"4B600D6",
"A5700CB",
"54E047C",
"D1600D2",
"E960882",
"D5200DA",
"DAA00CF",
"6AA167F",
"56D00D7",
"4AE00CD",
"A9D047D",
"A2D00D4",
"D1500C9",
"F250279",
"D5200D1",
}
-- 十进制转二进制
local function Dec2bin(n)
local t, t1
local tables = {}
t = tonumber(n)
while math.floor(t / 2) >= 1 do
t1 = t and math.fmod(t, 2)
if t1 > 0 then
if #tables > 0 then
table.insert(tables, 1, 1)
else
tables[1] = 1
end
else
if #tables > 0 then
table.insert(tables, 1, 0)
else
tables[1] = 0
end
end
t = math.floor(t / 2)
if t == 1 then
if #tables > 0 then
table.insert(tables, 1, 1)
else
tables[1] = 1
end
end
end
return string.gsub(table.concat(tables), "^[0]+", "")
end
-- 2/10/16进制互转
local function Atoi(x, inPuttype, outputtype)
local r
if tonumber(inPuttype) == 2 then
if tonumber(outputtype) == 10 then -- 2进制-->10进制
r = tonumber(tostring(x), 2)
-- elseif tonumber(outputtype) == 16 then -- 2进制-->16进制
-- r = bin2hex(tostring(x))
end
elseif tonumber(inPuttype) == 10 then
if tonumber(outputtype) == 2 then -- 10进制-->2进制
r = Dec2bin(tonumber(x))
elseif tonumber(outputtype) == 16 then -- 10进制-->16进制
r = string.format("%x", x)
end
elseif tonumber(inPuttype) == 16 then
if tonumber(outputtype) == 2 then -- 16进制-->2进制
r = Dec2bin(tonumber(tostring(x), 16))
elseif tonumber(outputtype) == 10 then -- 16进制-->10进制
r = tonumber(tostring(x), 16)
end
end
return r
end
-- 农历16进制数据分解
local function Analyze(Data)
local rtn1, rtn2, rtn3, rtn4
rtn1 = Atoi(string.sub(Data, 1, 3), 16, 2)
if string.len(rtn1) < 12 then
rtn1 = "0" .. rtn1
end
rtn2 = string.sub(Data, 4, 4)
rtn3 = Atoi(string.sub(Data, 5, 5), 16, 10)
rtn4 = Atoi(string.sub(Data, -2, -1), 16, 10)
if string.len(rtn4) == 3 then
rtn4 = "0" .. Atoi(string.sub(Data, -2, -1), 16, 10)
end
-- string.gsub(rtn1, "^[0]*", "")
return { rtn1, rtn2, rtn3, rtn4 }
end
-- 年天数判断
local function IsLeap(y)
local year = tonumber(y)
if not year then
return nil
end
if math.fmod(year, 400) ~= 0 and math.fmod(year, 4) == 0 or math.fmod(year, 400) == 0 then
return 366
else
return 365
end
end
-- 返回当年过了多少天
local function leaveDate(y)
local day, total
total = 0
if IsLeap(tonumber(string.sub(y, 1, 4))) > 365 then
day = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
else
day = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
end
if tonumber(string.sub(y, 5, 6)) > 1 then
for i = 1, tonumber(string.sub(y, 5, 6)) - 1 do
total = total + day[i]
end
total = total + tonumber(string.sub(y, 7, 8))
else
return tonumber(string.sub(y, 7, 8))
end
return tonumber(total)
end
-- 计算日期差两个8位数日期之间相隔的天数date2>date1
local function diffDate(date1, date2)
local n, total
total = 0
date1 = tostring(date1)
date2 = tostring(date2)
if tonumber(date2) > tonumber(date1) then
n = tonumber(string.sub(date2, 1, 4)) - tonumber(string.sub(date1, 1, 4))
if n > 1 then
for i = 1, n - 1 do
total = total + IsLeap(tonumber(string.sub(date1, 1, 4)) + i)
end
total = total
+ leaveDate(tonumber(string.sub(date2, 1, 8)))
+ IsLeap(tonumber(string.sub(date1, 1, 4)))
- leaveDate(tonumber(string.sub(date1, 1, 8)))
elseif n == 1 then
total = IsLeap(tonumber(string.sub(date1, 1, 4)))
- leaveDate(tonumber(string.sub(date1, 1, 8)))
+ leaveDate(tonumber(string.sub(date2, 1, 8)))
else
total = leaveDate(tonumber(string.sub(date2, 1, 8))) - leaveDate(tonumber(string.sub(date1, 1, 8)))
-- print(date1 .. "-" .. date2)
end
elseif tonumber(date2) == tonumber(date1) then
return 0
else
return -1
end
return total
end
-- 公历转农历支持转化范围公元1900-2100年
-- 公历日期 Gregorian:格式 YYYYMMDD
-- <返回值>农历日期 中文 天干地支属相
local function Date2LunarDate(Gregorian)
Gregorian = tostring(Gregorian)
local Year, Month, Day, Pos, Data0, Data1, MonthInfo, LeapInfo, Leap, Newyear, LYear, thisMonthInfo
Year = tonumber(Gregorian.sub(Gregorian, 1, 4))
Month = tonumber(Gregorian.sub(Gregorian, 5, 6))
Day = tonumber(Gregorian.sub(Gregorian, 7, 8))
if Year > 2100 or Year < 1899 or Month > 12 or Month < 1 or Day < 1 or Day > 31 or string.len(Gregorian) < 8 then
return "无效日期", "无效日期"
end
-- 获取两百年内的农历数据
Pos = Year - 1900 + 2
Data0 = wNongliData[Pos - 1]
Data1 = wNongliData[Pos]
-- 判断农历年份
local tb1 = Analyze(Data1)
MonthInfo = tb1[1]
LeapInfo = tb1[2]
Leap = tb1[3]
Newyear = tb1[4]
local Date1 = Year .. Newyear
local Date2 = Gregorian
local Date3 = diffDate(Date1, Date2) -- 和当年农历新年相差的天数
if Date3 < 0 then
-- print(Data0 .. "-2")
tb1 = Analyze(Data0)
Year = Year - 1
MonthInfo = tb1[1]
LeapInfo = tb1[2]
Leap = tb1[3]
Newyear = tb1[4]
Date1 = Year .. Newyear
Date2 = Gregorian
Date3 = diffDate(Date1, Date2)
-- print(Date2 .. "--" .. Date1 .. "--" .. Date3)
end
Date3 = Date3 + 1
LYear = Year -- 农历年份,就是上面计算后的值
if Leap > 0 then -- 有闰月
thisMonthInfo = string.sub(MonthInfo, 1, tonumber(Leap)) .. LeapInfo .. string.sub(MonthInfo, Leap + 1)
else
thisMonthInfo = MonthInfo
end
local thisMonth, thisDays, LMonth, LDay, Isleap, LunarDate, LunarDate2, LunarYear, LunarMonth
for i = 1, 13 do
thisMonth = string.sub(thisMonthInfo, i, i)
thisDays = 29 + thisMonth
if Date3 > thisDays then
Date3 = Date3 - thisDays
else
if Leap > 0 then
if Leap >= i then
LMonth = i
Isleap = 0
else
LMonth = i - 1
if i - Leap == 1 then
Isleap = 1
else
Isleap = 0
end
end
else
LMonth = i
Isleap = 0
end
LDay = math.floor(Date3)
break
end
end
if Isleap > 0 then
LunarMonth = "" .. cMonName[LMonth]
else
LunarMonth = cMonName[LMonth]
end
local _nis = tostring(LYear)
local _LunarYears = ""
for i = 1, _nis:len() do
local _ni_digit = tonumber(_nis:sub(i, i))
_LunarYears = _LunarYears .. convert_arab_to_chinese(_ni_digit)
end
LunarYear = string.gsub(_LunarYears, "", "")
LunarDate = cTianGan[math.fmod(LYear - 4, 10) + 1]
.. cDiZhi[math.fmod(LYear - 4, 12) + 1]
.. "年("
.. cShuXiang[math.fmod(LYear - 4, 12) + 1]
.. ""
.. LunarMonth
.. cDayName[LDay]
LunarDate2 = LunarYear .. "" .. LunarMonth .. cDayName[LDay]
return LunarDate, LunarDate2
end
-- 农历
-- 从 lunar: nl 获取农历触发关键字(双拼默认为 lunar
-- 从 recognizer/patterns/gregorian_to_lunar 获取第 2 个字符作为公历转农历的触发前缀,默认为 N
local function translator(input, seg, env)
env.lunar_key_word = env.lunar_key_word or
(env.engine.schema.config:get_string(env.name_space:gsub('^*', '')) or 'nl')
env.gregorian_to_lunar = env.gregorian_to_lunar or
(env.engine.schema.config:get_string('recognizer/patterns/gregorian_to_lunar'):sub(2, 2) or 'N')
if input == env.lunar_key_word then
local date1, date2 = Date2LunarDate(os.date("%Y%m%d"))
local lunar_ymd = (Candidate("", seg.start, seg._end, date2, ""))
lunar_ymd.quality = 999
yield(lunar_ymd)
local lunar_date = Candidate("", seg.start, seg._end, date1, "")
lunar_date.quality = 999
yield(lunar_date)
elseif env.gregorian_to_lunar ~= '' and input:sub(1, 1) == env.gregorian_to_lunar then
local date1, date2 = Date2LunarDate(input:sub(2))
local lunar_ymd = (Candidate("", seg.start, seg._end, date2, ""))
lunar_ymd.quality = 999
yield(lunar_ymd)
local lunar_date = Candidate("", seg.start, seg._end, date1, "")
lunar_date.quality = 999
yield(lunar_date)
end
end
return translator