Module:IriniaCalendar: Difference between revisions

From DSRPG
No edit summary
No edit summary
 
Line 4: Line 4:
local p = {}
local p = {}


-- Normalize arguments for any #invoke call
-- Configurable calendar variables
local function getArgs(frame)
    -- Prefer named args on this frame
    if frame.args and next(frame.args) then
        return frame.args
    end
    -- Otherwise fall back to parent frame args
    if frame.getParent then
        local parent = frame:getParent()
        if parent and parent.args then
            return parent.args
        end
    end
    return {}
end
 
-- Configurable variables for the calendar system
local calendar = {
local calendar = {
    -- Current date in the world
     current = { day = 4, month = 5, year = 1236 },
     current = { day = 4, month = 5, year = 1236 },
    -- Month names in order
     months = {
     months = {
         [1] = "Frost Moon", [2] = "Shadow Moon", [3] = "Storm Moon",
         [1] = "Frost Moon",   [2] = "Shadow Moon", [3] = "Storm Moon",
         [4] = "Mist Moon", [5] = "Bloom Moon",  [6] = "Sun Moon",
         [4] = "Mist Moon",   [5] = "Bloom Moon",  [6] = "Sun Moon",
         [7] = "Thunder Moon", [8] = "Harvest Moon", [9] = "Ember Moon",
         [7] = "Thunder Moon", [8] = "Harvest Moon",[9] = "Ember Moon",
         [10] = "Frost Moon", [11] = "Twilight Moon", [12] = "Star Moon"
         [10] = "Frost Moon", [11] = "Twilight Moon",[12] = "Star Moon"
     },
     },
    -- Days in each month
     days_in_month = {
     days_in_month = {
         [1] = 30, [2] = 28, [3] = 30, [4] = 29,
         [1] = 30, [2] = 28, [3] = 30, [4] = 29,
         [5] = 31, [6] = 31, [7] = 30, [8] = 31,
         [5] = 31, [6] = 31, [7] = 30, [8] = 31,
         [9] = 30, [10] = 31, [11] = 28, [12] = 30
         [9] = 30,[10] = 31,[11] = 28,[12] = 30
     },
     },
    -- Weekday names
     weekdays = {
     weekdays = {
         [1] = "Silverday", [2] = "Brassday", [3] = "Woodday",
         [1] = "Silverday", [2] = "Brassday", [3] = "Woodday",
         [4] = "Stoneday", [5] = "Glassday", [6] = "Steamday",
         [4] = "Stoneday", [5] = "Glassday", [6] = "Steamday",
         [7] = "Gearsday"
         [7] = "Gearsday"
     },
     },
    -- Seasons by month
     seasons = {
     seasons = {
         [1] = "Winter", [2] = "Winter", [3] = "Spring",
         [1] = "Winter", [2] = "Winter", [3] = "Spring",
         [4] = "Spring", [5] = "Spring", [6] = "Summer",
         [4] = "Spring", [5] = "Spring", [6] = "Summer",
         [7] = "Summer", [8] = "Summer", [9] = "Autumn",
         [7] = "Summer", [8] = "Summer", [9] = "Autumn",
         [10] = "Autumn", [11] = "Autumn", [12] = "Winter"
         [10] = "Autumn",[11] = "Autumn",[12] = "Winter"
     },
     },
     era = "AE",
     era = "AE",
    -- Total days per year
     days_in_year = 359
     days_in_year = 359
}
}


-- Update the current in-world date
-- Format a date via #invoke args: day, month, year, format
function p.updateCurrentDate(frame)
function p.formatDate(frame)
     local args = getArgs(frame)
     local args = frame.args or {}
     calendar.current.day  = tonumber(args.day)  or calendar.current.day
     local day  = tonumber(args.day)  or calendar.current.day
     calendar.current.month = tonumber(args.month) or calendar.current.month
     local month = tonumber(args.month) or calendar.current.month
     calendar.current.year  = tonumber(args.year)  or calendar.current.year
     local year  = tonumber(args.year)  or calendar.current.year
     return "Current date updated to " .. p.formatDate(calendar.current)
     local fmt  = args.format          or "full"
end


