Saltar para o conteúdo

Módulo:Age

Origem: Wikipédia, a enciclopédia livre.
-- Implement various "age of" and other date-related templates.

localmtext={
-- Message and other text that should be localized.
['mt-bad-param1']='Invalid parameter $1',
['mt-bad-param2']='Parameter $1=$2 is invalid',
['mt-bad-show']='Parameter show=$1 is not supported here',
['mt-cannot-add']='Cannot add "$1" ',
['mt-conflicting-show']='Parameter show=$1 conflicts with round=$2',
['mt-date-wrong-order']='The second date must be later in time than the first date',
['mt-dd-future']='Death date (first date) must not be in the future',
['mt-dd-wrong-order']='Death date (first date) must be later in time than the birth date (second date)',
['mt-invalid-bd-age']='Invalid birth date for calculating age',
['mt-invalid-dates-age']='Invalid dates for calculating age',
['mt-invalid-end']='Invalid end date in second parameter',
['mt-invalid-start']='Invalid start date in first parameter',
['mt-need-jdn']='Need valid Julian date number',
['mt-need-valid-bd']='Need valid birth date: year, month, day',
['mt-need-valid-bd2']='Need valid birth date (second date): year, month, day',
['mt-need-valid-date']='Need valid date',
['mt-need-valid-dd']='Need valid death date (first date): year, month, day',
['mt-need-valid-ymd']='Need valid year, month, day',
['mt-need-valid-ymd-current']='Need valid year|month|day or "currentdate" ',
['mt-need-valid-ymd2']='Second date should be year, month, day',
['mt-template-bad-name']='The specified template name is not valid',
['mt-template-x']='The template invoking this must have "|template=x" where x is the wanted operation',
['txt-age']='(age ',
['txt-aged']=' (aged ',
['txt-and']=' and ',
['txt-comma-and']=', and ',
['txt-error']='Error: ',
['txt-or']=' or ',
}

localtranslate,from_en,to_en,isZero
iftranslatethen
-- Functions to translate from en to local language and reverse go here.
-- See example at [[:bn:Module:বয়স]].
else
from_en=function(text)
returntext
end
isZero=function(text)
returntonumber(text)==0
end
end

local_Date,_currentDate
localfunctiongetExports(frame)
-- Return objects exported from the date module or its sandbox.
ifnot_Datethen
localsandbox=frame:getTitle():find('sandbox',1,true)and'/sandbox'or''
localdatemod=require('Module:Date'..sandbox)
localrealDate=datemod._Date
_currentDate=datemod._current
ifto_enthen
_Date=function(...)
localargs={}
fori,vinipairs({...})do
args[i]=to_en(v)
end
returnrealDate(unpack(args))
end
else
_Date=realDate
end
end
return_Date,_currentDate
end

localCollection-- a table to hold items
Collection={
add=function(self,item)
ifitem~=nilthen
self.n=self.n+1
self[self.n]=item
end
end,
join=function(self,sep)
returntable.concat(self,sep)
end,
remove=function(self,pos)
ifself.n>0and(pos==nilor(0<posandpos<=self.n))then
self.n=self.n-1
returntable.remove(self,pos)
end
end,
sort=function(self,comp)
table.sort(self,comp)
end,
new=function()
returnsetmetatable({n=0},Collection)
end
}
Collection.__index=Collection

localfunctionstripToNil(text)
-- If text is a string, return its trimmed content, or nil if empty.
-- Otherwise return text (which may, for example, be nil).
iftype(text)=='string'then
text=text:match('(%S.-)%s*$')
end
returntext
end

