Module:Age: Difference between revisions

    (update from sandbox: sortable=table; ranges of more time units; no default sort key in Template:Age in years and days; implement Template:Birth date and age; experimental range=no)
    (update from sandbox: dates can be "partial" consisting of ymd, ym or y (but yd is invalid); with an invalid date, show=format gives 'error' rather than a run-time error)
    Line 206: Line 206:
    table.insert(parms, 'partial')
    table.insert(parms, 'partial')
    end
    end
    local show = stripToNil(args.show) or 'dmy'
    local date = Date(unpack(parms))
    local date = Date(unpack(parms))
    if not date then
    if not date then
    if show == 'format' then
    return 'error'
    end
    return message('Need valid date')
    return message('Need valid date')
    end
    end
    Line 225: Line 229:
    prefix = makeSort(value, sortable)
    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]
    Line 237: Line 240:
    end
    end
    return (prefix or '') .. (result or '')
    return (prefix or '') .. (result or '')
    end
    local function rangeJoin(range)
    -- Return text to be used between a range of ages.
    return range == 'dash' and '–' or ' or '
    end
    end


    Line 256: Line 264:
    end
    end
    if islist then
    if islist then
    local join = options.range == 'dash' and '–' or ' or '
    vstr = fmt(v[1]) .. rangeJoin(options.range) .. fmt(v[2])
    vstr = fmt(v[1]) .. join .. fmt(v[2])
    else
    else
    vstr = fmt(v)
    vstr = fmt(v)
    Line 417: Line 424:
    (textOptions.prefix or '') ..
    (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 ' or ') ..
    rangeJoin(textOptions.range) ..
    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 '')
    Line 429: Line 436:
    -- * 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 replaced with the current date.
    -- A missing date is optionally 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
    Line 436: Line 443:
    local Date, currentDate = getExports(frame)
    local Date, currentDate = getExports(frame)
    getopt = getopt or {}
    getopt = getopt or {}
    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 = {}
    Line 462: Line 467:
    end
    end
    end
    end
    local fix = getopt.fix and 'fix' or ''
    local noDefault = imax == 0 and getopt.noMissing
    local noDefault = imax == 0 and getopt.noMissing
    local partialText = getopt.partial and 'partial' or ''
    local dates = {}
    local dates = {}
    if isNamed or imax >= 3 then
    if isNamed or imax >= 3 then
    Line 477: Line 484:
    end
    end
    else
    else
    -- If partial dates are allowed, accept
    --    year only, or
    --    year and month only
    -- Do not accept year and day without a month because that makes no sense
    -- (and because, for example, Date('partial', 2001, nil, 12) sets day = nil, not 12).
    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 (partial and y) 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, partial, y, m, d)
    dates[i] = Date(fix, partialText, y, m, d)
    elseif not y and not m and not d and not noDefault then
    elseif not y and not m and not d and not noDefault then
    dates[i] = Date('currentdate')
    dates[i] = Date('currentdate')
    Line 489: Line 501:
    elseif not noDefault then
    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, partial, fields[1] or 'currentdate')
    dates[1] = Date(fix, partialText, fields[1] or 'currentdate')
    if not getopt.single then
    if not getopt.single then
    dates[2] = Date(fix, partial, fields[2] or 'currentdate')
    dates[2] = Date(fix, partialText, fields[2] or 'currentdate')
    end
    end
    end
    end
    Line 617: Line 629:
    end
    end
    end
    end
    local partial
    local partial, autofill
    local range = stripToNil(args.range) or spec.range
    local range = stripToNil(args.range) or spec.range
    if range then
    if range then
    Line 623: Line 635:
    -- "|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" sets range = 'dash' (gives "11–12").
    -- "|range=dash" or spec.range == 'dash' sets range = 'dash' (gives "11–12").
    -- "|range=no" sets range = nil (gives "12").
    -- "|range=no" or spec.range == 'no' sets range = nil and fills each date in the diff (gives "12").
    --    Above gives a good result with age in years, but is probably unhelpful for other cases.
    --    ("on" is equivalent to "yes", and "off" is equivalent to "no").
    --    {{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', no = 'no', [true] = true })[range] or yes(range)
    range = ({ dash = 'dash', off = 'no', 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
    autofill = true  -- missing month/day in first or second date are filled from other date or 1
    range = nil
    range = nil
    end
    end
    Line 653: Line 664:
    end
    end
    local parms = {
    local parms = {
    diff = date2 - date1,
    diff = date2:subtract(date1, { fill = autofill }),
    wantDuration = spec.duration or yes(args.duration),
    wantDuration = spec.duration or yes(args.duration),
    range = range,
    range = range,