-- Get the formatted current date
function p.getCurrentDate(frame)
    local args = getArgs(frame)
    local fmt  = args.format or "full"
    return p.formatDate(calendar.current, fmt)
end
-- Convert numeric month to name
function p.getMonthName(frame)
    local args = getArgs(frame)
    local m    = tonumber(args.month) or 1
    local cnt  = #calendar.months
    if m < 1 or m > cnt then m = ((m-1)%cnt)+1 end
    return calendar.months[m]
end
-- Format a date (either a table or via #invoke frame)
function p.formatDate(dateOrFrame, format)
    local date, fmt
    if dateOrFrame and dateOrFrame.args then
        -- invoked via #invoke, grab named args
        local args = getArgs(dateOrFrame)
        date = {
            day  = tonumber(args.day)  or calendar.current.day,
            month = tonumber(args.month) or calendar.current.month,
            year  = tonumber(args.year)  or calendar.current.year
        }
        fmt = args.format or "full"
    else
        -- called programmatically
        date = dateOrFrame or {}
        fmt  = format or "full"
    end
    local day  = date.day  or 1
    local month = date.month or 1
    local year  = date.year  or 0
     if month < 1 or month > #calendar.months then
     if month < 1 or month > #calendar.months then
         month = ((month-1)%#calendar.months)+1
         month = ((month - 1) % #calendar.months) + 1
     end
     end
     local monthName = calendar.months[month]
     local monthName = calendar.months[month]
Line 123: Line 57:
         return day .. "/" .. month .. "/" .. year .. " " .. calendar.era
         return day .. "/" .. month .. "/" .. year .. " " .. calendar.era
     else
     else
        -- fallback for unknown formats
         return day .. " " .. monthName .. ", " .. year .. " " .. calendar.era
         return day .. " " .. monthName .. ", " .. year .. " " .. calendar.era
     end
     end
end
end


-- Parse a date string back into a table
-- Total days since year 0
function p.parseDate(dateStr)
function p.totalDays(date)
    local day, monName, year = string.match(dateStr, "(%d+)%s+(%a+%s*%a*),%s*(%d+)")
     if not date or not date.day or not date.month or not date.year then return 0 end
     if not day or not monName or not year then return nil end
     local total = date.year * calendar.days_in_year
     local month
     for i = 1, date.month - 1 do
     for i,name in ipairs(calendar.months) do
         total = total + calendar.days_in_month[i]
         if string.lower(name) == string.lower(monName) then month = i break end
     end
     end
    if not month then return nil end
     total = total + date.day
    return { day = tonumber(day), month = month, year = tonumber(year) }
end
 
-- Total days since year 0
function p.totalDays(d)
    if not d or not d.day or not d.month or not d.year then return 0 end
    local total = d.year * calendar.days_in_year
    for i=1, d.month-1 do total = total + calendar.days_in_month[i] end
     total = total + d.day
     return total
     return total
end
end


-- Compare two dates
-- Compare two dates: returns -1, 0, or 1
function p.compareDate(a,b)
function p.compareDate(a, b)
     if a.year ~= b.year then return a.year < b.year and -1 or 1 end
     if a.year < b.year then return -1 elseif a.year > b.year then return 1 end
     if a.month ~= b.month then return a.month < b.month and -1 or 1 end
     if a.month < b.month then return -1 elseif a.month > b.month then return 1 end
     if a.day ~= b.day then return a.day < b.day and -1 or 1 end
     if a.day < b.day then return -1 elseif a.day > b.day then return 1 end
     return 0
     return 0
end
end


-- Compute difference between two dates
-- Calculate difference between two dates as a string
function p.dateDifference(d1, d2)
function p.dateDifference(d1, d2)
     local negative = false
     local negative = false
     if p.compareDate(d1,d2) > 0 then d1,d2 = d2,d1; negative = true end
     if p.compareDate(d1, d2) > 0 then d1, d2 = d2, d1; negative = true end


     local years  = d2.year - d1.year
     local years  = d2.year - d1.year
Line 168: Line 93:
         months = d2.month - d1.month
         months = d2.month - d1.month
     else
     else
         years = years - 1
         years = years - 1
         months = 12 - d1.month + d2.month
         months = 12 - d1.month + d2.month
     end
     end
Line 178: Line 103:
             months = months - 1
             months = months - 1
         else
         else
             years = years - 1
             years = years - 1
             months = 11
             months = 11
         end
         end
         local prevMon = d2.month - 1
         local prev = d2.month - 1
         if prevMon < 1 then prevMon = 12 end
         if prev < 1 then prev = 12 end
         days = calendar.days_in_month[prevMon] - d1.day + d2.day
         days = calendar.days_in_month[prev] - d1.day + d2.day
     end
     end


     local parts = {}
     local parts = {}
     if years ~= 0 then table.insert(parts, years .. " year" .. (years~=1 and "s" or "")) end
     if years ~= 0 then table.insert(parts, years .. " year" .. (years ~= 1 and "s" or "")) end
     if months~= 0 then table.insert(parts, months .. " month" .. (months~=1 and "s" or "")) end
     if months ~= 0 then table.insert(parts, months.. " month" .. (months ~= 1 and "s" or "")) end
     if days  ~= 0 or #parts == 0 then table.insert(parts, days   .. " day"  .. (days~=1   and "s" or "")) end
     if days  ~= 0 or #parts == 0 then table.insert(parts, days .. " day"  .. (days   ~= 1 and "s" or "")) end


     local result = table.concat(parts, ", ")
     local result = table.concat(parts, ", ")
Line 199: Line 124:
end
end


-- Time since a given date
-- How long since a given date
function p.timeSince(frame)
function p.timeSince(frame)
     local args = getArgs(frame)
     local args = frame.args or {}
     local d = { day=tonumber(args.day) or 1, month=tonumber(args.month) or 1, year=tonumber(args.year) or 0 }
     local d   = {
        day   = tonumber(args.day)   or 1,
        month = tonumber(args.month) or 1,
        year = tonumber(args.year) or 0
    }
     return p.dateDifference(d, calendar.current)
     return p.dateDifference(d, calendar.current)
end
end


-- Time until a given date
-- How long until a given date
function p.timeUntil(frame)
function p.timeUntil(frame)
     local args = getArgs(frame)
     local args = frame.args or {}
     local d = { day=tonumber(args.day) or 1, month=tonumber(args.month) or 1, year=tonumber(args.year) or 0 }
     local d   = {
        day   = tonumber(args.day)   or 1,
        month = tonumber(args.month) or 1,
        year = tonumber(args.year) or 0
    }
     return p.dateDifference(calendar.current, d)
     return p.dateDifference(calendar.current, d)
end
end


-- Timeline entry formatter
-- Calculate age in years given birth date
function p.timelineEntry(frame)
    local args  = getArgs(frame)
    local d      = { day=tonumber(args.day) or 1, month=tonumber(args.month) or 1, year=tonumber(args.year) or 0 }
    local fmt    = args.format or "full"
    local dateStr= p.formatDate(d, fmt)
    local ago    = p.dateDifference(d, calendar.current)
    local text  = args.text or ""
    return string.format("<div class='timeline-entry'><span class='timeline-date'>%s</span> <span class='timeline-ago'>(%s ago)</span>: <span class='timeline-text'>%s</span></div>", dateStr, ago, text)
end
 
-- Single-month calendar display
function p.calendarDisplay(frame)
    local args = getArgs(frame)
    local m    = tonumber(args.month) or calendar.current.month
    local y    = tonumber(args.year)  or calendar.current.year
    if m<1 or m>#calendar.months then m = ((m-1)%#calendar.months)+1 end
 
    local daysInM = calendar.days_in_month[m]
    local monName = calendar.months[m]
    local out = { string.format("<table class='calendar-table'><caption>%s %d %s</caption>", monName, y, calendar.era), "<tr class='calendar-header'>" }
    for _,wd in ipairs(calendar.weekdays) do table.insert(out, "<th>"..wd.."</th>") end
    table.insert(out, "</tr>")
 
    local firstWeekday = (p.totalDays({ day=1, month=m, year=y }) - 1) % #calendar.weekdays + 1
    table.insert(out, "<tr>")
    for i=1, firstWeekday-1 do table.insert(out, "<td class='calendar-empty'></td>") end
 
    local cw = firstWeekday
    for d=1, daysInM do
        local cls = (d==calendar.current.day and m==calendar.current.month and y==calendar.current.year)
                    and 'calendar-current-day' or 'calendar-day'
        table.insert(out, string.format("<td class='%s'>%d</td>", cls, d))
        cw = cw + 1
        if cw > #calendar.weekdays and d < daysInM then table.insert(out, "</tr><tr>"); cw = 1 end
    end
    for i=cw,#calendar.weekdays do table.insert(out, "<td class='calendar-empty'></td>") end
    table.insert(out, "</tr></table>")
 
    return table.concat(out)
end
 
-- Full-year calendar display
function p.yearCalendar(frame)
    local args = getArgs(frame)
    local y    = tonumber(args.year) or calendar.current.year
    local out = { string.format("<div class='year-calendar'><h2>Calendar for Year %d %s</h2>", y, calendar.era) }
    for m=1,#calendar.months do
        local subArgs = { args={ month=tostring(m), year=tostring(y) } }
        table.insert(out, "<div class='month-calendar'>" .. p.calendarDisplay(subArgs) .. "</div>")
    end
    table.insert(out, "</div>")
    return table.concat(out)
end
 
-- Season lookup
function p.getSeason(frame)
    local args = getArgs(frame)
    local m    = tonumber(args.month) or calendar.current.month
    if m<1 or m>#calendar.months then m = ((m-1)%#calendar.months)+1 end
    return calendar.seasons[m]
end
 
-- Calculate age in years
function p.calculateAge(frame)
function p.calculateAge(frame)
     local args = getArgs(frame)
     local args = frame.args or {}
     local bd   = tonumber(args.day)  or 1
     local d    = {
    local bm  = tonumber(args.month) or 1
        day   = tonumber(args.day)  or 1,
    local by  = tonumber(args.year)  or 0
        month = tonumber(args.month) or 1,
     local ageStr = p.dateDifference({ day=bd, month=bm, year=by }, calendar.current)
        year  = tonumber(args.year)  or 0
     local y     = string.match(ageStr, "^(%d+) years?")
    }
     local diff = p.dateDifference(d, calendar.current)
     local y   = string.match(diff, "^(%d+) years?")
     return y or "0"
     return y or "0"
end
-- Event time ago with optional prefix/suffix
function p.eventTimeAgo(frame)
    local args  = getArgs(frame)
    local d      = { day=tonumber(args.day) or 1, month=tonumber(args.month) or 1, year=tonumber(args.year) or 0 }
    local prefix = args.prefix or ""
    local suffix = args.suffix or " ago"
    return prefix .. p.dateDifference(d, calendar.current) .. suffix
end
end


return p
return p

Latest revision as of 20:00, 4 May 2025

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

-- Module:IriniaCalendar
-- A module for handling the custom calendar of Irinia

local p = {}

-- Configurable calendar variables
local calendar = {
    current = { day = 4, month = 5, year = 1236 },
    months = {
        [1] = "Frost Moon",   [2] = "Shadow Moon", [3] = "Storm Moon",
        [4] = "Mist Moon",    [5] = "Bloom Moon",  [6] = "Sun Moon",
        [7] = "Thunder Moon", [8] = "Harvest Moon",[9] = "Ember Moon",
        [10] = "Frost Moon",  [11] = "Twilight Moon",[12] = "Star Moon"
    },
    days_in_month = {
        [1] = 30, [2] = 28, [3] = 30, [4] = 29,
        [5] = 31, [6] = 31, [7] = 30, [8] = 31,
        [9] = 30,[10] = 31,[11] = 28,[12] = 30
    },
    weekdays = {
        [1] = "Silverday", [2] = "Brassday", [3] = "Woodday",
        [4] = "Stoneday",  [5] = "Glassday", [6] = "Steamday",
        [7] = "Gearsday"
    },
    seasons = {
        [1] = "Winter", [2] = "Winter", [3] = "Spring",
        [4] = "Spring", [5] = "Spring", [6] = "Summer",
        [7] = "Summer", [8] = "Summer", [9] = "Autumn",
        [10] = "Autumn",[11] = "Autumn",[12] = "Winter"
    },
    era = "AE",
    days_in_year = 359
}

-- Format a date via #invoke args: day, month, year, format
function p.formatDate(frame)
    local args  = frame.args or {}
    local day   = tonumber(args.day)   or calendar.current.day
    local month = tonumber(args.month) or calendar.current.month
    local year  = tonumber(args.year)  or calendar.current.year
    local fmt   = args.format          or "full"

    if month < 1 or month > #calendar.months then
        month = ((month - 1) % #calendar.months) + 1
    end
    local monthName = calendar.months[month]

    if fmt == "full" then
        return day .. " " .. monthName .. ", " .. year .. " " .. calendar.era
    elseif fmt == "short" then
        return day .. " " .. monthName .. ", " .. year
    elseif fmt == "month-year" then
        return monthName .. ", " .. year .. " " .. calendar.era
    elseif fmt == "day-month" then
        return day .. " " .. monthName
    elseif fmt == "numeric" then
        return day .. "/" .. month .. "/" .. year .. " " .. calendar.era
    else
        -- fallback for unknown formats
        return day .. " " .. monthName .. ", " .. year .. " " .. calendar.era
    end
end

-- Total days since year 0
function p.totalDays(date)
    if not date or not date.day or not date.month or not date.year then return 0 end
    local total = date.year * calendar.days_in_year
    for i = 1, date.month - 1 do
        total = total + calendar.days_in_month[i]
    end
    total = total + date.day
    return total
end

-- Compare two dates: returns -1, 0, or 1
function p.compareDate(a, b)
    if a.year < b.year then return -1 elseif a.year > b.year then return 1 end
    if a.month < b.month then return -1 elseif a.month > b.month then return 1 end
    if a.day < b.day then return -1 elseif a.day > b.day then return 1 end
    return 0
end

-- Calculate difference between two dates as a string
function p.dateDifference(d1, d2)
    local negative = false
    if p.compareDate(d1, d2) > 0 then d1, d2 = d2, d1; negative = true end

    local years  = d2.year - d1.year
    local months = 0
    local days   = 0

    if d2.month >= d1.month then
        months = d2.month - d1.month
    else
        years  = years - 1
        months = 12 - d1.month + d2.month
    end

    if d2.day >= d1.day then
        days = d2.day - d1.day
    else
        if months > 0 then
            months = months - 1
        else
            years  = years - 1
            months = 11
        end
        local prev = d2.month - 1
        if prev < 1 then prev = 12 end
        days = calendar.days_in_month[prev] - d1.day + d2.day
    end

    local parts = {}
    if years  ~= 0 then table.insert(parts, years .. " year"  .. (years  ~= 1 and "s" or "")) end
    if months ~= 0 then table.insert(parts, months.. " month" .. (months ~= 1 and "s" or "")) end
    if days   ~= 0 or #parts == 0 then table.insert(parts, days  .. " day"   .. (days   ~= 1 and "s" or "")) end

    local result = table.concat(parts, ", ")
    if #parts > 1 then
        result = string.gsub(result, ", ([^,]*)$", " and %1")
    end
    if negative then result = result .. " ago" end
    return result
end

-- How long since a given date
function p.timeSince(frame)
    local args = frame.args or {}
    local d    = {
        day   = tonumber(args.day)   or 1,
        month = tonumber(args.month) or 1,
        year  = tonumber(args.year)  or 0
    }
    return p.dateDifference(d, calendar.current)
end

-- How long until a given date
function p.timeUntil(frame)
    local args = frame.args or {}
    local d    = {
        day   = tonumber(args.day)   or 1,
        month = tonumber(args.month) or 1,
        year  = tonumber(args.year)  or 0
    }
    return p.dateDifference(calendar.current, d)
end

-- Calculate age in years given birth date
function p.calculateAge(frame)
    local args = frame.args or {}
    local d    = {
        day   = tonumber(args.day)   or 1,
        month = tonumber(args.month) or 1,
        year  = tonumber(args.year)  or 0
    }
    local diff = p.dateDifference(d, calendar.current)
    local y    = string.match(diff, "^(%d+) years?")
    return y or "0"
end

return p