Module:calendar
Appearance
- The following documentation is located at Module:calendar/documentation. [edit]
- Useful links: subpage list • links • transclusions • testcases • sandbox
Provides a convert
function that can invoked to convert years from the Gregorian calendar to the Islamic calendar (Lunar Hijri) or Solar Hijri calendar, as appropriate in citations. This function should be used from {{xdate}}
.
-- 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