Modul:calendar
A modult a Modul:calendar/doc lapon tudod dokumentálni
-- The Lunar Hijri conversion functions are translated from
-- https://github.com/GeniusTS/hijri-dates/blob/master/src/Converter.php
-- which is under the MIT license, and the Solar Hijri functions are translated
-- from https://github.com/xsoh/solarHijri-js/blob/master/index.js,
-- which is under the MIT license.
-- convert and _convert are wrappers unique to
-- this module.
local export = {}
do
local floor = math.floor
function export.Gregorian_to_Julian(year, month, day)
if month < 3 then
year = year - 1
month = month + 12
end
local a = floor(year / 100.0)
local b
if year == 1582 and (month > 10 or (month == 10 and day > 4)) then
b = -10
elseif year == 1582 and month == 10 then
b = 0
elseif year < 1583 then
b = 0
else
b = 2 - a + floor(a / 4.0)
end
return floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + b - 1524
end
function export.Lunar_Hijri_to_Julian(year, month, day)
return floor((11 * year + 3) / 30) + floor(354 * year) + floor(30 * month)
- floor((month - 1) / 2) + day + 1948440 - 386
end
function export.Julian_to_Gregorian(Julian_day)
local b = 0
if Julian_day > 2299160 then
local a = floor((Julian_day - 1867216.25) / 36524.25)
b = 1 + a - floor(a / 4.0)
end
local bb = Julian_day + b + 1524
local cc = floor((bb - 122.1) / 365.25)
local dd = floor(365.25 * cc)
local ee = floor((bb - dd) / 30.6001)
local day = (bb - dd) - floor(30.6001 * ee)
local month = ee - 1
if ee > 13 then
cc = cc + 1
month = ee - 13
end
local year = cc - 4716
return { year = year, month = month, day = day }
end
function export.Julian_to_Lunar_Hijri(Julian_day)
local y = 10631.0 / 30.0
local epoch_astro = 1948084
local shift1 = 8.01 / 60.0
local z = Julian_day - epoch_astro
local cyc = floor(z / 10631.0)
z = z - 10631 * cyc
local j = floor((z - shift1) / y)
z = z - floor(j * y + shift1)
local year = 30 * cyc + j
local month = floor((z + 28.5001) / 29.5)
if month == 13 then
month = 12
end
local day = z - floor(29.5001 * month - 29)
return { year = year, month = month, day = day }
end
end
do
-- translated from here:
-- https://github.com/xsoh/solarHijri-js/blob/master/index.js
local floor, ceil = math.floor, math.ceil
-- Utility helper functions.
local function idiv(a, b)
local c = a / b
return (c > 0 and floor or ceil)(c)
end
local function mod(a, b)
return a - idiv(a, b) * b
end
--[[
local function is_valid_Solar_Hijri_date(year, month, day)
return year >= -61 and year <= 3177 and
month >= 1 and month <= 12 and
day >= 1 and day <= Solar_Hijri_month_length(year, month)
end
--]]
local function is_leap_Solar_Hijri_year(year)
return Solar_Hijri_Cal(year).leap == true
end
local function Solar_Hijri_month_length(year, month)
if month <= 5 then
return 30
elseif month >= 7 then
return 31
end
-- here where month = 6.
if is_leap_Solar_Hijri_year(year) then
return 30
else
return 29
end
end
local function Gregorian_to_Julian(year, month, day)
local d = idiv((year + idiv(month - 8, 6) + 100100) * 1461, 4)
+ idiv(153 * mod(month + 9, 12) + 2, 5)
+ day - 34840408
d = d - idiv(idiv(year + 100100 + idiv(month - 8, 6), 100) * 3, 4) + 752
return d
end
local function Julian_to_Gregorian(Julian_day)
local j = 4 * Julian_day + 139361631
j = j + idiv(idiv(4 * Julian_day + 183187720, 146097) * 3, 4) * 4 - 3908
local i = idiv(mod(j, 1461), 4) * 5 + 308
local month = mod(idiv(i, 153), 12) + 1
return {
year = idiv(j, 1461) - 100100 + idiv(8 - month, 6),
month = month,
day = idiv(mod(i, 153), 5) + 1,
}
end
local function Solar_Hijri_Cal(year)
local Gregorian_year = year + 621
local pGregorian_year = Gregorian_year + 1
return {
leap = (pGregorian_year % 4 == 0 and pGregorian_year % 100 ~= 0) or pGregorian_year % 400 == 0,
Gregorian_year = Gregorian_year,
start_date = 23, -- September 23
}
end
local function Julian_to_Solar_Hijri(Julian_day)
local Gregorian_year = Julian_to_Gregorian(Julian_day).year
local year = Gregorian_year - 621
local r = Solar_Hijri_Cal(year)
local Julian_day_1st_Libra = Gregorian_to_Julian(Gregorian_year, 9, r.start_date)
local k = Julian_day - Julian_day_1st_Libra
if k >= 0 and k <= 99 then
local leap = r.leap and 1 or 0
return {
year = year,
month = 1 + idiv(k, 30),
day = mod(k, 30) + 1,
}
else
k = k + 365
year = year - 1
r = Solar_Hijri_Cal(year)
leap = r.leap and 1 or 0
if k <= 178 then
k = k + leap
return {
year = year,
month = 1 + idiv(k, 30),
day = mod(k, 30) + 1,
}
else
k = k - 179
return {
year = year,
month = 7 + idiv(k, 31),
day = mod(k, 31) + 1,
}
end
end
end
local function Solar_Hijri_to_Julian(year, month, day)
local r = Solar_Hijri_Cal(year);
local leap = r.leap and 0 or -1
return Gregorian_to_Julian(r.Gregorian_year, 9, r.start_date)
+ (month - 1) * 30 + idiv(month, 7) * (month - 7) + day - 1 + (idiv(month, 7) * leap)
end
function export.Gregorian_to_Solar_Hijri(year, month, day)
return Julian_to_Solar_Hijri(Gregorian_to_Julian(year, month, day))
end
function export.Solar_Hijri_to_Gregorian(year, month, day)
return Julian_to_Gregorian(Solar_Hijri_to_Julian(year, month, day))
end
end
function export._convert(from, to, year)
local f1, f2
if from == "gregorian" then
if to == "lunar_hijri" then
f1 = "Gregorian_to_Julian"
f2 = "Julian_to_Lunar_Hijri"
elseif to == "solar_hijri" then
f1 = "Gregorian_to_Solar_Hijri"
else
error("not implemented")
end
elseif from == "lunar_hijri" then
if to == "gregorian" then
f1 = "Lunar_Hijri_to_Julian"
f2 = "Julian_to_Gregorian"
else
error("not implemented")
end
elseif from == "solar_hijri" then
if to == "gregorian" then
f1 = "Solar_Hijri_to_Gregorian"
else
error("not implemented")
end
else
error("not implemented")
end
local converter
if f2 then
function converter(year, month, day)
return export[f2](export[f1](year, month, day))
end
else
function converter(year, month, day)
return export[f1](year, month, day)
end
end
-- assuming month is 1-based
return converter(year, 1, 1), converter(year, 12, 30)
end
local aliases = {
ah = "lunar_hijri",
sh = "solar_hijri",
gc = "gregorian",
}
local function normalize_calendar_code(code)
code = code:lower():gsub(" ", "_")
return aliases[code] or code
end
function export.convert(frame)
assert(frame and frame.args and type(frame.args) == "table")
local args = pairs(frame.args)(frame.args) and frame.args
or frame:getParent().args
local from = args[1] or error("Expected calendar name in parameter 1")
local to = args[2] or error("Expected calendar name in parameter 2")
local year = tonumber(args[3]) or error("Expected number in parameter 3")
from, to = normalize_calendar_code(from), normalize_calendar_code(to)
local date1, date2 = export._convert(from, to, year)
local res = ""
local date = date1
while date.year <= date2.year do
local year_string = os.date("%Y", os.time(date))
if res ~= "" then
res = res .. "/"
end
res = res .. year_string
date.year = date.year + 1
end
return res
end
function export.debug(from, to, year)
return export.convert { args = { from, to, year } }
end
return export