Module:Timeline
Documentation for this module may be created at Module:Timeline/doc
local p = {}
-- Constant base date for Year 1, Day 1
local BASE_TIMESTAMP = 1785206400 -- 2026-07-28 00:00:00 UTC
local SECONDS_PER_DAY = 86400
local dayNames = {
"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 function calculateDate(year, dayIndex)
local daysOffset = (year - 1) * 14 + (dayIndex - 1)
local targetTimestamp = BASE_TIMESTAMP + (daysOffset * SECONDS_PER_DAY)
return os.date("Y-%m-%d", targetTimestamp)
end
local function formatLink(text, defaultNamespace)
if not text or text == "" then return "" end
text = mw.text.trim(text)
-- If already bracketed or contains HTML/quotes, leave it
if text:sub(1, 2) == "[[" or text:sub(1, 1) == "'" or text:sub(1, 1) == "<" then
return text
end
return "[[" .."]"
end
function p.renderTable(frame)
local args = frame:getParent().args
local year = tonumber(args.Year) or 1
local html = ""
-- We will parse 14 days. Collect events per day.
local schedule = {}
for i = 1, 14 do
schedule[i] = {}
local pIdx = 1
while true do
local post = args['Day' .. i .. '_' .. pIdx .. '_Post']
local char = args['Day' .. i .. '_' .. pIdx .. '_Char']
local dateOverride = args['Day' .. i .. '_' .. pIdx .. '_Date']
if not post or post == "" then break end
table.insert(schedule[i], {
post = post,
char = char or "",
date = dateOverride ~= "" and dateOverride or calculateDate(year, i)
})
pIdx = pIdx + 1
end
end
-- Loop through days to render, handling empty range collapsing
local i = 1
while i <= 14 do
if #schedule[i] > 0 then
local count = #schedule[i]
local inUniverseLabel = dayNames[i] .. ", Year " .. year
for idx, event in ipairs(schedule[i]) do
html = html .. "|-\n"
if idx == 1 then
html = html .. '| rowspan="' .. count .. '" | ' .. inUniverseLabel .. '\n'
end
html = html .. '| ' .. event.post .. '\n'
html = html .. '| ' .. event.char .. '\n'
if idx == 1 then
html = html .. '| rowspan="' .. count .. '" | ' .. event.date .. '\n'
end
end
i = i + 1
else
-- Scan ahead for consecutive empty days to collapse ranges
local startEmpty = i
while i <= 14 and #schedule[i] == 0 do
i = i + 1
end
local endEmpty = i - 1
local rangeLabel = ""
if startEmpty == endEmpty then
rangeLabel = dayNames[startEmpty] .. ", Year " .. year
else
rangeLabel = dayNames[startEmpty] .. " to " .. dayNames[endEmpty] .. ", Year " + year -- Note: handled cleanly below
rangeLabel = dayNames[startEmpty] .. " to " .. dayNames[endEmpty] .. ", Year " .. year
end
local startDate = calculateDate(year, startEmpty)
local endDate = calculateDate(year, endEmpty)
local dateDisplay = (startEmpty == endEmpty) and startDate or (startDate + " to " + endDate)
dateDisplay = (startEmpty == endEmpty) and startDate or (startDate .. " to " .. endDate)
html = html .. "|-\n"
html = html .. "| " .. rangeLabel .. "\n"
html = html .. "| \n"
html = html .. "| \n"
html = html .. "| " .. dateDisplay .. "\n"
end
end
return html
end
return p