Module:IriniaCalendar: Difference between revisions

From DSRPG
Created page with "-- Module:IriniaCalendar -- A module for handling the custom calendar of Irinia local p = {} -- Configurable variables for the calendar system local calendar = { -- Current date in the world (can be updated as game progresses) current = { day = 4, month = 5, -- Bloom Moon year = 1236, -- AE (After Establishment) }, -- Month names in order months = { [1] = "Frost Moon", [2] = "Shadow Moon", [3] = "..."
 
No edit summary
 
(5 intermediate revisions by the same user not shown)
Line 4: Line 4:
local p = {}
local p = {}


-- Configurable variables for the calendar system
-- Configurable calendar variables
local calendar = {
local calendar = {
    -- Current date in the world (can be updated as game progresses)
     current = { day = 4, month = 5, year = 1236 },
     current = {
        day = 4,
        month = 5, -- Bloom Moon
        year = 1236, -- AE (After Establishment)
    },
   
    -- Month names in order
     months = {
     months = {
         [1] = "Frost Moon",
         [1] = "Frost Moon",   [2] = "Shadow Moon", [3] = "Storm Moon",
        [2] = "Shadow Moon",
         [4] = "Mist Moon",   [5] = "Bloom Moon", [6] = "Sun Moon",
        [3] = "Storm Moon",
         [7] = "Thunder Moon", [8] = "Harvest Moon",[9] = "Ember Moon",
         [4] = "Mist Moon",
         [10] = "Frost Moon", [11] = "Twilight Moon",[12] = "Star 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 each month (adjust as needed for your calendar)
     days_in_month = {
     days_in_month = {
         [1] = 30, -- Frost Moon
         [1] = 30, [2] = 28, [3] = 30, [4] = 29,
        [2] = 28, -- Shadow Moon
         [5] = 31, [6] = 31, [7] = 30, [8] = 31,
        [3] = 30, -- Storm Moon
         [9] = 30,[10] = 31,[11] = 28,[12] = 30
        [4] = 29, -- Mist Moon
         [5] = 31, -- Bloom Moon
        [6] = 31, -- Sun Moon
        [7] = 30, -- Thunder Moon
        [8] = 31, -- Harvest Moon
         [9] = 30, -- Ember Moon
        [10] = 31, -- Frost Moon
        [11] = 28, -- Twilight Moon
        [12] = 30 -- Star Moon
     },
     },
   
    -- Days of the week (if your calendar uses a weekly cycle)
     weekdays = {
     weekdays = {
         [1] = "Silverday",
         [1] = "Silverday", [2] = "Brassday", [3] = "Woodday",
        [2] = "Brassday",
         [4] = "Stoneday", [5] = "Glassday", [6] = "Steamday",
        [3] = "Woodday",
         [4] = "Stoneday",
        [5] = "Glassday",
        [6] = "Steamday",
         [7] = "Gearsday"
         [7] = "Gearsday"
     },
     },
   
    -- Seasons (useful for some calendar displays)
     seasons = {
     seasons = {
         [1] = "Winter", -- Frost Moon, Shadow Moon
         [1] = "Winter", [2] = "Winter", [3] = "Spring",
        [2] = "Winter",
         [4] = "Spring", [5] = "Spring", [6] = "Summer",
        [3] = "Spring", -- Storm Moon, Mist Moon, Bloom Moon
         [7] = "Summer", [8] = "Summer", [9] = "Autumn",
         [4] = "Spring",
         [10] = "Autumn",[11] = "Autumn",[12] = "Winter"
        [5] = "Spring",
        [6] = "Summer", -- Sun Moon, Thunder Moon, Harvest Moon
         [7] = "Summer",
        [8] = "Summer",
        [9] = "Autumn", -- Ember Moon, Frost Moon, Twilight Moon
         [10] = "Autumn",
        [11] = "Autumn",
        [12] = "Winter", -- Star Moon
     },
     },
   
    -- Era name (e.g., "AE" for "After Establishment")
     era = "AE",
     era = "AE",
   
     days_in_year = 359
    -- Days in a year
     days_in_year = 359 -- Sum of all month days above
}
}


-- Update the current date (can be called from other modules or templates)
-- Format a date via #invoke args: day, month, year, format
function p.updateCurrentDate(frame)
function p.formatDate(frame)
     local args = frame.args
     local args = frame.args or {}
   
     local day   = tonumber(args.day)   or calendar.current.day
    -- If called via #invoke, get the args from the parent frame
     local month = tonumber(args.month) or calendar.current.month
    if not args or not args[1] then
     local year = tonumber(args.year) or calendar.current.year
        args = frame:getParent().args
     local fmt  = args.format         or "full"
     end
   
    -- Get the new current date
    calendar.current.day = tonumber(args.day) or calendar.current.day
     calendar.current.month = tonumber(args.month) or calendar.current.month
     calendar.current.year = tonumber(args.year) or calendar.current.year
   
    -- Return confirmation
    return "Current date updated to " .. p.formatDate(calendar.current)
end
 
-- Get the current date
function p.getCurrentDate(frame)
     local args = frame.args
    local format = "full"
   
    -- If called via #invoke, get the args from the parent frame
    if not args or not args[1] then
        args = frame:getParent().args
    end
   
    -- Get format parameter if provided
    if args.format then
        format = args.format
    end
   
    -- Format and return the current date
    return p.formatDate(calendar.current, format)
end
 
-- Convert numerical month to name
function p.getMonthName(frame)
    local args = frame.args
   
    -- If called via #invoke, get the args from the parent frame
    if not args or not args[1] then
        args = frame:getParent().args
    end
   
    local month = tonumber(args.month) or 1
   
    -- Ensure month is in valid range
    if month < 1 or month > #calendar.months then
        month = ((month - 1) % #calendar.months) + 1
    end
   
    return calendar.months[month]
end


-- Format a date according to the specified format
function p.formatDate(date, format)
    format = format or "full"
   
    local day = date.day or 1
    local month = date.month or 1
    local year = date.year or 0
   
    -- Ensure month is in valid range
     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]
   
 
    -- Different format options
     if fmt == "full" then
     if format == "full" then
         return day .. " " .. monthName .. ", " .. year .. " " .. calendar.era
         return day .. " " .. monthName .. ", " .. year .. " " .. calendar.era
     elseif format == "short" then
     elseif fmt == "short" then
         return day .. " " .. monthName .. ", " .. year
         return day .. " " .. monthName .. ", " .. year
     elseif format == "month-year" then
     elseif fmt == "month-year" then
         return monthName .. ", " .. year .. " " .. calendar.era
         return monthName .. ", " .. year .. " " .. calendar.era
     elseif format == "day-month" then
     elseif fmt == "day-month" then
         return day .. " " .. monthName
         return day .. " " .. monthName
     elseif format == "numeric" then
     elseif fmt == "numeric" then
         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 into a date object
-- Total days since year 0
function p.parseDate(dateStr)
    local day, monthName, year = string.match(dateStr, "(%d+)%s+(%a+%s*%a*),%s*(%d+)")
   
    if not day or not monthName or not year then
        return nil
    end
   
    -- Find the month number from the name
    local month = nil
    for i, name in ipairs(calendar.months) do
        if string.lower(name) == string.lower(monthName) then
            month = i
            break
        end
    end
   
    if not month then
        return nil
    end
   
    return {
        day = tonumber(day),
        month = month,
        year = tonumber(year)
    }
end
 
-- Calculate total days from year 0
function p.totalDays(date)
function p.totalDays(date)
     if not date or not date.day or not date.month or not date.year then
     if not date or not date.day or not date.month or not date.year then return 0 end
        return 0
     local total = date.year * calendar.days_in_year
    end
   
     local total = 0
   
    -- Add days from complete years
    total = total + date.year * calendar.days_in_year
   
    -- Add days from complete months in current year
     for i = 1, date.month - 1 do
     for i = 1, date.month - 1 do
         total = total + calendar.days_in_month[i]
         total = total + calendar.days_in_month[i]
     end
     end
   
    -- Add days in current month
     total = total + date.day
     total = total + date.day
   
     return total
     return total
end
end


-- Calculate how many years, months, and days between two dates
-- Compare two dates: returns -1, 0, or 1
function p.dateDifference(date1, date2)
function p.compareDate(a, b)
     -- If date2 is before date1, swap them and set a negative flag
     if a.year < b.year then return -1 elseif a.year > b.year then return 1 end
    local negative = false
     if a.month < b.month then return -1 elseif a.month > b.month then return 1 end
    if p.totalDays(date1) > p.totalDays(date2) then
     if a.day < b.day then return -1 elseif a.day > b.day then return 1 end
        date1, date2 = date2, date1
     return 0
        negative = true
    end
   
    local years = date2.year - date1.year
     local months = date2.month - date1.month
    local days = date2.day - date1.day
   
    -- Adjust if days are negative
    if days < 0 then
        months = months - 1
        days = days + calendar.days_in_month[((date2.month - 2) % 12) + 1]
    end
   
    -- Adjust if months are negative
    if months < 0 then
        years = years - 1
        months = months + 12
    end
   
    -- Format the result
    local result = {}
     if years ~= 0 then
        table.insert(result, years .. " year" .. (years ~= 1 and "s" or ""))
    end
    if months ~= 0 then
        table.insert(result, months .. " month" .. (months ~= 1 and "s" or ""))
    end
    if days ~= 0 or #result == 0 then
        table.insert(result, days .. " day" .. (days ~= 1 and "s" or ""))
    end
   
    local output = table.concat(result, ", ")
   
    -- Replace the last comma with "and" if there's more than one element
    if #result > 1 then
        output = string.gsub(output, ", ([^,]*)$", " and %1")
    end
   
    if negative then
        output = output .. " ago"
    end
   
     return output
end
end


-- Calculate time since a given date, relative to the current date
-- Calculate difference between two dates as a string
function p.timeSince(frame)
function p.dateDifference(d1, d2)
     local args = frame.args
     local negative = false
   
     if p.compareDate(d1, d2) > 0 then d1, d2 = d2, d1; negative = true end
    -- If called via #invoke, get the args from the parent frame
     if not args or not args[1] then
        args = frame:getParent().args
    end
   
    local day = tonumber(args.day) or 1
    local month = tonumber(args.month) or 1
    local year = tonumber(args.year) or 0
   
    local date = {
        day = day,
        month = month,
        year = year
    }
   
    -- Calculate the difference between the provided date and the current date
    return p.dateDifference(date, calendar.current)
end


-- Calculate time until a given date, relative to the current date
     local years  = d2.year - d1.year
function p.timeUntil(frame)
     local months = 0
     local args = frame.args
     local days  = 0
   
    -- If called via #invoke, get the args from the parent frame
    if not args or not args[1] then
        args = frame:getParent().args
    end
   
     local day = tonumber(args.day) or 1
     local month = tonumber(args.month) or 1
    local year = tonumber(args.year) or 0
   
    local date = {
        day = day,
        month = month,
        year = year
    }
   
    -- Calculate the difference between the current date and the provided date
    return p.dateDifference(calendar.current, date)
end


-- Format a timeline entry with a specific date and event text
    if d2.month >= d1.month then
function p.timelineEntry(frame)
        months = d2.month - d1.month
    local args = frame.args
     else
      
        years  = years - 1
    -- If called via #invoke, get the args from the parent frame
         months = 12 - d1.month + d2.month
    if not args or not args[1] then
         args = frame:getParent().args
     end
     end
   
    local day = tonumber(args.day) or 1
    local month = tonumber(args.month) or 1
    local year = tonumber(args.year) or 0
    local text = args.text or ""
    local format = args.format or "full"
   
    local date = {
        day = day,
        month = month,
        year = year
    }
   
    -- Format the date according to the specified format
    local dateStr = p.formatDate(date, format)
   
    -- Calculate how long ago this date was
    local timeAgo = p.dateDifference(date, calendar.current)
   
    -- Format the timeline entry
    return "<div class='timeline-entry'><span class='timeline-date'>" .. dateStr .. "</span> <span class='timeline-ago'>(" .. timeAgo .. " ago)</span>: <span class='timeline-text'>" .. text .. "</span></div>"
end


-- Generate a calendar display for a specific month and year
     if d2.day >= d1.day then
function p.calendarDisplay(frame)
         days = d2.day - d1.day
    local args = frame.args
     else
   
         if months > 0 then
    -- If called via #invoke, get the args from the parent frame
            months = months - 1
     if not args or not args[1] then
         else
        args = frame:getParent().args
            years  = years - 1
    end
             months = 11
   
    local month = tonumber(args.month) or calendar.current.month
    local year = tonumber(args.year) or calendar.current.year
   
    -- Ensure month is in valid range
    if month < 1 or month > #calendar.months then
         month = ((month - 1) % #calendar.months) + 1
    end
   
    local daysInMonth = calendar.days_in_month[month]
    local monthName = calendar.months[month]
   
    -- Start building the calendar table
    local output = "<table class='calendar-table'>\n"
    output = output .. "<caption>" .. monthName .. " " .. year .. " " .. calendar.era .. "</caption>\n"
   
     -- Add weekday headers
    output = output .. "<tr class='calendar-header'>\n"
    for _, dayName in ipairs(calendar.weekdays) do
         output = output .. "<th>" .. dayName .. "</th>\n"
    end
    output = output .. "</tr>\n"
   
    -- Calculate the weekday of the first day of the month
    -- This is a simplified calculation assuming the first day of year 0 was weekday 1
    local totalDays = p.totalDays({day = 1, month = month, year = year}) - 1
    local firstWeekday = (totalDays % #calendar.weekdays) + 1
   
    -- Start the first week
    output = output .. "<tr>\n"
   
    -- Add empty cells for days before the first of the month
    for i = 1, firstWeekday - 1 do
         output = output .. "<td class='calendar-empty'></td>\n"
    end
   
    -- Add the days of the month
    local currentWeekday = firstWeekday
    for day = 1, daysInMonth do
        -- Check if this day is the current date
        local isCurrentDate = (day == calendar.current.day and month == calendar.current.month and year == calendar.current.year)
        local cellClass = isCurrentDate and "calendar-current-day" or "calendar-day"
       
        output = output .. "<td class='" .. cellClass .. "'>" .. day .. "</td>\n"
       
        -- Move to the next weekday
        currentWeekday = currentWeekday + 1
       
        -- Start a new week if necessary
        if currentWeekday > #calendar.weekdays and day < daysInMonth then
             output = output .. "</tr>\n<tr>\n"
            currentWeekday = 1
         end
         end
        local prev = d2.month - 1
        if prev < 1 then prev = 12 end
        days = calendar.days_in_month[prev] - d1.day + d2.day
     end
     end
   
    -- Add empty cells for days after the end of the month
    for i = currentWeekday, #calendar.weekdays do
        output = output .. "<td class='calendar-empty'></td>\n"
    end
   
    -- Close the final week and the table
    output = output .. "</tr>\n</table>"
   
    return output
end


-- Generate an annual calendar display
    local parts = {}
function p.yearCalendar(frame)
    if years  ~= 0 then table.insert(parts, years .. " year"  .. (years  ~= 1 and "s" or "")) end
     local args = frame.args
     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 called via #invoke, get the args from the parent frame
 
     if not args or not args[1] then
     local result = table.concat(parts, ", ")
         args = frame:getParent().args
     if #parts > 1 then
         result = string.gsub(result, ", ([^,]*)$", " and %1")
     end
     end
      
     if negative then result = result .. " ago" end
    local year = tonumber(args.year) or calendar.current.year
     return result
   
    -- Start building the year calendar
    local output = "<div class='year-calendar'>\n"
    output = output .. "<h2>Calendar for Year " .. year .. " " .. calendar.era .. "</h2>\n"
   
    -- Add each month
    for month = 1, #calendar.months do
        output = output .. "<div class='month-calendar'>\n"
       
        -- Create arguments for the month calendar
        local monthArgs = {
            month = month,
            year = year
        }
       
        -- Generate the calendar for this month
        output = output .. p.calendarDisplay({ args = monthArgs })
        output = output .. "</div>\n"
    end
   
    output = output .. "</div>"
   
     return output
end
end


-- Get the season for a given month
-- How long since a given date
function p.getSeason(frame)
function p.timeSince(frame)
     local args = frame.args
     local args = frame.args or {}
      
     local d    = {
    -- If called via #invoke, get the args from the parent frame
        day  = tonumber(args.day)  or 1,
    if not args or not args[1] then
         month = tonumber(args.month) or 1,
         args = frame:getParent().args
         year  = tonumber(args.year) or 0
    end
     }
   
     return p.dateDifference(d, calendar.current)
    local month = tonumber(args.month) or calendar.current.month
   
    -- Ensure month is in valid range
    if month < 1 or month > #calendar.months then
         month = ((month - 1) % #calendar.months) + 1
    end
      
     return calendar.seasons[month]
end
end


-- Calculate a person's age given their birth date
-- How long until a given date
function p.calculateAge(frame)
function p.timeUntil(frame)
     local args = frame.args
     local args = frame.args or {}
      
     local d    = {
    -- If called via #invoke, get the args from the parent frame
         day  = tonumber(args.day)   or 1,
    if not args or not args[1] then
        month = tonumber(args.month) or 1,
         args = frame:getParent().args
        year  = tonumber(args.year) or 0
    end
   
    local birthDay = tonumber(args.day) or 1
    local birthMonth = tonumber(args.month) or 1
    local birthYear = tonumber(args.year) or 0
   
    local birthDate = {
        day = birthDay,
        month = birthMonth,
        year = birthYear
     }
     }
      
     return p.dateDifference(calendar.current, d)
    -- Calculate the difference between the birth date and the current date
    local age = p.dateDifference(birthDate, calendar.current)
   
    -- Extract just the years from the age string
    local years = string.match(age, "^(%d+) years?")
    if years then
        return years
    else
        return "0"
    end
end
end


-- Calculate how long ago an event occurred
-- Calculate age in years given birth date
function p.eventTimeAgo(frame)
function p.calculateAge(frame)
     local args = frame.args
     local args = frame.args or {}
      
     local d    = {
    -- If called via #invoke, get the args from the parent frame
         day  = tonumber(args.day)   or 1,
    if not args or not args[1] then
        month = tonumber(args.month) or 1,
         args = frame:getParent().args
        year  = tonumber(args.year) or 0
    end
   
    local eventDay = tonumber(args.day) or 1
    local eventMonth = tonumber(args.month) or 1
    local eventYear = tonumber(args.year) or 0
    local prefix = args.prefix or ""
    local suffix = args.suffix or " ago"
   
    local eventDate = {
        day = eventDay,
        month = eventMonth,
        year = eventYear
     }
     }
   
     local diff = p.dateDifference(d, calendar.current)
    -- Calculate the difference between the event date and the current date
     local y    = string.match(diff, "^(%d+) years?")
     local timeAgo = p.dateDifference(eventDate, calendar.current)
     return y or "0"
      
     return prefix .. timeAgo .. 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