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}}→ −13 tahun, 9 bulan{{age in years and months||||2012|2|20}}→ −13 tahun, 9 bulan{{age in years and months||2012-2-20}}→ −13 tahun, 9 bulan{{age in years and months||20 Feb 2012}}→ −13 tahun, 9 bulan{{age in years and months||Feb 20, 2012}}→ −13 tahun, 9 bulan{{age in years and months|year1=2001|month1=1|day1=10}}→ 24 tahun, 11 bulan{{age in years and months|year=2001|month=1|day=10}}→ 24 tahun, 11 bulan{{age in years and months|2001|1|10}}→ 24 tahun, 11 bulan{{age in years and months|2001-1-10}}→ 24 tahun, 11 bulan{{age in years and months|10 Jan 2001}}→ 24 tahun, 11 bulan{{age in years and months|January 10, 2001}}→ 24 tahun, 11 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
}