localfunctionyes(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

localfunctionmessage(msg,id)
-- Return formatted message text for an error or warning.
localfunctiongetText(msg)
returnmtext[msg]orerror('Bug: message "'..tostring(msg)..' "not defined')
end
localtext
iftype(msg)=='table'then
text=getText(msg[1])
localrep={}
fori,vinipairs(msg)do
ifi>1then
rep['$'..(i-1)]=v
end
end
text=text:gsub('$%d+',rep)
else
text=getText(msg)
end
localcategories={
error='[[Categoria:!Erro idade]]',
warning='[[Categoria:!Erro idade]]',-- same as error until determine whether 'Age warning' would be worthwhile
}
locala,b,category
ifid=='warning'then
a='<sup>[<i>'
b='</i>]</sup>'
else
a='<strong class= "error" >'..getText('txt-error')
b='</strong>'
end
ifmw.title.getCurrentTitle():inNamespaces(0)then
-- Category only in namespaces: 0=article.
category=categories[idor'error']
end
return
a..
mw.text.nowiki(text)..
b..
(categoryor'')
end

localfunctionformatNumber(number)
-- Return the given number formatted with commas as group separators,
-- given that the number is an integer.
localnumstr=tostring(number)
locallength=#numstr
localplaces=Collection.new()
localpos=0
repeat
places:add(pos)
pos=pos+3
untilpos>=length
places:add(length)
localgroups=Collection.new()
fori=places.n,2,-1do
localp1=length-places[i]+1
localp2=length-places[i-1]
groups:add(numstr:sub(p1,p2))
end
returngroups:join(',')
end

localfunctionspellNumber(number,options,i)
-- Return result of spelling number, or
-- return number (as a string) if cannot spell it.
-- i == 1 for the first number which can optionally start with an uppercase letter.
number=tostring(number)
returnrequire('Module:ConvertNumeric').spell_number(
number,
nil,-- fraction numerator
nil,-- fraction denominator
i==1andoptions.upper,-- true: 'One' instead of 'one'
notoptions.us,-- true: use 'and' between tens/ones etc
options.adj,-- true: hyphenated
options.ordinal-- true: 'first' instead of 'one'
)ornumber
end

localfunctionmakeExtra(args,flagCurrent)
-- Return extra text that will be inserted before the visible result
-- but after any sort key.
localextra=args.prefixor''
ifmw.ustring.len(extra)>1then
-- Parameter "~" gives "~3" whereas "over" gives "over 3".
ifextra:sub(-6,-1)~='&nbsp;'then
extra=extra..' '
end
end
ifflagCurrentthen
extra='<span class= "currentage" ></span>'..extra
end
returnextra
end

localfunctionmakeSort(value,sortable)
-- Return a sort key if requested.
-- Assume value is a valid number which has not overflowed.
ifsortable=='sortable_table'orsortable=='sortable_on'orsortable=='sortable_debug'then
localsortKey
ifvalue==0then
sortKey='5000000000000000000'
else
localmag=math.floor(math.log10(math.abs(value))+1e-14)
ifvalue>0then
sortKey=7000+mag
else
sortKey=2999-mag
value=value+10^(mag+1)
end
sortKey=string.format('%d',sortKey)..string.format('%015.0f',math.floor(value*10^(14-mag)))
end
localresult
ifsortable=='sortable_table'then
result='data-sort-value= "_SORTKEY_" |'
elseifsortable=='sortable_debug'then
result='<span data-sort-value= "_SORTKEY_♠" ><span style= "border:1px solid" >_SORTKEY_♠</span></span>'
else
result='<span data-sort-value= "_SORTKEY_♠" ></span>'
end
returnresult:gsub('_SORTKEY_',sortKey)
end
end

localtranslateParameters={
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',
months='ym',
weeks='ymw',
days='ymd',
hours='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'},
M={'M',id='M'},
s={'S',id='s'},
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',
},
}

localspellOptions={
cardinal={},
Cardinal={upper=true},
cardinal_us={us=true},
Cardinal_us={us=true,upper=true},
ordinal={ordinal=true},
Ordinal={ordinal=true,upper=true},
ordinal_us={ordinal=true,us=true},
Ordinal_us={ordinal=true,us=true,upper=true},
}

localfunctiondateExtract(frame)
-- Return part of a date after performing an optional operation.
localDate=getExports(frame)
localargs=frame:getParent().args
localparms={}
fori,vinipairs(args)do
parms[i]=v
end
ifyes(args.fix)then
table.insert(parms,'fix')
end
ifyes(args.partial)then
table.insert(parms,'partial')
end
localshow=stripToNil(args.show)or'dmy'
localdate=Date(unpack(parms))
ifnotdatethen
ifshow=='format'then
return'error'
end
returnmessage('mt-need-valid-date')
end
localadd=stripToNil(args.add)
ifaddthen
foriteminadd:gmatch('%S+')do
date=date+item
ifnotdatethen
returnmessage({'mt-cannot-add',item})
end
end
end
localsortKey,result
localsortable=translateParameters.sortable[args.sortable]
ifsortablethen
localvalue=(date.partialanddate.partial.firstordate).jdz
sortKey=makeSort(value,sortable)
end
ifshow~='hide'then
result=date[show]
ifresult==nilthen
result=from_en(date:text(show))
elseiftype(result)=='boolean'then
result=resultand'1'or'0'
else
result=from_en(tostring(result))
end
end
return(sortKeyor'')..makeExtra(args)..(resultor'')
end

