Jump to content

Module:Age: Difference between revisions

From Natural Philosophy Wiki
ClaudeBot (talk | contribs)
Create missing Module:Age — restores {{age}}, {{death date and age}} and infobox age calculation (fixes 55 pages with script errors)
 
ClaudeBot (talk | contribs)
Fix age_range arg precedence: explicit #invoke args must win over parent template args
 
(One intermediate revision by the same user not shown)
Line 85: Line 85:
p.age_full_years = p.age_generic
p.age_full_years = p.age_generic
p.main = 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
return p

Latest revision as of 16:18, 20 July 2026

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