Modul:Age: Perbedaan antara revisi
dw>Johnuniq (change default for {{age}} from range=no to range=dash per Template talk:Age#Inaccurate age shown) |
(←Membuat halaman berisi '-- Implement various "age of" and other date-related templates. local _Date, _currentDate local function getExports(frame) -- Return objects exported from the date module or its sandbox. if not _Date then local sandbox = frame:getTitle():find('sandbox', 1, true) and '/sandbox' or '' local datemod = require('Module:Date' .. sandbox) _Date = datemod._Date _currentDate = datemod._current end return _Date, _currentDate end local Collection -- a table t...') |
||
Baris 1: | Baris 1: | ||
-- Implement various "age of" and other date-related templates. | -- Implement various "age of" and other date-related templates. | ||
local _Date, _currentDate | local _Date, _currentDate | ||
Baris 64: | Baris 5: | ||
-- Return objects exported from the date module or its sandbox. | -- Return objects exported from the date module or its sandbox. | ||
if not _Date then | if not _Date then | ||
local sandbox = frame:getTitle():find( | local sandbox = frame:getTitle():find('sandbox', 1, true) and '/sandbox' or '' | ||
local datemod = require( | local datemod = require('Module:Date' .. sandbox) | ||
_Date = datemod._Date | |||
_currentDate = datemod._current | _currentDate = datemod._current | ||
end | end | ||
return _Date, _currentDate | return _Date, _currentDate | ||
Baris 116: | Baris 46: | ||
end | end | ||
return text | return text | ||
end | end | ||
Baris 138: | Baris 55: | ||
end | end | ||
local function message(msg, | local function message(msg, id) | ||
-- Return formatted message text for an error or warning. | -- Return formatted message text for an error or warning. | ||
local categories = { | local categories = { | ||
error = | error = '[[Category:Age error]]', | ||
warning = | warning = '[[Category:Age error]]', -- same as error until determine whether 'Age warning' would be worthwhile | ||
} | } | ||
local a, b | local a, b, category | ||
if id == 'warning' then | |||
a = '<sup>[<i>' | a = '<sup>[<i>' | ||
b = '</i>]</sup>' | b = '</i>]</sup>' | ||
else | else | ||
a = '<strong class="error">' | a = '<strong class="error">Error: ' | ||
b = '</strong>' | b = '</strong>' | ||
end | end | ||
if mw.title.getCurrentTitle():inNamespaces(0) then | if mw.title.getCurrentTitle():inNamespaces(0) then | ||
-- Category only in namespaces: 0=article. | -- Category only in namespaces: 0=article. | ||
category = | category = categories[id or 'error'] | ||
end | end | ||
return | return | ||
a .. | a .. | ||
mw.text.nowiki( | mw.text.nowiki(msg) .. | ||
b .. | b .. | ||
(category or '') | (category or '') | ||
Baris 188: | Baris 99: | ||
end | end | ||
return groups:join(',') | return groups:join(',') | ||
end | end | ||
Baris 226: | Baris 105: | ||
-- Assume value is a valid number which has not overflowed. | -- Assume value is a valid number which has not overflowed. | ||
if sortable == 'sortable_table' or sortable == 'sortable_on' or sortable == 'sortable_debug' then | if sortable == 'sortable_table' or sortable == 'sortable_on' or sortable == 'sortable_debug' then | ||
local | local sortkey | ||
if value == 0 then | if value == 0 then | ||
sortkey = '5000000000000000000' | |||
else | else | ||
local mag = math.floor(math.log10(math.abs(value)) + 1e-14) | local mag = math.floor(math.log10(math.abs(value)) + 1e-14) | ||
local prefix | |||
if value > 0 then | if value > 0 then | ||
prefix = 7000 + mag | |||
else | else | ||
prefix = 2999 - mag | |||
value = value + 10^(mag+1) | value = value + 10^(mag+1) | ||
end | end | ||
sortkey = string.format('%d', prefix) .. string.format('%015.0f', math.floor(value * 10^(14-mag))) | |||
end | end | ||
local | local lhs, rhs | ||
if sortable == 'sortable_table' then | if sortable == 'sortable_table' then | ||
lhs = 'data-sort-value="' | |||
rhs = '"|' | |||
else | else | ||
lhs = sortable == 'sortable_debug' and | |||
'<span style="border:1px solid;display:inline;" class="sortkey">' or | |||
'<span style="display:none" class="sortkey">' | |||
rhs = '♠</span>' | |||
end | end | ||
return | return lhs .. sortkey .. rhs | ||
end | end | ||
end | end | ||
Baris 267: | Baris 149: | ||
on = 'on', | on = 'on', | ||
yes = 'on', | yes = 'on', | ||
bulan = 'ym', | |||
minggu = 'ymw', | |||
hari = 'ymd', | |||
jam = 'ymdh', | |||
}, | }, | ||
sep = { | sep = { | ||
Baris 293: | Baris 175: | ||
hm = { 'H', 'M', id = 'hm' }, | hm = { 'H', 'M', id = 'hm' }, | ||
hms = { 'H', 'M', 'S', id = 'hms' }, | hms = { 'H', 'M', 'S', id = 'hms' }, | ||
d = { 'd', id = 'd' }, | d = { 'd', id = 'd' }, | ||
dh = { 'd', 'H', id = 'dh' }, | dh = { 'd', 'H', id = 'dh' }, | ||
Baris 310: | Baris 190: | ||
debug = 'sortable_debug', | debug = 'sortable_debug', | ||
}, | }, | ||
} | } | ||
Baris 337: | Baris 206: | ||
table.insert(parms, 'partial') | table.insert(parms, 'partial') | ||
end | end | ||
local date = Date(unpack(parms)) | local date = Date(unpack(parms)) | ||
if not date then | if not date then | ||
return message('Need valid date') | |||
return message(' | |||
end | end | ||
local add = stripToNil(args.add) | local add = stripToNil(args.add) | ||
Baris 350: | Baris 215: | ||
date = date + item | date = date + item | ||
if not date then | if not date then | ||
return message(' | return message('Cannot add "' .. item .. '"') | ||
end | end | ||
end | end | ||
end | end | ||
local | local prefix, result | ||
local sortable = translateParameters.sortable[args.sortable] | local sortable = translateParameters.sortable[args.sortable] | ||
if sortable then | if sortable then | ||
local value = (date.partial and date.partial.first or date).jdz | local value = (date.partial and date.partial.first or date).jdz | ||
prefix = makeSort(value, sortable) | |||
end | end | ||
local show = stripToNil(args.show) or 'dmy' | |||
if show ~= 'hide' then | if show ~= 'hide' then | ||
result = date[show] | result = date[show] | ||
if result == nil then | if result == nil then | ||
result = | result = date:text(show) | ||
elseif type(result) == 'boolean' then | elseif type(result) == 'boolean' then | ||
result = result and '1' or '0' | result = result and '1' or '0' | ||
else | else | ||
result = | result = tostring(result) | ||
end | end | ||
end | end | ||
return ( | return (prefix or '') .. (result or '') | ||
end | end | ||
local function makeText(values, components, names, options | local function makeText(values, components, names, options) | ||
-- Return wikitext representing an age or duration. | -- Return wikitext representing an age or duration. | ||
local text = Collection.new() | local text = Collection.new() | ||
Baris 388: | Baris 249: | ||
if (islist or v > 0) or (text.n == 0 and i == count) or (text.n > 0 and components.keepZero) then | if (islist or v > 0) or (text.n == 0 and i == count) or (text.n > 0 and components.keepZero) then | ||
local fmt, vstr | local fmt, vstr | ||
if | if i == 1 and options.format == 'format_commas' then | ||
-- Numbers after the first should be small and not need formatting. | -- Numbers after the first should be small and not need formatting. | ||
fmt = formatNumber | fmt = formatNumber | ||
Baris 399: | Baris 256: | ||
end | end | ||
if islist then | if islist then | ||
vstr = fmt(v[1]) .. | local join = options.range == 'dash' and '–' or ' atau ' | ||
vstr = fmt(v[1]) .. join .. fmt(v[2]) | |||
else | else | ||
vstr = fmt(v) | vstr = fmt(v) | ||
Baris 407: | Baris 263: | ||
local name = names[components[i]] | local name = names[components[i]] | ||
if name then | if name then | ||
local plural = names.plural | |||
if not plural or (islist and v[2] or v) == 1 then | |||
plural = '' | |||
end | end | ||
text:add(vstr .. sep .. name) | text:add(vstr .. sep .. name .. plural) | ||
else | else | ||
text:add(vstr) | text:add(vstr) | ||
Baris 425: | Baris 282: | ||
elseif options.join == 'sep_serialcomma' and text.n > 2 then | elseif options.join == 'sep_serialcomma' and text.n > 2 then | ||
first = ', ' | first = ', ' | ||
last = | last = ', dan ' | ||
else | else | ||
first = ', ' | first = ', ' | ||
last = | last = ' dan ' | ||
end | end | ||
for i, v in ipairs(text) do | for i, v in ipairs(text) do | ||
Baris 447: | Baris 304: | ||
end | end | ||
return | return | ||
(options. | (options.prefix or '') .. | ||
sign .. | sign .. | ||
text:join() .. | text:join() .. | ||
Baris 458: | Baris 314: | ||
-- which have been validated. | -- which have been validated. | ||
local names = { | local names = { | ||
abbr_off = { | abbr_off = { | ||
plural = '', | |||
sep = ' ', | sep = ' ', | ||
y = | y = 'tahun', | ||
m = | m = 'bulan', | ||
w = | w = 'minggu', | ||
d = | d = 'hari', | ||
H = | H = 'jam', | ||
M = | M = 'menit', | ||
S = | S = 'detik', | ||
}, | }, | ||
abbr_on = { | abbr_on = { | ||
y = ' | y = 'thn', | ||
m = ' | m = 'bln', | ||
w = ' | w = 'mgg', | ||
d = ' | d = 'hr', | ||
H = ' | H = 'jam', | ||
M = ' | M = 'mnt', | ||
S = ' | S = 'dtk', | ||
}, | }, | ||
abbr_infant = { -- for {{age for infant}} | abbr_infant = { -- for {{age for infant}} | ||
plural = '', | |||
sep = ' ', | sep = ' ', | ||
y = | y = 'thn', | ||
m = | m = 'bln', | ||
w = | w = 'mgg', | ||
d = | d = 'hr', | ||
H = | H = 'jam', | ||
M = | M = 'mnt', | ||
S = | S = 'dtk', | ||
}, | }, | ||
abbr_raw = {}, | abbr_raw = {}, | ||
Baris 529: | Baris 383: | ||
range = parms.range and true or nil, | range = parms.range and true or nil, | ||
} | } | ||
local | local prefix | ||
if parms.sortable then | if parms.sortable then | ||
local value = diff.age_days + (parms.wantDuration and 1 or 0) -- days and fraction of a day | local value = diff.age_days + (parms.wantDuration and 1 or 0) -- days and fraction of a day | ||
Baris 535: | Baris 389: | ||
value = -value | value = -value | ||
end | end | ||
prefix = makeSort(value, parms.sortable) | |||
end | end | ||
local textOptions = { | local textOptions = { | ||
prefix = prefix, | |||
suffix = parms.suffix, -- not currently used | |||
format = parms.format, | format = parms.format, | ||
join = parms.sep or defaultJoin, | join = parms.sep or defaultJoin, | ||
isnegative = diff.isnegative, | isnegative = diff.isnegative, | ||
range = parms.range, | range = parms.range, | ||
} | } | ||
if show.id == 'hide' then | if show.id == 'hide' then | ||
return | return prefix or '' | ||
end | end | ||
local values = { diff:age(show.id, diffOptions) } | local values = { diff:age(show.id, diffOptions) } | ||
Baris 561: | Baris 413: | ||
join = textOptions.join, | join = textOptions.join, | ||
isnegative = textOptions.isnegative, | isnegative = textOptions.isnegative, | ||
} | } | ||
return | return | ||
(textOptions. | (textOptions.prefix or '') .. | ||
makeText({ diff.partial.mindiff:age(show.id, diffOptions) }, show, names[abbr], opt) .. | makeText({ diff.partial.mindiff:age(show.id, diffOptions) }, show, names[abbr], opt) .. | ||
(textOptions.range == 'dash' and '–' or ' atau ') .. | |||
makeText({ diff.partial.maxdiff:age(show.id, diffOptions) }, show, names[abbr], opt | makeText({ diff.partial.maxdiff:age(show.id, diffOptions) }, show, names[abbr], opt) .. | ||
(textOptions.suffix or '') | (textOptions.suffix or '') | ||
end | end | ||
return message(' | return message('Parameter show=' .. show.id .. ' is not supported here') | ||
end | end | ||
Baris 578: | Baris 429: | ||
-- * date1, date2 (two date tables, if not single) | -- * date1, date2 (two date tables, if not single) | ||
-- * text (a string error message) | -- * text (a string error message) | ||
-- A missing date is | -- A missing date is replaced with the current date. | ||
-- If wantMixture is true, a missing date component is replaced | -- If wantMixture is true, a missing date component is replaced | ||
-- from the current date, so can get a bizarre mixture of | -- from the current date, so can get a bizarre mixture of | ||
Baris 585: | Baris 436: | ||
local Date, currentDate = getExports(frame) | local Date, currentDate = getExports(frame) | ||
getopt = getopt or {} | getopt = getopt or {} | ||
local | local fix = getopt.fix and 'fix' or '' | ||
local partial = getopt.partial and 'partial' or '' | |||
local args = frame:getParent().args | local args = frame:getParent().args | ||
local fields = {} | local fields = {} | ||
Baris 623: | Baris 460: | ||
if fields[i] then | if fields[i] then | ||
imax = i | imax = i | ||
end | end | ||
end | end | ||
local | local noDefault = imax == 0 and getopt.noMissing | ||
local dates = {} | local dates = {} | ||
if isNamed or imax >= 3 then | if isNamed or imax >= 3 then | ||
Baris 644: | Baris 474: | ||
for i = 1, nrDates do | for i = 1, nrDates do | ||
local index = i == 1 and 1 or 4 | local index = i == 1 and 1 or 4 | ||
dates[i] = Date(fields[index], fields[index+1], fields[index+2]) | |||
end | end | ||
else | else | ||
for i = 1, nrDates do | for i = 1, nrDates do | ||
local index = i == 1 and 1 or 4 | local index = i == 1 and 1 or 4 | ||
local y, m, d = fields[index], fields[index+1], fields[index+2] | local y, m, d = fields[index], fields[index+1], fields[index+2] | ||
if ( | if (partial and y) or (y and m and d) then | ||
dates[i] = Date(fix, | dates[i] = Date(fix, partial, y, m, d) | ||
elseif not y and not m and not d then | elseif not y and not m and not d and not noDefault then | ||
dates[i] = Date( | dates[i] = Date('currentdate') | ||
end | end | ||
end | end | ||
end | end | ||
elseif not noDefault then | |||
getopt.textdates = true -- have parsed each date from a single text field | getopt.textdates = true -- have parsed each date from a single text field | ||
dates[1] = Date(fix, | dates[1] = Date(fix, partial, fields[1] or 'currentdate') | ||
if not getopt.single then | if not getopt.single then | ||
dates[2] = Date(fix, | dates[2] = Date(fix, partial, fields[2] or 'currentdate') | ||
end | end | ||
end | end | ||
if not dates[1] then | if not dates[1] then | ||
return message( | return message('Need valid year, month, day') | ||
end | end | ||
if getopt.single then | if getopt.single then | ||
Baris 687: | Baris 501: | ||
end | end | ||
if not dates[2] then | if not dates[2] then | ||
return message( | return message('Second date should be year, month, day') | ||
end | end | ||
return dates[1], dates[2] | return dates[1], dates[2] | ||
Baris 698: | Baris 512: | ||
local name = frame.args.template | local name = frame.args.template | ||
if not name then | if not name then | ||
return message(' | return message('The template invoking this must have "|template=x" where x is the wanted operation') | ||
end | end | ||
local args = frame:getParent().args | local args = frame:getParent().args | ||
Baris 727: | Baris 541: | ||
show = 'y', | show = 'y', | ||
abbr = 'abbr_raw', | abbr = 'abbr_raw', | ||
}, | }, | ||
age_full_years_nts = { -- {{age nts}} | age_full_years_nts = { -- {{age nts}} | ||
Baris 795: | Baris 606: | ||
local spec = specs[name] | local spec = specs[name] | ||
if not spec then | if not spec then | ||
return message(' | return message('The specified template name is not valid') | ||
end | end | ||
if name == 'age_days' then | if name == 'age_days' then | ||
Baris 806: | Baris 617: | ||
end | end | ||
end | end | ||
local partial | local partial | ||
local range = stripToNil(args.range) or spec.range | local range = stripToNil(args.range) or spec.range | ||
if range then | if range then | ||
Baris 812: | Baris 623: | ||
-- "|range=" (empty value) has no effect (spec is used). | -- "|range=" (empty value) has no effect (spec is used). | ||
-- "|range=yes" or spec.range == true sets range = true (gives "11 or 12") | -- "|range=yes" or spec.range == true sets range = true (gives "11 or 12") | ||
-- "|range=dash" | -- "|range=dash" sets range = 'dash' (gives "11–12"). | ||
-- "|range=no" | -- "|range=no" sets range = nil (gives "12"). | ||
-- | -- Above gives a good result with age in years, but is probably unhelpful for other cases. | ||
-- {{age in years|year1=1900|year2=1910|range=no}} → 10 | |||
-- {{age in days|year1=1900|year2=1910|range=no}} → 4016 (from 1900-01-01 to 1910-12-31) | |||
-- "|range=OTHER" sets range = nil and rejects partial dates. | -- "|range=OTHER" sets range = nil and rejects partial dates. | ||
range = ({ dash = 'dash | range = ({ dash = 'dash', no = 'no', [true] = true })[range] or yes(range) | ||
if range then | if range then | ||
partial = true -- accept partial dates with a possible age range for the result | partial = true -- accept partial dates with a possible age range for the result | ||
if range == 'no' then | if range == 'no' then | ||
range = nil | range = nil | ||
end | end | ||
Baris 827: | Baris 639: | ||
local getopt = { | local getopt = { | ||
fix = yes(args.fix), | fix = yes(args.fix), | ||
partial = partial, | partial = partial, | ||
wantMixture = spec.wantMixture, | wantMixture = spec.wantMixture, | ||
Baris 837: | Baris 647: | ||
end | end | ||
local format = stripToNil(args.format) | local format = stripToNil(args.format) | ||
if format then | if format then | ||
format = 'format_' .. format | format = 'format_' .. format | ||
Baris 844: | Baris 653: | ||
end | end | ||
local parms = { | local parms = { | ||
diff = date2 | diff = date2 - date1, | ||
wantDuration = spec.duration or yes(args.duration), | wantDuration = spec.duration or yes(args.duration), | ||
range = range, | range = range, | ||
Baris 851: | Baris 660: | ||
abbr = spec.abbr, | abbr = spec.abbr, | ||
disp = spec.disp, | disp = spec.disp, | ||
format = format or spec.format, | format = format or spec.format, | ||
round = yes(args.round), | round = yes(args.round), | ||
sep = spec.sep, | sep = spec.sep, | ||
sortable = translateParameters.sortable[args.sortable or spec.sortable], | sortable = translateParameters.sortable[args.sortable or spec.sortable], | ||
} | } | ||
if (spec.negative or frame.args.negative) == 'error' and parms.diff.isnegative then | if (spec.negative or frame.args.negative) == 'error' and parms.diff.isnegative then | ||
return message(' | return message('The second date should not be before the first date') | ||
end | end | ||
return | return dateDifference(parms) | ||
end | end | ||
Baris 867: | Baris 674: | ||
-- Implement [[Template:Birth date and age]]. | -- Implement [[Template:Birth date and age]]. | ||
local args = frame:getParent().args | local args = frame:getParent().args | ||
local options = { | local options = { noMissing=true, single=true } | ||
local date = getDates(frame, options) | local date = getDates(frame, options) | ||
if type(date) == 'string' then | if type(date) == 'string' then | ||
Baris 879: | Baris 682: | ||
local diff = Date('currentdate') - date | local diff = Date('currentdate') - date | ||
if diff.isnegative or diff.years > 150 then | if diff.isnegative or diff.years > 150 then | ||
return message(' | return message('Invalid birth date for calculating age') | ||
end | end | ||
local disp = | local disp, show = 'disp_raw', 'y' | ||
if diff.years < 2 then | if diff.years < 2 then | ||
disp = 'disp_age' | disp = 'disp_age' | ||
Baris 891: | Baris 693: | ||
end | end | ||
end | end | ||
local result = | local df = stripToNil(args.df) -- day first (dmy); default is month first (mdy) | ||
local result = df and | |||
'%-d %B %-Y' or | |||
'%B %-d, %-Y' | |||
result = '(<span class="bday">%-Y-%m-%d</span>) </span>' .. result | |||
result = '<span style="display:none"> ' .. | |||
date:text(result) .. | |||
'<span class="noprint ForceAgeToShow"> ' .. | |||
'(usia ' .. | |||
dateDifference({ | |||
diff = diff, | diff = diff, | ||
show = show, | show = show, | ||
Baris 901: | Baris 708: | ||
disp = disp, | disp = disp, | ||
sep = 'sep_space', | sep = 'sep_space', | ||
}) | }) .. | ||
')</span>' | |||
local warnings = tonumber(frame.args.warnings) | local warnings = tonumber(frame.args.warnings) | ||
if warnings and warnings > 0 then | if warnings and warnings > 0 then | ||
Baris 908: | Baris 715: | ||
df = true, | df = true, | ||
mf = true, | mf = true, | ||
template = true, | |||
day = true, | day = true, | ||
day1 = true, | day1 = true, | ||
Baris 931: | Baris 739: | ||
end | end | ||
if invalid then | if invalid then | ||
result = result .. message(' | result = result .. message('invalid parameter ' .. invalid, 'warning') | ||
end | end | ||
end | end | ||
Baris 1.047: | Baris 765: | ||
local date = Date('juliandate', args[1], args[2]) | local date = Date('juliandate', args[1], args[2]) | ||
if date then | if date then | ||
return | return date:text() | ||
end | end | ||
return message(' | return message('Need valid Julian date number') | ||
end | end | ||
Baris 1.062: | Baris 780: | ||
return tostring(date.jd) | return tostring(date.jd) | ||
end | end | ||
return message(' | return message('Need valid year/month/day or "currentdate"') | ||
end | end | ||
Baris 1.073: | Baris 791: | ||
local args = frame:getParent().args | local args = frame:getParent().args | ||
local parms = { | local parms = { | ||
wantDuration = yes(args.duration), | wantDuration = yes(args.duration), | ||
range = yes(args.range) or (args.range == 'dash' and 'dash' or nil), | range = yes(args.range) or (args.range == 'dash' and 'dash' or nil), | ||
Baris 1.081: | Baris 798: | ||
local date1 = Date(fix, 'partial', stripToNil(args[1]) or 'currentdatetime') | local date1 = Date(fix, 'partial', stripToNil(args[1]) or 'currentdatetime') | ||
if not date1 then | if not date1 then | ||
return message(' | return message('Invalid start date in first parameter') | ||
end | end | ||
local date2 = Date(fix, 'partial', stripToNil(args[2]) or 'currentdatetime') | local date2 = Date(fix, 'partial', stripToNil(args[2]) or 'currentdatetime') | ||
if not date2 then | if not date2 then | ||
return message(' | return message('Invalid end date in second parameter') | ||
end | end | ||
parms.diff = date2 - date1 | parms.diff = date2 - date1 | ||
Baris 1.093: | Baris 810: | ||
parm = translate[parm] | parm = translate[parm] | ||
if parm == nil then -- test for nil because false is a valid setting | if parm == nil then -- test for nil because false is a valid setting | ||
return message(' | return message('Parameter ' .. argname .. '=' .. args[argname] .. ' is invalid') | ||
end | end | ||
parms[argname] = parm | parms[argname] = parm | ||
Baris 1.104: | Baris 821: | ||
if show then | if show then | ||
if show.id ~= round then | if show.id ~= round then | ||
return message(' | return message('Parameter show=' .. args.show .. ' conflicts with round=' .. args.round) | ||
end | end | ||
else | else | ||
Baris 1.112: | Baris 829: | ||
parms.round = true | parms.round = true | ||
end | end | ||
return | return dateDifference(parms) | ||
end | end | ||
Baris 1.118: | Baris 835: | ||
age_generic = ageGeneric, -- can emulate several age templates | age_generic = ageGeneric, -- can emulate several age templates | ||
birth_date_and_age = bda, -- Template:Birth_date_and_age | birth_date_and_age = bda, -- Template:Birth_date_and_age | ||
gsd = dateToGsd, -- Template:Gregorian_serial_date | gsd = dateToGsd, -- Template:Gregorian_serial_date | ||
extract = dateExtract, -- Template:Extract | extract = dateExtract, -- Template:Extract |
Revisi per 23 September 2023 11.51
Modul ini dilindungi. Modul ini sangat mencolok yang digunakan oleh banyak halaman, atau sangat sering disubstitusikan. Karena vandalisme atau kesalahan akan mempengaruhi banyak halaman, dan suntingan kecil dapat memberi beban besar pada server, modul ini dilindungi dari penyuntingan. |
Modul Lua ini digunakan pada banyak halaman dan perubahannya kemungkinan dipantau. Uji cobalah di subhalaman /bak pasir atau /kasus uji modul, atau bak pasir modul Anda. Pertimbangkan untuk mendiskusikan perubahan di halaman pembicaraan sebelum mengimplementasikannya. |
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 |
---|---|
{{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}}
→ 11 tahun, 1 bulan{{age in years and months|year=2001|month=1|day=10|year2=2012|month2=2|day2=20}}
→ 11 tahun, 1 bulan{{age in years and months|2001|1|10|2012|2|20}}
→ 11 tahun, 1 bulan{{age in years and months|2001-1-10|2012-2-20}}
→ 11 tahun, 1 bulan{{age in years and months|10 Jan 2001|20 Feb 2012}}
→ 11 tahun, 1 bulan{{age in years and months|January 10, 2001|Feb 20, 2012}}
→ 11 tahun, 1 bulan
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}}
→ −12 tahun, 9 bulan{{age in years and months||||2012|2|20}}
→ −12 tahun, 9 bulan{{age in years and months||2012-2-20}}
→ −12 tahun, 9 bulan{{age in years and months||20 Feb 2012}}
→ −12 tahun, 9 bulan{{age in years and months||Feb 20, 2012}}
→ −12 tahun, 9 bulan{{age in years and months|year1=2001|month1=1|day1=10}}
→ 23 tahun, 10 bulan{{age in years and months|year=2001|month=1|day=10}}
→ 23 tahun, 10 bulan{{age in years and months|2001|1|10}}
→ 23 tahun, 10 bulan{{age in years and months|2001-1-10}}
→ 23 tahun, 10 bulan{{age in years and months|10 Jan 2001}}
→ 23 tahun, 10 bulan{{age in years and months|January 10, 2001}}
→ 23 tahun, 10 bulan
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. |
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}}
→ 11 tahun, 0 atau 1 bulan{{age in years and months|2001|1||2012|2|range=yes}}
→ 11 tahun, 0 atau 1 bulan{{age in years and months|Jan 2001|Feb 2012|range=yes}}
→ 11 tahun, 0 atau 1 bulan{{age in years and months|Jan 2001|Feb 2012|range=dash}}
→ 11 tahun, 0–1 bulan{{age in years and months|Jan 2001|Feb 2012|range=no}}
→ 11 tahun, 1 bulan (assume 1 Jan 2001 to 1 Feb 2012){{age in years and months|12 Jan 2001|Feb 2012|range=no}}
→ 11 tahun, 1 bulan (assume 12 Jan 2001 to 12 Feb 2012){{age in years and months|2001|2012|range=no}}
→ 11 tahun (assume 1 Jan 2001 to 1 Jan 2012){{age in years and months|2001|23 Feb 2012|range=no}}
→ 11 tahun (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}}
→ 7003405800000000000♠11 tahun, 1 bulan{{age in years and months|10 Jan 2001|6:00 am 20 Feb 2012|sortable=debug}}
→ 7003405825000000000♠11 tahun, 1 bulan{{age in years and months|10 Jan 2001|6:00 am 20 Feb 2012|sortable=debug|show=hide}}
→ 7003405825000000000♠
An extra day is added for a duration.
{{age in years and months|20 Jan 2001|19 Feb 2012}}
→ 11 tahun (one day short of 11 years, 1 month){{age in years and months|20 Jan 2001|19 Feb 2012|duration=on}}
→ 11 tahun, 1 bulan
The least-significant time unit can be rounded.
{{age in years and months|20 Jan 2001|10 Feb 2012}}
→ 11 tahun{{age in years and months|20 Jan 2001|10 Feb 2012|round=on}}
→ 11 tahun, 1 bulan (round to nearest month)
Large numbers can be formatted with commas.
{{age in years and months|120|2012|format=commas|range=yes}}
→ 1,891 atau 1,892 tahun{{age in years and months|120|2012|format=commas|range=dash}}
→ 1,891–1,892 tahun
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}}
→ 120{{age|1898|01|01|2018|02|01|format=cardinal_us}}
→ 120{{age|1898|01|01|2018|02|01|format=Cardinal}}
→ 120{{age|1898|01|01|2018|02|01|format=Cardinal_us}}
→ 120{{age|1898|01|01|2018|02|01|format=Ordinal}}
→ 120{{age|1898|01|01|2018|02|01|format=Ordinal_us}}
→ 120{{age|1898|01|01|2018|02|01|format=ordinal}}
→ 120{{age|1898|01|01|2018|02|01|format=ordinal_us}}
→ 120{{age|1980|1990|range=yes|format=Cardinal}}
→ Kesalahan ekspresi: Karakter tanda baca "{" tidak dikenal.{{age in years, months and days|April 1980|1995|format=Cardinal|range=yes}}
→ 14 atau 15 tahun
See also
- {{time interval}} • This template supports all age/duration calculations and provides more options such as abbreviating or omitting units.
-- Implement various "age of" and other date-related templates. local _Date, _currentDate local function getExports(frame) -- Return objects exported from the date module or its sandbox. if not _Date then local sandbox = frame:getTitle():find('sandbox', 1, true) and '/sandbox' or '' local datemod = require('Module:Date' .. sandbox) _Date = datemod._Date _currentDate = datemod._current end return _Date, _currentDate end local Collection -- a table to hold items Collection = { add = function (self, item) if item ~= nil then self.n = self.n + 1 self[self.n] = item end end, join = function (self, sep) return table.concat(self, sep) end, remove = function (self, pos) if self.n > 0 and (pos == nil or (0 < pos and pos <= self.n)) then self.n = self.n - 1 return table.remove(self, pos) end end, sort = function (self, comp) table.sort(self, comp) end, new = function () return setmetatable({n = 0}, Collection) end } Collection.__index = Collection local function stripToNil(text) -- If text is a string, return its trimmed content, or nil if empty. -- Otherwise return text (which may, for example, be nil). if type(text) == 'string' then text = text:match('(%S.-)%s*$') end return text end local function yes(parameter) -- Return true if parameter should be interpreted as "yes". -- Do not want to accept mixed upper/lowercase unless done by current templates. -- Need to accept "on" because "round=on" is wanted. return ({ y = true, yes = true, on = true })[parameter] end local function message(msg, id) -- Return formatted message text for an error or warning. local categories = { error = '[[Category:Age error]]', warning = '[[Category:Age error]]', -- same as error until determine whether 'Age warning' would be worthwhile } local a, b, category if id == 'warning' then a = '<sup>[<i>' b = '</i>]</sup>' else a = '<strong class="error">Error: ' b = '</strong>' end if mw.title.getCurrentTitle():inNamespaces(0) then -- Category only in namespaces: 0=article. category = categories[id or 'error'] end return a .. mw.text.nowiki(msg) .. b .. (category or '') end local function formatNumber(number) -- Return the given number formatted with commas as group separators, -- given that the number is an integer. local numstr = tostring(number) local length = #numstr local places = Collection.new() local pos = 0 repeat places:add(pos) pos = pos + 3 until pos >= length places:add(length) local groups = Collection.new() for i = places.n, 2, -1 do local p1 = length - places[i] + 1 local p2 = length - places[i - 1] groups:add(numstr:sub(p1, p2)) end return groups:join(',') end local function makeSort(value, sortable) -- Return a sort key if requested. -- Assume value is a valid number which has not overflowed. if sortable == 'sortable_table' or sortable == 'sortable_on' or sortable == 'sortable_debug' then local sortkey if value == 0 then sortkey = '5000000000000000000' else local mag = math.floor(math.log10(math.abs(value)) + 1e-14) local prefix if value > 0 then prefix = 7000 + mag else prefix = 2999 - mag value = value + 10^(mag+1) end sortkey = string.format('%d', prefix) .. string.format('%015.0f', math.floor(value * 10^(14-mag))) end local lhs, rhs if sortable == 'sortable_table' then lhs = 'data-sort-value="' rhs = '"|' else lhs = sortable == 'sortable_debug' and '<span style="border:1px solid;display:inline;" class="sortkey">' or '<span style="display:none" class="sortkey">' rhs = '♠</span>' end return lhs .. sortkey .. rhs end end local translateParameters = { abbr = { off = 'abbr_off', on = 'abbr_on', }, disp = { age = 'disp_age', raw = 'disp_raw', }, format = { raw = 'format_raw', commas = 'format_commas', }, round = { on = 'on', yes = 'on', bulan = 'ym', minggu = 'ymw', hari = 'ymd', jam = 'ymdh', }, sep = { comma = 'sep_comma', [','] = 'sep_comma', serialcomma = 'sep_serialcomma', space = 'sep_space', }, show = { hide = { id = 'hide' }, y = { 'y', id = 'y' }, ym = { 'y', 'm', id = 'ym' }, ymd = { 'y', 'm', 'd', id = 'ymd' }, ymw = { 'y', 'm', 'w', id = 'ymw' }, ymwd = { 'y', 'm', 'w', 'd', id = 'ymwd' }, yd = { 'y', 'd', id = 'yd', keepZero = true }, m = { 'm', id = 'm' }, md = { 'm', 'd', id = 'md' }, w = { 'w', id = 'w' }, wd = { 'w', 'd', id = 'wd' }, h = { 'H', id = 'h' }, hm = { 'H', 'M', id = 'hm' }, hms = { 'H', 'M', 'S', id = 'hms' }, d = { 'd', id = 'd' }, dh = { 'd', 'H', id = 'dh' }, dhm = { 'd', 'H', 'M', id = 'dhm' }, dhms = { 'd', 'H', 'M', 'S', id = 'dhms' }, ymdh = { 'y', 'm', 'd', 'H', id = 'ymdh' }, ymdhm = { 'y', 'm', 'd', 'H', 'M', id = 'ymdhm' }, ymwdh = { 'y', 'm', 'w', 'd', 'H', id = 'ymwdh' }, ymwdhm = { 'y', 'm', 'w', 'd', 'H', 'M', id = 'ymwdhm' }, }, sortable = { off = false, on = 'sortable_on', table = 'sortable_table', debug = 'sortable_debug', }, } local function dateExtract(frame) -- Return part of a date after performing an optional operation. local Date = getExports(frame) local args = frame:getParent().args local parms = {} for i, v in ipairs(args) do parms[i] = v end if yes(args.fix) then table.insert(parms, 'fix') end if yes(args.partial) then table.insert(parms, 'partial') end local date = Date(unpack(parms)) if not date then return message('Need valid date') end local add = stripToNil(args.add) if add then for item in add:gmatch('%S+') do date = date + item if not date then return message('Cannot add "' .. item .. '"') end end end local prefix, result local sortable = translateParameters.sortable[args.sortable] if sortable then local value = (date.partial and date.partial.first or date).jdz prefix = makeSort(value, sortable) end local show = stripToNil(args.show) or 'dmy' if show ~= 'hide' then result = date[show] if result == nil then result = date:text(show) elseif type(result) == 'boolean' then result = result and '1' or '0' else result = tostring(result) end end return (prefix or '') .. (result or '') end local function makeText(values, components, names, options) -- Return wikitext representing an age or duration. local text = Collection.new() local count = #values local sep = names.sep or '' for i, v in ipairs(values) do -- v is a number (say 4 for 4 years), or a table ({4,5} for 4 or 5 years). local islist = type(v) == 'table' if (islist or v > 0) or (text.n == 0 and i == count) or (text.n > 0 and components.keepZero) then local fmt, vstr if i == 1 and options.format == 'format_commas' then -- Numbers after the first should be small and not need formatting. fmt = formatNumber else fmt = tostring end if islist then local join = options.range == 'dash' and '–' or ' atau ' vstr = fmt(v[1]) .. join .. fmt(v[2]) else vstr = fmt(v) end local name = names[components[i]] if name then local plural = names.plural if not plural or (islist and v[2] or v) == 1 then plural = '' end text:add(vstr .. sep .. name .. plural) else text:add(vstr) end end end local first, last if options.join == 'sep_space' then first = ' ' last = ' ' elseif options.join == 'sep_comma' then first = ', ' last = ', ' elseif options.join == 'sep_serialcomma' and text.n > 2 then first = ', ' last = ', dan ' else first = ', ' last = ' dan ' end for i, v in ipairs(text) do if i < text.n then text[i] = v .. (i + 1 < text.n and first or last) end end local sign = '' if options.isnegative then -- Do not display negative zero. if text.n > 1 or (text.n == 1 and text[1]:sub(1, 1) ~= '0' ) then if options.format == 'format_raw' then sign = '-' -- plain hyphen so result can be used in a calculation else sign = '−' -- Unicode U+2212 MINUS SIGN end end end return (options.prefix or '') .. sign .. text:join() .. (options.suffix or '') end local function dateDifference(parms) -- Return a formatted date difference using the given parameters -- which have been validated. local names = { abbr_off = { plural = '', sep = ' ', y = 'tahun', m = 'bulan', w = 'minggu', d = 'hari', H = 'jam', M = 'menit', S = 'detik', }, abbr_on = { y = 'thn', m = 'bln', w = 'mgg', d = 'hr', H = 'jam', M = 'mnt', S = 'dtk', }, abbr_infant = { -- for {{age for infant}} plural = '', sep = ' ', y = 'thn', m = 'bln', w = 'mgg', d = 'hr', H = 'jam', M = 'mnt', S = 'dtk', }, abbr_raw = {}, } local diff = parms.diff -- must be a valid date difference local show = parms.show -- may be nil; default is set below local abbr = parms.abbr or 'abbr_off' local defaultJoin if abbr ~= 'abbr_off' then defaultJoin = 'sep_space' end if not show then show = 'ymd' if parms.disp == 'disp_age' then if diff.years < 3 then defaultJoin = 'sep_space' if diff.years >= 1 then show = 'ym' else show = 'md' end else show = 'y' end end end if type(show) ~= 'table' then show = translateParameters.show[show] end if parms.disp == 'disp_raw' then defaultJoin = 'sep_space' abbr = 'abbr_raw' elseif parms.wantSc then defaultJoin = 'sep_serialcomma' end local diffOptions = { round = parms.round, duration = parms.wantDuration, range = parms.range and true or nil, } local prefix if parms.sortable then local value = diff.age_days + (parms.wantDuration and 1 or 0) -- days and fraction of a day if diff.isnegative then value = -value end prefix = makeSort(value, parms.sortable) end local textOptions = { prefix = prefix, suffix = parms.suffix, -- not currently used format = parms.format, join = parms.sep or defaultJoin, isnegative = diff.isnegative, range = parms.range, } if show.id == 'hide' then return prefix or '' end local values = { diff:age(show.id, diffOptions) } if values[1] then return makeText(values, show, names[abbr], textOptions) end if diff.partial then -- Handle a more complex range such as -- {{age_yd|20 Dec 2001|2003|range=yes}} → 1 year, 12 days or 2 years, 11 days local opt = { format = textOptions.format, join = textOptions.join, isnegative = textOptions.isnegative, } return (textOptions.prefix or '') .. makeText({ diff.partial.mindiff:age(show.id, diffOptions) }, show, names[abbr], opt) .. (textOptions.range == 'dash' and '–' or ' atau ') .. makeText({ diff.partial.maxdiff:age(show.id, diffOptions) }, show, names[abbr], opt) .. (textOptions.suffix or '') end return message('Parameter show=' .. show.id .. ' is not supported here') end local function getDates(frame, getopt) -- Parse template parameters and return one of: -- * date (a date table, if single) -- * date1, date2 (two date tables, if not single) -- * text (a string error message) -- A missing date is replaced with the current date. -- If wantMixture is true, a missing date component is replaced -- from the current date, so can get a bizarre mixture of -- specified/current y/m/d as has been done by some "age" templates. -- Some results may be placed in table getopt. local Date, currentDate = getExports(frame) getopt = getopt or {} local fix = getopt.fix and 'fix' or '' local partial = getopt.partial and 'partial' or '' local args = frame:getParent().args local fields = {} local isNamed = args.year or args.year1 or args.year2 or args.month or args.month1 or args.month2 or args.day or args.day1 or args.day2 if isNamed then fields[1] = args.year1 or args.year fields[2] = args.month1 or args.month fields[3] = args.day1 or args.day fields[4] = args.year2 fields[5] = args.month2 fields[6] = args.day2 else for i = 1, 6 do fields[i] = args[i] end end local imax = 0 for i = 1, 6 do fields[i] = stripToNil(fields[i]) if fields[i] then imax = i end end local noDefault = imax == 0 and getopt.noMissing local dates = {} if isNamed or imax >= 3 then local nrDates = getopt.single and 1 or 2 if getopt.wantMixture then -- Cannot be partial since empty fields are set from current. local components = { 'year', 'month', 'day' } for i = 1, nrDates * 3 do fields[i] = fields[i] or currentDate[components[i > 3 and i - 3 or i]] end for i = 1, nrDates do local index = i == 1 and 1 or 4 dates[i] = Date(fields[index], fields[index+1], fields[index+2]) end else for i = 1, nrDates do local index = i == 1 and 1 or 4 local y, m, d = fields[index], fields[index+1], fields[index+2] if (partial and y) or (y and m and d) then dates[i] = Date(fix, partial, y, m, d) elseif not y and not m and not d and not noDefault then dates[i] = Date('currentdate') end end end elseif not noDefault then getopt.textdates = true -- have parsed each date from a single text field dates[1] = Date(fix, partial, fields[1] or 'currentdate') if not getopt.single then dates[2] = Date(fix, partial, fields[2] or 'currentdate') end end if not dates[1] then return message('Need valid year, month, day') end if getopt.single then return dates[1] end if not dates[2] then return message('Second date should be year, month, day') end return dates[1], dates[2] end local function ageGeneric(frame) -- Return the result required by the specified template. -- Can use sortable=x where x = on/table/off/debug in any supported template. -- Some templates default to sortable=on but can be overridden. local name = frame.args.template if not name then return message('The template invoking this must have "|template=x" where x is the wanted operation') end local args = frame:getParent().args local specs = { age_days = { -- {{age in days}} show = 'd', disp = 'disp_raw', }, age_days_nts = { -- {{age in days nts}} show = 'd', disp = 'disp_raw', format = 'format_commas', sortable = 'on', }, duration_days = { -- {{duration in days}} show = 'd', disp = 'disp_raw', duration = true, }, duration_days_nts = { -- {{duration in days nts}} show = 'd', disp = 'disp_raw', format = 'format_commas', sortable = 'on', duration = true, }, age_full_years = { -- {{age}} show = 'y', abbr = 'abbr_raw', }, age_full_years_nts = { -- {{age nts}} show = 'y', abbr = 'abbr_raw', format = 'format_commas', sortable = 'on', }, age_in_years = { -- {{age in years}} show = 'y', abbr = 'abbr_raw', negative = 'error', range = 'dash', }, age_in_years_nts = { -- {{age in years nts}} show = 'y', abbr = 'abbr_raw', negative = 'error', range = 'dash', format = 'format_commas', sortable = 'on', }, age_infant = { -- {{age for infant}} -- Do not set show because special processing is done later. abbr = yes(args.abbr) and 'abbr_infant' or 'abbr_off', disp = 'disp_age', sep = 'sep_space', sortable = 'on', }, age_m = { -- {{age in months}} show = 'm', disp = 'disp_raw', }, age_w = { -- {{age in weeks}} show = 'w', disp = 'disp_raw', }, age_wd = { -- {{age in weeks and days}} show = 'wd', }, age_yd = { -- {{age in years and days}} show = 'yd', format = 'format_commas', sep = args.sep ~= 'and' and 'sep_comma' or nil, }, age_yd_nts = { -- {{age in years and days nts}} show = 'yd', format = 'format_commas', sep = args.sep ~= 'and' and 'sep_comma' or nil, sortable = 'on', }, age_ym = { -- {{age in years and months}} show = 'ym', sep = 'sep_comma', }, age_ymd = { -- {{age in years, months and days}} show = 'ymd', range = true, }, age_ymwd = { -- {{age in years, months, weeks and days}} show = 'ymwd', wantMixture = true, }, } local spec = specs[name] if not spec then return message('The specified template name is not valid') end if name == 'age_days' then local su = stripToNil(args['show unit']) if su then if su == 'abbr' or su == 'full' then spec.disp = nil spec.abbr = su == 'abbr' and 'abbr_on' or nil end end end local partial local range = stripToNil(args.range) or spec.range if range then -- Suppose partial dates are used and age could be 11 or 12 years. -- "|range=" (empty value) has no effect (spec is used). -- "|range=yes" or spec.range == true sets range = true (gives "11 or 12") -- "|range=dash" sets range = 'dash' (gives "11–12"). -- "|range=no" sets range = nil (gives "12"). -- Above gives a good result with age in years, but is probably unhelpful for other cases. -- {{age in years|year1=1900|year2=1910|range=no}} → 10 -- {{age in days|year1=1900|year2=1910|range=no}} → 4016 (from 1900-01-01 to 1910-12-31) -- "|range=OTHER" sets range = nil and rejects partial dates. range = ({ dash = 'dash', no = 'no', [true] = true })[range] or yes(range) if range then partial = true -- accept partial dates with a possible age range for the result if range == 'no' then range = nil end end end local getopt = { fix = yes(args.fix), partial = partial, wantMixture = spec.wantMixture, } local date1, date2 = getDates(frame, getopt) if type(date1) == 'string' then return date1 end local format = stripToNil(args.format) if format then format = 'format_' .. format elseif name == 'age_days' and getopt.textdates then format = 'format_commas' end local parms = { diff = date2 - date1, wantDuration = spec.duration or yes(args.duration), range = range, wantSc = yes(args.sc), show = args.show == 'hide' and 'hide' or spec.show, abbr = spec.abbr, disp = spec.disp, format = format or spec.format, round = yes(args.round), sep = spec.sep, sortable = translateParameters.sortable[args.sortable or spec.sortable], } if (spec.negative or frame.args.negative) == 'error' and parms.diff.isnegative then return message('The second date should not be before the first date') end return dateDifference(parms) end local function bda(frame) -- Implement [[Template:Birth date and age]]. local args = frame:getParent().args local options = { noMissing=true, single=true } local date = getDates(frame, options) if type(date) == 'string' then return date -- error text end local Date = getExports(frame) local diff = Date('currentdate') - date if diff.isnegative or diff.years > 150 then return message('Invalid birth date for calculating age') end local disp, show = 'disp_raw', 'y' if diff.years < 2 then disp = 'disp_age' if diff.years == 0 and diff.months == 0 then show = 'd' else show = 'm' end end local df = stripToNil(args.df) -- day first (dmy); default is month first (mdy) local result = df and '%-d %B %-Y' or '%B %-d, %-Y' result = '(<span class="bday">%-Y-%m-%d</span>) </span>' .. result result = '<span style="display:none"> ' .. date:text(result) .. '<span class="noprint ForceAgeToShow"> ' .. '(usia ' .. dateDifference({ diff = diff, show = show, abbr = 'abbr_off', disp = disp, sep = 'sep_space', }) .. ')</span>' local warnings = tonumber(frame.args.warnings) if warnings and warnings > 0 then local good = { df = true, mf = true, template = true, day = true, day1 = true, month = true, month1 = true, year = true, year1 = true, } local invalid local imax = options.textdates and 1 or 3 for k, _ in pairs(args) do if type(k) == 'number' then if k > imax then invalid = tostring(k) break end else if not good[k] then invalid = k break end end end if invalid then result = result .. message('invalid parameter ' .. invalid, 'warning') end end return result end local function dateToGsd(frame) -- Implement [[Template:Gregorian serial date]]. -- Return Gregorian serial date of the given date, or the current date. -- The returned value is negative for dates before 1 January 1 AD -- despite the fact that GSD is not defined for such dates. local date = getDates(frame, { wantMixture=true, single=true }) if type(date) == 'string' then return date end return tostring(date.gsd) end local function jdToDate(frame) -- Return formatted date from a Julian date. -- The result includes a time if the input includes a fraction. -- The word 'Julian' is accepted for the Julian calendar. local Date = getExports(frame) local args = frame:getParent().args local date = Date('juliandate', args[1], args[2]) if date then return date:text() end return message('Need valid Julian date number') end local function dateToJd(frame) -- Return Julian date (a number) from a date which may include a time, -- or the current date ('currentdate') or current date and time ('currentdatetime'). -- The word 'Julian' is accepted for the Julian calendar. local Date = getExports(frame) local args = frame:getParent().args local date = Date(args[1], args[2], args[3], args[4], args[5], args[6], args[7]) if date then return tostring(date.jd) end return message('Need valid year/month/day or "currentdate"') end local function timeInterval(frame) -- Implement [[Template:Time interval]]. -- There are two positional arguments: date1, date2. -- The default for each is the current date and time. -- Result is date2 - date1 formatted. local Date = getExports(frame) local args = frame:getParent().args local parms = { wantDuration = yes(args.duration), range = yes(args.range) or (args.range == 'dash' and 'dash' or nil), wantSc = yes(args.sc), } local fix = yes(args.fix) and 'fix' or '' local date1 = Date(fix, 'partial', stripToNil(args[1]) or 'currentdatetime') if not date1 then return message('Invalid start date in first parameter') end local date2 = Date(fix, 'partial', stripToNil(args[2]) or 'currentdatetime') if not date2 then return message('Invalid end date in second parameter') end parms.diff = date2 - date1 for argname, translate in pairs(translateParameters) do local parm = stripToNil(args[argname]) if parm then parm = translate[parm] if parm == nil then -- test for nil because false is a valid setting return message('Parameter ' .. argname .. '=' .. args[argname] .. ' is invalid') end parms[argname] = parm end end if parms.round then local round = parms.round local show = parms.show if round ~= 'on' then if show then if show.id ~= round then return message('Parameter show=' .. args.show .. ' conflicts with round=' .. args.round) end else parms.show = translateParameters.show[round] end end parms.round = true end return dateDifference(parms) end return { age_generic = ageGeneric, -- can emulate several age templates birth_date_and_age = bda, -- Template:Birth_date_and_age gsd = dateToGsd, -- Template:Gregorian_serial_date extract = dateExtract, -- Template:Extract jd_to_date = jdToDate, -- Template:? JULIANDAY = dateToJd, -- Template:JULIANDAY time_interval = timeInterval, -- Template:Time_interval }