localfunctionrangeJoin(range)
-- Return text to be used between a range of ages.
returnrange=='dash'and'–'ormtext['txt-or']
end

localfunctionmakeText(values,components,names,options,noUpper)
-- Return wikitext representing an age or duration.
localtext=Collection.new()
localcount=#values
localsep=names.sepor''
fori,vinipairs(values)do
-- v is a number (say 4 for 4 years), or a table ({4,5} for 4 or 5 years).
localislist=type(v)=='table'
if(islistorv>0)or(text.n==0andi==count)or(text.n>0andcomponents.keepZero)then
localfmt,vstr
ifoptions.spellthen
fmt=function(number)
returnspellNumber(number,options.spell,noUpperori)
end
elseifi==1andoptions.format=='format_commas'then
-- Numbers after the first should be small and not need formatting.
fmt=formatNumber
else
fmt=tostring
end
ifislistthen
vstr=fmt(v[1])..rangeJoin(options.range)
noUpper=true
vstr=vstr..fmt(v[2])
else
vstr=fmt(v)
end
localname=names[components[i]]
ifnamethen
localplural=names.plural
ifnotpluralor(islistandv[2]orv)==1then
plural=''
end
text:add(vstr..sep..name..plural)
else
text:add(vstr)
end
end
end
localfirst,last
ifoptions.join=='sep_space'then
first=' '
last=' '
elseifoptions.join=='sep_comma'then
first=', '
last=', '
elseifoptions.join=='sep_serialcomma'andtext.n>2then
first=', '
last=mtext['txt-comma-and']
else
first=', '
last=mtext['txt-and']
end
fori,vinipairs(text)do
ifi<text.nthen
text[i]=v..(i+1<text.nandfirstorlast)
end
end
localsign=''
ifoptions.isnegativethen
-- Do not display negative zero.
iftext.n>1or(text.n==1andtext[1]:sub(1,1)~='0')then
ifoptions.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.sortKeyor'')..
(options.extraor'')..
sign..
text:join()..
(options.suffixor'')
end

localfunctiondateDifference(parms)
-- Return a formatted date difference using the given parameters
-- which have been validated.
localnames={
abbr_off={
plural='s',
sep='&nbsp;',
y='year',
m='month',
w='week',
d='day',
H='hour',
M='minute',
S='second',
},
abbr_on={
y='y',
m='m',
w='w',
d='d',
H='h',
M='m',
S='s',
},
abbr_infant={-- for {{age for infant}}
plural='s',
sep='&nbsp;',
y='yr',
m='mo',
w='wk',
d='day',
H='hr',
M='min',
S='sec',
},
abbr_raw={},
}
localdiff=parms.diff-- must be a valid date difference
localshow=parms.show-- may be nil; default is set below
localabbr=parms.abbror'abbr_off'
localdefaultJoin
ifabbr~='abbr_off'then
defaultJoin='sep_space'
end
ifnotshowthen
show='ymd'
ifparms.disp=='disp_age'then
ifdiff.years<3then
defaultJoin='sep_space'
ifdiff.years>=1then
show='ym'
else
show='md'
end
else
show='y'
end
end
end
iftype(show)~='table'then
show=translateParameters.show[show]
end
ifparms.disp=='disp_raw'then
defaultJoin='sep_space'
abbr='abbr_raw'
elseifparms.wantScthen
defaultJoin='sep_serialcomma'
end
localdiffOptions={
round=parms.round,
duration=parms.wantDuration,
range=parms.rangeandtrueornil,
}
localsortKey
ifparms.sortablethen
localvalue=diff.age_days+(parms.wantDurationand1or0)-- days and fraction of a day
ifdiff.isnegativethen
value=-value
end
sortKey=makeSort(value,parms.sortable)
end
localtextOptions={
extra=parms.extra,
format=parms.format,
join=parms.sepordefaultJoin,
isnegative=diff.isnegative,
range=parms.range,
sortKey=sortKey,
spell=parms.spell,
suffix=parms.suffix,-- not currently used
}
ifshow.id=='hide'then
returnsortKeyor''
end
localvalues={diff:age(show.id,diffOptions)}
ifvalues[1]then
returnmakeText(values,show,names[abbr],textOptions)
end
ifdiff.partialthen
-- Handle a more complex range such as
-- {{age_yd|20 Dec 2001|2003|range=yes}} → 1 year, 12 days or 2 years, 11 days
localopt={
format=textOptions.format,
join=textOptions.join,
isnegative=textOptions.isnegative,
spell=textOptions.spell,
}
return
(textOptions.sortKeyor'')..
makeText({diff.partial.mindiff:age(show.id,diffOptions)},show,names[abbr],opt)..
rangeJoin(textOptions.range)..
makeText({diff.partial.maxdiff:age(show.id,diffOptions)},show,names[abbr],opt,true)..
(textOptions.suffixor'')
end
returnmessage({'mt-bad-show',show.id})
end

