Editing Module:Age
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision | Your text | ||
Line 1: | Line 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 | ||
Line 64: | Line 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 | ||
Line 116: | Line 46: | ||
end | end | ||
return text | return text | ||
end | end | ||
Line 138: | Line 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 '') | ||
Line 195: | Line 106: | ||
-- i == 1 for the first number which can optionally start with an uppercase letter. | -- i == 1 for the first number which can optionally start with an uppercase letter. | ||
number = tostring(number) | number = tostring(number) | ||
return require( | return require('Module:ConvertNumeric').spell_number( | ||
number, | number, | ||
nil, -- fraction numerator | nil, -- fraction numerator | ||
Line 204: | Line 115: | ||
options.ordinal -- true: 'first' instead of 'one' | options.ordinal -- true: 'first' instead of 'one' | ||
) or number | ) or number | ||
end | end | ||
Line 226: | Line 121: | ||
-- 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 | ||
Line 293: | Line 191: | ||
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' }, | ||
Line 343: | Line 239: | ||
return 'error' | return 'error' | ||
end | end | ||
return message(' | return message('Need valid date') | ||
end | end | ||
local add = stripToNil(args.add) | local add = stripToNil(args.add) | ||
Line 350: | Line 246: | ||
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 | ||
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 rangeJoin(range) | local function rangeJoin(range) | ||
-- Return text to be used between a range of ages. | -- Return text to be used between a range of ages. | ||
return range == 'dash' and '–' or | return range == 'dash' and '–' or ' or ' | ||
end | end | ||
Line 407: | Line 303: | ||
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) | ||
Line 425: | Line 322: | ||
elseif options.join == 'sep_serialcomma' and text.n > 2 then | elseif options.join == 'sep_serialcomma' and text.n > 2 then | ||
first = ', ' | first = ', ' | ||
last = | last = ', and ' | ||
else | else | ||
first = ', ' | first = ', ' | ||
last = | last = ' and ' | ||
end | end | ||
for i, v in ipairs(text) do | for i, v in ipairs(text) do | ||
Line 447: | Line 344: | ||
end | end | ||
return | return | ||
(options. | (options.prefix or '') .. | ||
(options.extra or '') .. | (options.extra or '') .. | ||
sign .. | sign .. | ||
Line 458: | Line 355: | ||
-- which have been validated. | -- which have been validated. | ||
local names = { | local names = { | ||
abbr_off = { | abbr_off = { | ||
plural = 's', | |||
sep = ' ', | sep = ' ', | ||
y = | y = 'year', | ||
m = | m = 'month', | ||
w = | w = 'week', | ||
d = | d = 'day', | ||
H = | H = 'hour', | ||
M = | M = 'minute', | ||
S = | S = 'second', | ||
}, | }, | ||
abbr_on = { | abbr_on = { | ||
Line 482: | Line 376: | ||
}, | }, | ||
abbr_infant = { -- for {{age for infant}} | abbr_infant = { -- for {{age for infant}} | ||
plural = 's', | |||
sep = ' ', | sep = ' ', | ||
y = | y = 'yr', | ||
m = | m = 'mo', | ||
w = | w = 'wk', | ||
d = | d = 'day', | ||
H = | H = 'hr', | ||
M = | M = 'min', | ||
S = | S = 'sec', | ||
}, | }, | ||
abbr_raw = {}, | abbr_raw = {}, | ||
Line 529: | Line 424: | ||
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 | ||
Line 535: | Line 430: | ||
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 | |||
extra = parms.extra, | extra = parms.extra, | ||
format = parms.format, | format = parms.format, | ||
Line 543: | Line 440: | ||
isnegative = diff.isnegative, | isnegative = diff.isnegative, | ||
range = parms.range, | range = parms.range, | ||
spell = parms.spell, | spell = parms.spell, | ||
} | } | ||
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) } | ||
Line 564: | Line 459: | ||
} | } | ||
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) .. | ||
rangeJoin(textOptions.range) .. | rangeJoin(textOptions.range) .. | ||
Line 570: | Line 465: | ||
(textOptions.suffix or '') | (textOptions.suffix or '') | ||
end | end | ||
return message(' | return message('Parameter show=' .. show.id .. ' is not supported here') | ||
end | end | ||
Line 591: | Line 486: | ||
-- Don't bother detecting if wantMixture is used because not needed and it is a poor option. | -- Don't bother detecting if wantMixture is used because not needed and it is a poor option. | ||
if not text then | if not text then | ||
text = 'currentdate' | text = 'currentdate' | ||
if getopt.flag == 'usesCurrent' then | if getopt.flag == 'usesCurrent' then | ||
Line 625: | Line 517: | ||
end | end | ||
if getopt.omitZero and i % 3 ~= 1 then -- omit zero months and days as unknown values but keep year 0 which is 1 BCE | if getopt.omitZero and i % 3 ~= 1 then -- omit zero months and days as unknown values but keep year 0 which is 1 BCE | ||
if | if tonumber(fields[i]) == 0 then | ||
fields[i] = nil | fields[i] = nil | ||
getopt.partial = true | getopt.partial = true | ||
Line 632: | Line 524: | ||
end | end | ||
local fix = getopt.fix and 'fix' or '' | local fix = getopt.fix and 'fix' or '' | ||
local noDefault = imax == 0 and getopt.noMissing | |||
local partialText = getopt.partial and 'partial' or '' | local partialText = getopt.partial and 'partial' or '' | ||
local dates = {} | local dates = {} | ||
Line 644: | Line 537: | ||
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 | ||
Line 668: | Line 550: | ||
if (getopt.partial and y and (m or not d)) or (y and m and d) then | if (getopt.partial and y and (m or not d)) or (y and m and d) then | ||
dates[i] = Date(fix, partialText, y, m, d) | dates[i] = Date(fix, partialText, 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(flagCurrent()) | dates[i] = Date(flagCurrent()) | ||
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, partialText, flagCurrent(fields[1])) | dates[1] = Date(fix, partialText, flagCurrent(fields[1])) | ||
Line 681: | Line 563: | ||
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 | ||
Line 687: | Line 569: | ||
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] | ||
Line 698: | Line 580: | ||
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 | ||
Line 795: | Line 677: | ||
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 | ||
Line 851: | Line 733: | ||
abbr = spec.abbr, | abbr = spec.abbr, | ||
disp = spec.disp, | disp = spec.disp, | ||
extra = | extra = (getopt.usesCurrent and format ~= 'format_raw') and '<span class="currentage"></span>' or nil, | ||
format = format or spec.format, | format = format or spec.format, | ||
round = yes(args.round), | round = yes(args.round), | ||
Line 859: | Line 741: | ||
} | } | ||
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 | ||
Line 867: | Line 749: | ||
-- 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 | ||
Line 879: | Line 757: | ||
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' | ||
Line 891: | Line 768: | ||
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"> ' .. | |||
'(age ' .. | |||
dateDifference({ | |||
diff = diff, | diff = diff, | ||
show = show, | show = show, | ||
Line 901: | Line 783: | ||
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 | ||
Line 931: | Line 813: | ||
end | end | ||
if invalid then | if invalid then | ||
result = result .. message(' | result = result .. message('invalid parameter ' .. invalid, 'warning') | ||
end | end | ||
end | end | ||
Line 1,041: | Line 839: | ||
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 | ||
Line 1,056: | Line 854: | ||
return tostring(date.jd) | return tostring(date.jd) | ||
end | end | ||
return message(' | return message('Need valid year/month/day or "currentdate"') | ||
end | end | ||
Line 1,067: | Line 865: | ||
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), | ||
Line 1,075: | Line 872: | ||
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 | ||
Line 1,087: | Line 884: | ||
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 | ||
Line 1,098: | Line 895: | ||
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 | ||
Line 1,106: | Line 903: | ||
parms.round = true | parms.round = true | ||
end | end | ||
return | return dateDifference(parms) | ||
end | end | ||
Line 1,112: | Line 909: | ||
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 |