Module:Age
Appearance
Documentation for this module may be created at Module:Age/doc
-- Module:Age
-- Provides age calculations for {{age}}, {{death date and age}}, {{birth date and age}}
-- and the date handling in {{Infobox person}} / {{Infobox scientist}}.
--
-- Entry point used on this wiki:
-- {{#invoke:age|age_generic|template=age_full_years}}
-- called from Template:Age, whose parent parameters are:
-- 1,2,3 = year, month, day of the earlier date
-- 4,5,6 = year, month, day of the later date (defaults to today if omitted)
local p = {}
-- Convert a parameter to a number, tolerating whitespace, empty strings and nil.
local function num(v)
if v == nil then
return nil
end
v = tostring(v):match('^%s*(.-)%s*$')
if v == '' then
return nil
end
return tonumber(v)
end
-- Whole years elapsed from (y1,m1,d1) to (y2,m2,d2).
local function fullYears(y1, m1, d1, y2, m2, d2)
local age = y2 - y1
if (m2 < m1) or (m2 == m1 and d2 < d1) then
age = age - 1
end
return age
end
-- Collect the six date parameters, preferring the parent frame (the template call),
-- falling back to direct #invoke arguments so the module can also be called directly.
local function getDates(frame)
local args = {}
local parent = frame:getParent()
if parent and parent.args then
for i = 1, 6 do
args[i] = parent.args[i]
end
end
for i = 1, 6 do
if num(args[i]) == nil then
args[i] = frame.args[i] or args[i]
end
end
local y1, m1, d1 = num(args[1]), num(args[2]), num(args[3])
local y2, m2, d2 = num(args[4]), num(args[5]), num(args[6])
-- No later date supplied: measure to today.
if y2 == nil then
local now = os.date('*t')
y2, m2, d2 = now.year, now.month, now.day
end
-- Missing month/day default to the start of the year, matching the
-- behaviour the wrapper templates expect when they pass partial dates.
m1, d1 = m1 or 1, d1 or 1
m2, d2 = m2 or 1, d2 or 1
return y1, m1, d1, y2, m2, d2
end
function p.age_generic(frame)
local y1, m1, d1, y2, m2, d2 = getDates(frame)
if y1 == nil then
return ''
end
local age = fullYears(y1, m1, d1, y2, m2, d2)
-- Guard against obviously bad input rather than emitting a negative age.
if age < 0 or age > 150 then
return ''
end
return tostring(age)
end
-- Aliases, so other standard wrappers work if they are ever imported.
p.age_full_years = p.age_generic
p.main = p.age_generic
-- ---------------------------------------------------------------------------
-- Year-only ("estimated") ages.
--
-- When either date is known only to the year, the age is ambiguous by exactly
-- one year, so the honest result is a range rather than a single figure.
-- A person born in 1960 who died in 2006 was 45 or 46 depending on whether the
-- birthday had passed; this returns "45\226\128\147\&46".
--
-- Entry points:
-- {{#invoke:Age|age_range}} -- args: earlierYear, laterYear
-- {{#invoke:Age|year_range}} -- alias
-- ---------------------------------------------------------------------------
local function rangeArgs(frame)
-- Explicit #invoke arguments win: a wrapper template that passes years
-- positionally must not be overridden by its own parent's parameters
-- (which may be in a different order).
local y1, y2 = num(frame.args[1]), num(frame.args[2])
if y1 == nil then
local parent = frame:getParent()
if parent and parent.args then
y1, y2 = num(parent.args[1]), num(parent.args[2])
end
end
return y1, y2
end
function p.age_range(frame)
local y1, y2 = rangeArgs(frame)
-- No later year supplied: measure to the current year.
if y2 == nil then
y2 = tonumber(os.date('%Y'))
end
if y1 == nil then
return ''
end
local hi = y2 - y1
local lo = hi - 1
if lo < 0 then
lo = 0
end
if hi < 0 or hi > 150 then
return ''
end
if lo == hi then
return tostring(hi)
end
-- en dash between the bounds
return tostring(lo) .. '\226\128\147' .. tostring(hi)
end
p.year_range = p.age_range
return p