localfunctiongetDates(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 optionally 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.
localDate,currentDate=getExports(frame)
getopt=getoptor{}
localfunctionflagCurrent(text)
-- This allows the calling template to detect if the current date has been used,
-- that is, whether both dates have been entered in a template expecting two.
-- For example, an infobox may want the age when an event occurred, not the current age.
-- Don't bother detecting if wantMixture is used because not needed and it is a poor option.
ifnottextthen
ifgetopt.noMissingthen
returnnil-- this gives a nil date which gives an error
end
text='currentdate'
ifgetopt.flag=='usesCurrent'then
getopt.usesCurrent=true
end
end
returntext
end
localargs=frame:getParent().args
localfields={}
localisNamed=args.yearorargs.year1orargs.year2or
args.monthorargs.month1orargs.month2or
args.dayorargs.day1orargs.day2
ifisNamedthen
fields[1]=args.year1orargs.year
fields[2]=args.month1orargs.month
fields[3]=args.day1orargs.day
fields[4]=args.year2
fields[5]=args.month2
fields[6]=args.day2
else
fori=1,6do
fields[i]=args[i]
end
end
localimax=0
fori=1,6do
fields[i]=stripToNil(fields[i])
iffields[i]then
imax=i
end
ifgetopt.omitZeroandi%3~=1then-- omit zero months and days as unknown values but keep year 0 which is 1 BCE
ifisZero(fields[i])then
fields[i]=nil
getopt.partial=true
end
end
end
localfix=getopt.fixand'fix'or''
localpartialText=getopt.partialand'partial'or''
localdates={}
ifisNamedorimax>=3then
localnrDates=getopt.singleand1or2
ifgetopt.wantMixturethen
-- Cannot be partial since empty fields are set from current.
localcomponents={'year','month','day'}
fori=1,nrDates*3do
fields[i]=fields[i]orcurrentDate[components[i>3andi-3ori]]
end
fori=1,nrDatesdo
localindex=i==1and1or4
localy,m,d=fields[index],fields[index+1],fields[index+2]
if(m==2orm=='2')and(d==29ord=='29')then
-- Workaround error with following which attempt to use invalid date 2001-02-29.
-- {{age_ymwd|year1=2001|year2=2004|month2=2|day2=29}}
-- {{age_ymwd|year1=2001|month1=2|year2=2004|month2=1|day2=29}}
-- TODO Get rid of wantMixture because even this ugly code does not handle
-- 'Feb' or 'February' or 'feb' or 'february'.
ifnot((y%4==0andy%100~=0)ory%400==0)then
d=28
end
end
dates[i]=Date(y,m,d)
end
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).
fori=1,nrDatesdo
localindex=i==1and1or4
localy,m,d=fields[index],fields[index+1],fields[index+2]
if(getopt.partialandyand(mornotd))or(yandmandd)then
dates[i]=Date(fix,partialText,y,m,d)
elseifnotyandnotmandnotdthen
dates[i]=Date(flagCurrent())
end
end
end
else
getopt.textdates=true-- have parsed each date from a single text field
dates[1]=Date(fix,partialText,flagCurrent(fields[1]))
ifnotgetopt.singlethen
dates[2]=Date(fix,partialText,flagCurrent(fields[2]))
end
end
ifnotdates[1]then
returnmessage(getopt.missing1or'mt-need-valid-ymd')
end
ifgetopt.singlethen
returndates[1]
end
ifnotdates[2]then
returnmessage(getopt.missing2or'mt-need-valid-ymd2')
end
returndates[1],dates[2]
end

