Module:Age
![]() | This module is rated as ready for general use. It has reached a mature form and is thought to be relatively bug-free and ready for use wherever appropriate. It is ready to mention on help pages and other Wikipedia resources as an option for new users to learn. To reduce server load and bad output, it should be improved by sandbox testing rather than repeated trial-and-error editing. |
![]() | This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
![]() | This Lua module is used on approximately 1,300,000 pages, or roughly 2% of all pages. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them. |
![]() | This module depends on the following other modules: |
Templates supported
Module:Age implements the following templates:
Template | Required wikitext |
---|---|
{{extract}} | {{#invoke:age|extract}}
|
{{gregorian serial date}} | {{#invoke:age|gsd}}
|
{{time interval}} | {{#invoke:age|time_interval}}
|
{{age in days}} | {{#invoke:age|age_generic|template=age_days}}
|
{{age in days nts}} | {{#invoke:age|age_generic|template=age_days_nts}}
|
{{duration in days}} | {{#invoke:age|age_generic|template=duration_days}}
|
{{duration in days nts}} | {{#invoke:age|age_generic|template=duration_days_nts}}
|
{{age}} | {{#invoke:age|age_generic|template=age_full_years}}
|
{{age nts}} | {{#invoke:age|age_generic|template=age_full_years_nts}}
|
{{age in years}} | {{#invoke:age|age_generic|template=age_in_years}}
|
{{age in years nts}} | {{#invoke:age|age_generic|template=age_in_years_nts}}
|
{{age for infant}} | {{#invoke:age|age_generic|template=age_infant}}
|
{{age in months}} | {{#invoke:age|age_generic|template=age_m}}
|
{{age in weeks}} | {{#invoke:age|age_generic|template=age_w}}
|
{{age in weeks and days}} | {{#invoke:age|age_generic|template=age_wd}}
|
{{age in years and days}} | {{#invoke:age|age_generic|template=age_yd}}
|
{{age in years and days nts}} | {{#invoke:age|age_generic|template=age_yd_nts}}
|
{{age in years and months}} | {{#invoke:age|age_generic|template=age_ym}}
|
{{age in years, months and days}} | {{#invoke:age|age_generic|template=age_ymd}}
|
{{age in years, months, weeks and days}} | {{#invoke:age|age_generic|template=age_ymwd}}
|
{{birth date and age}} | {{#invoke:age|birth_date_and_age}}
|
{{death date and age}} | {{#invoke:age|death_date_and_age}}
|
Redirects
Template | Redirects to |
---|---|
{{ayd}} | {{age in years and days nts}} |
{{hla}} | {{age in years, months and days}} |
{{age in months, weeks and days}} | {{age in years, months, weeks and days}} |
{{bda}} | {{birth date and age}} |
{{dda}} | {{death date and age}} |
{{gsd}} | {{gregorian serial date}} |
The age templates expect the older date to be first. The implementations of age_in_years
and age_in_years_nts
display an error message if that is not the case. If similar checking is wanted for other templates, negative=error
can be added to the invoke. For example, {{age}} might use:
{{#invoke:age|age_generic|template=age_full_years|negative=error}}
If negative=error
does not apply, a negative difference is indicated with a minus sign (−).
Date formats
Dates can use numbered or named parameters to specify year/month/day. Alternatively, a full date can be entered in a variety of formats. For example:
{{age in years and months|year1=2001|month1=1|day1=10|year2=2012|month2=2|day2=20}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|year=2001|month=1|day=10|year2=2012|month2=2|day2=20}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|2001|1|10|2012|2|20}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|2001-1-10|2012-2-20}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|10 Jan 2001|20 Feb 2012}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|January 10, 2001|Feb 20, 2012}}
→ Script error: The function "age_generic" does not exist.
If the first or second date is omitted, the current date is used. For example:
{{age in years and months|year2=2012|month2=2|day2=20}}
→ Script error: The function "age_generic" does not exist.{{age in years and months||||2012|2|20}}
→ Script error: The function "age_generic" does not exist.{{age in years and months||2012-2-20}}
→ Script error: The function "age_generic" does not exist.{{age in years and months||20 Feb 2012}}
→ Script error: The function "age_generic" does not exist.{{age in years and months||Feb 20, 2012}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|year1=2001|month1=1|day1=10}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|year=2001|month=1|day=10}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|2001|1|10}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|2001-1-10}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|10 Jan 2001}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|January 10, 2001}}
→ Script error: The function "age_generic" does not exist.
Parameters
The following options are available:
Parameter | Description |
---|---|
duration=on |
The finishing date is included in the result; that adds one day to the age. |
fix=on |
Adjust invalid time units. See Template:Extract#Fix. |
format=commas |
A value of 1,000 or more is displayed with commas. |
format=raw |
Numbers are displayed without commas and negative numbers are displayed with a hyphen for {{#expr}} . In addition, {{age}} outputs a plain number and will not include a span to indicate if the result relies on the current date.
|
format=cardinal |
Display the resulting number using words such as "five" instead of 5. See below. |
format=ordinal |
Display the resulting number using words such as "fifth" instead of 5. See below. |
prefix=text |
Insert the given text before the result but after any sort key. For example, {{age|23 July 1910|14 July 1976|prefix=about|sortable=on}} outputs a hidden sort key followed by "about 65".
|
range=dash |
Accept a year only, or a year and month only, and show a range of ages with an en dash (–). |
range=yes |
Accept a year or year/month, and show the range with "or". |
range=no |
Accept a year only, or year/month, but show only a single age as if full dates had been entered. |
round=on |
The age is rounded to the nearest least-significant time unit. |
sc=on |
A serial comma is used (only useful when three or more values are displayed). |
sc=yes |
Same as sc=on .
|
show=hide |
The age is not displayed; may be useful with sortable=on .
|
sortable=on |
Insert a hidden sort key before the result (for use in sortable tables). |
sortable=table |
Insert a sort key using table syntax data-sort-value="value"| .
|
sortable=debug |
Same as sortable=on but the sort key is displayed for testing.
|
sortable=off |
No sort key (can override the default for a template like {{age nts}}). |
Examples using the range
parameter follow.
{{age in years and months|year=2001|month=1|year2=2012|month2=2|range=yes}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|2001|1||2012|2|range=yes}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|Jan 2001|Feb 2012|range=yes}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|Jan 2001|Feb 2012|range=dash}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|Jan 2001|Feb 2012|range=no}}
→ Script error: The function "age_generic" does not exist. (assume 1 Jan 2001 to 1 Feb 2012){{age in years and months|12 Jan 2001|Feb 2012|range=no}}
→ Script error: The function "age_generic" does not exist. (assume 12 Jan 2001 to 12 Feb 2012){{age in years and months|2001|2012|range=no}}
→ Script error: The function "age_generic" does not exist. (assume 1 Jan 2001 to 1 Jan 2012){{age in years and months|2001|23 Feb 2012|range=no}}
→ Script error: The function "age_generic" does not exist. (assume 23 Feb 2001 to 23 Feb 2012)
The sort key is based on the age in days, and fractions of a day if a time is specified.
{{age in years and months|10 Jan 2001|20 Feb 2012|sortable=debug}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|10 Jan 2001|6:00 am 20 Feb 2012|sortable=debug}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|10 Jan 2001|6:00 am 20 Feb 2012|sortable=debug|show=hide}}
→ Script error: The function "age_generic" does not exist.
An extra day is added for a duration.
{{age in years and months|20 Jan 2001|19 Feb 2012}}
→ Script error: The function "age_generic" does not exist. (one day short of 11 years, 1 month){{age in years and months|20 Jan 2001|19 Feb 2012|duration=on}}
→ Script error: The function "age_generic" does not exist.
The least-significant time unit can be rounded.
{{age in years and months|20 Jan 2001|10 Feb 2012}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|20 Jan 2001|10 Feb 2012|round=on}}
→ Script error: The function "age_generic" does not exist. (round to nearest month)
Large numbers can be formatted with commas.
{{age in years and months|120|2012|format=commas|range=yes}}
→ Script error: The function "age_generic" does not exist.{{age in years and months|120|2012|format=commas|range=dash}}
→ Script error: The function "age_generic" does not exist.
Spelling numbers
The templates that use age_generic
can display numbers in words rather than using numerals. The result can be a cardinal number (such as "five") or an ordinal number (such as "fifth"). The first letter can be in uppercase, and US spelling of numbers can be used. Examples:
{{age|1898|01|01|2018|02|01|format=cardinal}}
→ Script error: The function "age_generic" does not exist.{{age|1898|01|01|2018|02|01|format=cardinal_us}}
→ Script error: The function "age_generic" does not exist.{{age|1898|01|01|2018|02|01|format=Cardinal}}
→ Script error: The function "age_generic" does not exist.{{age|1898|01|01|2018|02|01|format=Cardinal_us}}
→ Script error: The function "age_generic" does not exist.{{age|1898|01|01|2018|02|01|format=Ordinal}}
→ Script error: The function "age_generic" does not exist.{{age|1898|01|01|2018|02|01|format=Ordinal_us}}
→ Script error: The function "age_generic" does not exist.{{age|1898|01|01|2018|02|01|format=ordinal}}
→ Script error: The function "age_generic" does not exist.{{age|1898|01|01|2018|02|01|format=ordinal_us}}
→ Script error: The function "age_generic" does not exist.{{age|1980|1990|range=yes|format=Cardinal}}
→ Script error: The function "age_generic" does not exist.{{age in years, months and days|April 1980|1995|format=Cardinal|range=yes}}
→ Script error: The function "age_generic" does not exist.
Tracking category
Localization
Inputs and outputs can be localized to suit the language used. Examples are at bnwiki and bswiki.
See also
- {{time interval}} • This template supports all age/duration calculations and provides more options such as abbreviating or omitting units.
--[[ Code for some date functions, including implementations of:
{{Age in days}} age_days
{{Age in years and months}} age_ym
{{Gregorian serial date}} gsd_ymd
Calendar functions will be needed in many areas, so this may be superseded
by some other system, perhaps using PHP functions accessed via mw.
]]
local MINUS = '−' -- Unicode U+2212 MINUS SIGN
local function number_name(number, singular, plural, sep)
-- Return the given number, converted to a string, with the
-- separator (default space) and singular or plural name appended.
plural = plural or (singular .. 's')
sep = sep or ' '
return tostring(number) .. sep .. ((number == 1) and singular or plural)
-- this uses an interesting trick of Lua:
-- * and reurns false if the first argument is false, and the second otherwise, so (number==1) and singular returns singular if its 1, returns false if it is only 1
-- * or returns the first argument if it is not false, and the second argument if the first is false
-- * so, if number is 1, and evaluates (true and singular) returning (singular); or evaluates (singular or plural), finds singular non-false, and returns singular
-- * but, if number is not 1, and evaluates (false and singular) returning (false); or evaluates (false or plural), and is forced to return plural
end
local function strip_to_nil(text)
-- If text is a non-blank string, return its content with no leading
-- or trailing whitespace.
-- Otherwise return nil (a nil or empty string argument gives a nil
-- result, as does a string argument of only whitespace).
if type(text) == 'string' then
local result = text:match("^%s*(.-)%s*$")
if result ~= '' then
return result
end
end
return nil
end
local function is_leap_year(year)
-- Return true if year is a leap year, assuming Gregorian calendar.
return (year % 4 == 0 and year % 100 ~= 0) or year % 400 == 0
end
local function days_in_month(year, month)
-- Return number of days (1..31) in given month (1..12).
local month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
if month == 2 and is_leap_year(year) then
return 29
end
return month_days[month]
end
-- A table to get current year/month/day (UTC), but only if needed.
local current = setmetatable({}, {
__index = function (self, key)
local d = os.date('!*t')
self.year = d.year
self.month = d.month
self.day = d.day
return rawget(self, key)
end
})
local function date_component(named, positional, component)
-- Return the first of the two arguments that is not nil and is not empty.
-- If both are nil, return the current date component, if specified.
-- The returned value is nil or is a number.
-- This translates empty arguments passed to the template to nil, and
-- optionally replaces a nil argument with a value from the current date.
named = strip_to_nil(named)
if named then
return tonumber(named)
end
positional = strip_to_nil(positional)
if positional then
return tonumber(positional)
end
if component then
return current[component]
end
return nil
end
local function gsd(year, month, day)
-- Return the Gregorian serial day (an integer >= 1) for the given date,
-- or return nil if the date is invalid (only check that year >= 1).
-- This is the number of days from the start of 1 AD (there is no year 0).
-- This code implements the logic in [[Template:Gregorian serial date]].
if year < 1 then
return nil
end
local floor = math.floor
local days_this_year = (month - 1) * 30.5 + day
if month > 2 then
if is_leap_year(year) then
days_this_year = days_this_year - 1
else
days_this_year = days_this_year - 2
end
if month > 8 then
days_this_year = days_this_year + 0.9
end
end
days_this_year = floor(days_this_year + 0.5)
year = year - 1
local days_from_past_years = year * 365
+ floor(year / 4)
- floor(year / 100)
+ floor(year / 400)
return days_from_past_years + days_this_year
end
local Date = {
-- A naive date that assumes the Gregorian calendar always applied.
year = 0, -- 1 to 9999 (0 if never set)
month = 1, -- 1 to 12
day = 1, -- 1 to 31
isvalid = false,
new = function (self, o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
}
function Date:__lt(rhs)
-- Return true if self < rhs.
if self.year < rhs.year then
return true
end
if self.year == rhs.year then
if self.month < rhs.month then
return true
end
if self.month == rhs.month then
return self.day < rhs.day
end
end
return false
end
function Date:set_current()
-- Set date from current time (UTC) and return self.
self.year = current.year
self.month = current.month
self.day = current.day
self.isvalid = true
return self
end
function Date:set_ymd(y, m, d)
-- Set date from year, month, day (strings or numbers) and return self.
-- LATER: If m is a name like "March" or "mar", translate it to a month.
y = tonumber(y)
m = tonumber(m)
d = tonumber(d)
if type(y) == 'number' and type(m) == 'number' and type(d) == 'number' then
self.year = y
self.month = m
self.day = d
self.isvalid = (1 <= y and y <= 9999 and 1 <= m and m <= 12 and
1 <= d and d <= days_in_month(y, m))
end
return self
end
local DateDiff = {
-- Simple difference between two dates, assuming Gregorian calendar.
isnegative = false, -- true if second date is before first
years = 0,
months = 0,
days = 0,
new = function (self, o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
}
function DateDiff:set(date1, date2)
-- Set difference between the two dates, and return self.
-- Difference is negative if the second date is older than the first.
local isnegative
if date2 < date1 then
isnegative = true
date1, date2 = date2, date1
else
isnegative = false
end
-- It is known that date1 <= date2.
local y1, m1, d1 = date1.year, date1.month, date1.day
local y2, m2, d2 = date2.year, date2.month, date2.day
local years, months, days = y2 - y1, m2 - m1, d2 - d1
if days < 0 then
days = days + days_in_month(y1, m1)
months = months - 1
end
if months < 0 then
months = months + 12
years = years - 1
end
self.years, self.months, self.days, self.isnegative = years, months, days, isnegative
return self
end
function DateDiff:age_ym()
-- Return text specifying difference in years, months.
local sign = self.isnegative and MINUS or ''
local mtext = number_name(self.months, 'month')
local result
if self.years > 0 then
local ytext = number_name(self.years, 'year')
if self.months == 0 then
result = ytext
else
result = ytext .. ', ' .. mtext
end
else
if self.months == 0 then
sign = ''
end
result = mtext
end
return sign .. result
end
local function error_wikitext(text)
-- Return message for display when template parameters are invalid.
local prefix = '[[Module talk:Age|Module error]]:'
local cat = '[[Category:Age error]]'
return '<span style="color:black; background-color:pink;">' ..
prefix .. ' ' .. text .. cat .. '</span>'
end
local function age_days(frame)
-- Return age in days between two given dates, or
-- between given date and current date.
-- This code implements the logic in [[Template:Age in days]].
-- Like {{Age in days}}, a missing argument is replaced from the current
-- date, so can get a bizarre mixture of specified/current y/m/d.
local args = frame:getParent().args
local year1 = date_component(args.year1 , args[1], 'year' )
local month1 = date_component(args.month1, args[2], 'month')
local day1 = date_component(args.day1 , args[3], 'day' )
local year2 = date_component(args.year2 , args[4], 'year' )
local month2 = date_component(args.month2, args[5], 'month')
local day2 = date_component(args.day2 , args[6], 'day' )
local gsd1 = gsd(year1, month1, day1)
local gsd2 = gsd(year2, month2, day2)
if gsd1 and gsd2 then
local sign = ''
local result = gsd2 - gsd1
if result < 0 then
sign = MINUS
result = -result
end
return sign .. tostring(result)
end
return error_wikitext('Cannot handle dates before the year 1 AD')
end
local function age_ym(frame)
-- Return age in years and months between two given dates, or
-- between given date and current date.
local args = frame:getParent().args
local fields = {}
for i = 1, 6 do
fields[i] = strip_to_nil(args[i])
end
local date1, date2
if fields[1] and fields[2] and fields[3] then
date1 = Date:new():set_ymd(fields[1], fields[2], fields[3])
end
if not (date1 and date1.isvalid) then
return error_wikitext('Need date: year, month, day')
end
if fields[4] and fields[5] and fields[6] then
date2 = Date:new():set_ymd(fields[4], fields[5], fields[6])
if not date2.isvalid then
return error_wikitext('Second date should be year, month, day')
end
else
date2 = Date:new():set_current()
end
return DateDiff:new():set(date1, date2):age_ym()
end
local function gsd_ymd(frame)
-- Return Gregorian serial day of the given date, or the current date.
-- Like {{Gregorian serial date}}, a missing argument is replaced from the
-- current date, so can get a bizarre mixture of specified/current y/m/d.
-- This accepts positional arguments, although the original template does not.
local args = frame:getParent().args
local year = date_component(args.year , args[1], 'year' )
local month = date_component(args.month, args[2], 'month')
local day = date_component(args.day , args[3], 'day' )
local result = gsd(year, month, day)
if result then
return tostring(result)
end
return error_wikitext('Cannot handle dates before the year 1 AD')
end
return { age_days = age_days, age_ym = age_ym, gsd = gsd_ymd }