Module:Calendar

From aquila
(Redirected from Module:Calender)
Jump to navigation Jump to search

Documentation for this module may be created at Module:Calendar/doc

local p = {}
 
local LAUNCH = { year = 2026, month = 7, day = 28 }
local ERA = "Era Nova"
 
local PERIODS = {
    "Early January",
    "January--February",
    "February--March",
    "March--April",
    "April--May",
    "May--June",
    "June",
    "June--July",
    "July--August",
    "August--September",
    "September--October",
    "October--November",
    "November--December",
    "Late December",
}
local DAYS_PER_YEAR = #PERIODS
 
p.periods = PERIODS
p.daysPerYear = DAYS_PER_YEAR
 
local function trim(s)
    return (tostring(s or ""):match("^%s*(.-)%s*$"))
end
 
local function daysFromCivil(y, m, d)
    if m <= 2 then y = y - 1 end
    local era = math.floor(y / 400)
    local yoe = y - era * 400
    local mp = (m + 9) % 12
    local doy = math.floor((153 * mp + 2) / 5) + d - 1
    local doe = yoe * 365 + math.floor(yoe / 4) - math.floor(yoe / 100) + doy
    return era * 146097 + doe - 719468
end
 
local function civilFromDays(z)
    z = z + 719468
    local era = math.floor(z / 146097)
    local doe = z - era * 146097
    local yoe = math.floor(
        (doe - math.floor(doe / 1460) + math.floor(doe / 36524) - math.floor(doe / 146096)) / 365
    )
    local y = yoe + era * 400
    local doy = doe - (365 * yoe + math.floor(yoe / 4) - math.floor(yoe / 100))
    local mp = math.floor((5 * doy + 2) / 153)
    local d = doy - math.floor((153 * mp + 2) / 5) + 1
    local m = mp + 3
    if m > 12 then m = m - 12 end
    if m <= 2 then y = y + 1 end
    return y, m, d
end
 
local LAUNCH_DAYNUM = daysFromCivil(LAUNCH.year, LAUNCH.month, LAUNCH.day)
local LAUNCH_ISO = string.format("%04d-%02d-%02d", LAUNCH.year, LAUNCH.month, LAUNCH.day)
 
function p.pretty(period)
    return (tostring(period or ""):gsub("%-%-", "–"))
end
 
function p.parse(text)
    text = trim(text)
    if text == "" then
        return nil, "no date was given"
    end
 
    local y, m, d = text:match("^(%d%d%d%d)[%-/%.](%d%d?)[%-/%.](%d%d?)%f[%D]")
    if not y then
        return nil, "“" .. text .. "” is not in YYYY-MM-DD format"
    end
    y, m, d = tonumber(y), tonumber(m), tonumber(d)
 
    local z = daysFromCivil(y, m, d)
    local ry, rm, rd = civilFromDays(z)
    if m < 1 or m > 12 or d < 1 or ry ~= y or rm ~= m or rd ~= d then
        return nil, "“" .. text .. "” is not a real calendar date"
    end
 
    local offset = z - LAUNCH_DAYNUM
    if offset < 0 then
        return nil, "“" .. text .. "” is before the server launched (" .. LAUNCH_ISO .. ")"
    end
 
    return string.format("%04d-%02d-%02d", y, m, d), offset
end
 
function p.info(text)
    local iso, offset = p.parse(text)
    if not iso then
        return nil, offset
    end
    local day = offset % DAYS_PER_YEAR + 1
    return {
        iso = iso,
        year = math.floor(offset / DAYS_PER_YEAR) + 1,
        day = day,
        period = PERIODS[day],
    }
end
 
function p.realDate(year, day)
    local y, m, d = civilFromDays(LAUNCH_DAYNUM + (year - 1) * DAYS_PER_YEAR + (day - 1))
    return string.format("%04d-%02d-%02d", y, m, d)
end
 
function p.iso(frame)
    local i = p.info(frame.args[1])
    return i and i.iso or ""
end
 
function p.yearNum(frame)
    local i = p.info(frame.args[1])
    return i and tostring(i.year) or ""
end
 
function p.dayNum(frame)
    local i = p.info(frame.args[1])
    return i and tostring(i.day) or ""
end
 
function p.period(frame)
    local i = p.info(frame.args[1])
    return i and i.period or ""
end
 
function p.label(frame)
    local i = p.info(frame.args[1])
    return i and p.pretty(i.period) or ""
end
 
function p.yearName(frame)
    local i = p.info(frame.args[1])
    return i and (ERA .. ", Year " .. i.year) or ""
end
 
function p.prettify(frame)
    return p.pretty(frame.args[1])
end
 
function p.check(frame)
    local i, message = p.info(frame.args[1])
    if i then
        return ""
    end
    return '<div class="error" style="margin: 0 0 1em;"><strong>RealDate problem:</strong> '
        .. message
        .. '. Use YYYY-MM-DD, for example 2026-09-15.</div>[[Category:Pages with a date problem]]'
end
 
return p