localfunctionageGeneric(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.
localname=frame.args.template
ifnotnamethen
returnmessage('mt-template-x')
end
localargs=frame:getParent().args
localspecs={
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',
flag='usesCurrent',
omitZero=true,
range='no',
},
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'ornil,
},
age_yd_nts={-- {{age in years and days nts}}
show='yd',
format='format_commas',
sep=args.sep~='and'and'sep_comma'ornil,
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,
},
}
localspec=specs[name]
ifnotspecthen
returnmessage('mt-template-bad-name')
end
ifname=='age_days'then
localsu=stripToNil(args['show unit'])
ifsuthen
ifsu=='abbr'orsu=='full'then
spec.disp=nil
spec.abbr=su=='abbr'and'abbr_on'ornil
end
end
end
localpartial,autofill
localrange=stripToNil(args.range)orspec.range
ifrangethen
-- 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" or spec.range == 'dash' sets range = 'dash' (gives "11–12" ).
-- "|range=no" or spec.range == 'no' sets range = nil and fills each date in the diff (gives "12" ).
-- ( "on" is equivalent to "yes", and "off" is equivalent to "no" ).
-- "|range=OTHER" sets range = nil and rejects partial dates.
range=({dash='dash',off='no',no='no',[true]=true})[range]oryes(range)
ifrangethen
partial=true-- accept partial dates with a possible age range for the result
ifrange=='no'then
autofill=true-- missing month/day in first or second date are filled from other date or 1
range=nil
end
end
end
localgetopt={
fix=yes(args.fix),
flag=stripToNil(args.flag)orspec.flag,
omitZero=spec.omitZero,
partial=partial,
wantMixture=spec.wantMixture,
}
localdate1,date2=getDates(frame,getopt)
iftype(date1)=='string'then
returndate1
end
localformat=stripToNil(args.format)
localspell=spellOptions[format]
ifformatthen
format='format_'..format
elseifname=='age_days'andgetopt.textdatesthen
format='format_commas'
end
localparms={
diff=date2:subtract(date1,{fill=autofill}),
wantDuration=spec.durationoryes(args.duration),
range=range,
wantSc=yes(args.sc),
show=args.show=='hide'and'hide'orspec.show,
abbr=spec.abbr,
disp=spec.disp,
extra=makeExtra(args,getopt.usesCurrentandformat~='format_raw'),
format=formatorspec.format,
round=yes(args.round),
sep=spec.sep,
sortable=translateParameters.sortable[args.sortableorspec.sortable],
spell=spell,
}
if(spec.negativeorframe.args.negative)=='error'andparms.diff.isnegativethen
returnmessage('mt-date-wrong-order')
end
returnfrom_en(dateDifference(parms))
end

localfunctionbda(frame)
-- Implement [[Template:Birth date and age]].
localargs=frame:getParent().args
localoptions={
missing1='mt-need-valid-bd',
noMissing=true,
single=true,
}
localdate=getDates(frame,options)
iftype(date)=='string'then
returndate-- error text
end
localDate=getExports(frame)
localdiff=Date('currentdate')-date
ifdiff.isnegativeordiff.years>150then
returnmessage('mt-invalid-bd-age')
end
localdisp,show='disp_raw','y'
ifdiff.years<2then
disp='disp_age'
ifdiff.years==0anddiff.months==0then
show='d'
else
show='m'
end
end
localdf=stripToNil(args.df)-- day first (dmy); default is month first (mdy)
localresult='(<span class= "bday" >%-Y-%m-%d</span>) </span>'..
(dfand'%-d %B %-Y'or'%B %-d, %-Y')
result=from_en('<span style= "display:none" > '..
date:text(result)..
'<span class= "noprint ForceAgeToShow" > '..
mtext['txt-age']..
dateDifference({
diff=diff,
show=show,
abbr='abbr_off',
disp=disp,
sep='sep_space',
})..
')</span>')
localwarnings=tonumber(frame.args.warnings)
ifwarningsandwarnings>0then
localgood={
df=true,
mf=true,
day=true,
day1=true,
month=true,
month1=true,
year=true,
year1=true,
}
localinvalid
localimax=options.textdatesand1or3
fork,_inpairs(args)do
iftype(k)=='number'then
ifk>imaxthen
invalid=tostring(k)
break
end
else
ifnotgood[k]then
invalid=k
break
end
end
end
ifinvalidthen
result=result..message({'mt-bad-param1',invalid},'warning')
end
end
returnresult
end

localfunctiondda(frame)
-- Implement [[Template:Death date and age]].
localargs=frame:getParent().args
localoptions={
missing1='mt-need-valid-dd',
missing2='mt-need-valid-bd2',
noMissing=true,
partial=true,
}
localdate1,date2=getDates(frame,options)
iftype(date1)=='string'then
returndate1
end
localdiff=date1-date2
ifdiff.isnegativethen
returnmessage('mt-dd-wrong-order')
end
localDate=getExports(frame)
localtoday=Date('currentdate')+1-- one day in future allows for timezones
ifdate1>todaythen
returnmessage('mt-dd-future')
end
localyears
ifdiff.partialthen
years=diff.partial.years
years=type(years)=='table'andyears[2]oryears
else
years=diff.years
end
ifyears>150then
returnmessage('mt-invalid-dates-age')
end
localdf=stripToNil(args.df)-- day first (dmy); default is month first (mdy)
localresult
ifdate1.daythen-- y, m, d known
result=(dfand
'%-d %B %-Y'or
'%B %-d, %-Y')..
'<span style= "display:none" >(%-Y-%m-%d)</span>'
elseifdate1.monththen-- y, m known; d unknown
result=
'%B %-Y'..
'<span style= "display:none" >(%-Y-%m-00)</span>'
else-- y known; m, d unknown
result=
'%-Y'..
'<span style= "display:none" >(%-Y-00-00)</span>'
end
result=from_en(date1:text(result)..
mtext['txt-aged']..
dateDifference({
diff=diff,
show='y',
abbr='abbr_off',
disp='disp_raw',
range='dash',
sep='sep_space',
})..
')')
localwarnings=tonumber(frame.args.warnings)
ifwarningsandwarnings>0then
localgood={
df=true,
mf=true,
}
localinvalid
localimax=options.textdatesand2or6
fork,_inpairs(args)do
iftype(k)=='number'then
ifk>imaxthen
invalid=tostring(k)
break
end
else
ifnotgood[k]then
invalid=k
break
end
end
end
ifinvalidthen
result=result..message({'mt-bad-param1',invalid},'warning')
end
end
returnresult
end

localfunctiondateToGsd(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.
localdate=getDates(frame,{wantMixture=true,single=true})
iftype(date)=='string'then
returndate
end
returntostring(date.gsd)
end

localfunctionjdToDate(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.
localDate=getExports(frame)
localargs=frame:getParent().args
localdate=Date('juliandate',args[1],args[2])
ifdatethen
returnfrom_en(date:text())
end
returnmessage('mt-need-jdn')
end

localfunctiondateToJd(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.
localDate=getExports(frame)
localargs=frame:getParent().args
localdate=Date(args[1],args[2],args[3],args[4],args[5],args[6],args[7])
ifdatethen
returntostring(date.jd)
end
returnmessage('mt-need-valid-ymd-current')
end

localfunctiontimeInterval(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.
localDate=getExports(frame)
localargs=frame:getParent().args
localparms={
extra=makeExtra(args),
wantDuration=yes(args.duration),
range=yes(args.range)or(args.range=='dash'and'dash'ornil),
wantSc=yes(args.sc),
}
localfix=yes(args.fix)and'fix'or''
localdate1=Date(fix,'partial',stripToNil(args[1])or'currentdatetime')
ifnotdate1then
returnmessage('mt-invalid-start')
end
localdate2=Date(fix,'partial',stripToNil(args[2])or'currentdatetime')
ifnotdate2then
returnmessage('mt-invalid-end')
end
parms.diff=date2-date1
forargname,translateinpairs(translateParameters)do
localparm=stripToNil(args[argname])
ifparmthen
parm=translate[parm]
ifparm==nilthen-- test for nil because false is a valid setting
returnmessage({'mt-bad-param2',argname,args[argname]})
end
parms[argname]=parm
end
end
ifparms.roundthen
localround=parms.round
localshow=parms.show
ifround~='on'then
ifshowthen
ifshow.id~=roundthen
returnmessage({'mt-conflicting-show',args.show,args.round})
end
else
parms.show=translateParameters.show[round]
end
end
parms.round=true
end
returnfrom_en(dateDifference(parms))
end

return{
age_generic=ageGeneric,-- can emulate several age templates
birth_date_and_age=bda,-- Template:Birth_date_and_age
death_date_and_age=dda,-- Template:Death_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
}