Difference between revisions of "Module:Age/sandbox"
Jump to navigation
Jump to search
blackwiki>Steel1943 (Create sandbox version of Module:Age) |
blackwiki>Johnuniq (plans for having Module:Age use Module:Date; more to do) |
||
Line 1: | Line 1: | ||
− | -- | + | -- Implement various "age of" and other date-related templates. |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | local datemod = require('Module:Date') | ||
+ | local current = datemod._current | ||
+ | local Date = datemod._Date | ||
local MINUS = '−' -- Unicode U+2212 MINUS SIGN | local MINUS = '−' -- Unicode U+2212 MINUS SIGN | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
local function strip_to_nil(text) | local function strip_to_nil(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 | end | ||
− | local function | + | local function number_name(number, singular, plural, sep) |
− | + | -- Return the given number, converted to a string, with the | |
− | + | -- separator (default space) and singular or plural name appended. | |
+ | plural = plural or (singular .. 's') | ||
+ | sep = sep or ' ' | ||
+ | return tostring(number) .. sep .. ((number == 1) and singular or plural) | ||
end | end | ||
− | local function | + | local function message(msg, nocat) |
− | + | -- Return formatted message text for an error. | |
− | + | -- Can append "#FormattingError" to URL of a page with a problem to find it. | |
− | + | local anchor = '<span id="FormattingError" />' | |
− | + | local category | |
− | + | if not nocat and mw.title.getCurrentTitle():inNamespaces(0, 10) then | |
− | + | -- Category only in namespaces: 0=article, 10=template. | |
+ | category = '[[Category:Age error]]' | ||
+ | else | ||
+ | category = '' | ||
+ | end | ||
+ | return anchor .. | ||
+ | '<strong class="error">Error: ' .. | ||
+ | msg .. | ||
+ | '</strong>' .. | ||
+ | category .. '\n' | ||
end | end | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
local function date_component(named, positional, component) | local function date_component(named, positional, component) | ||
− | + | -- Return the first of the two arguments (named like {{example|year=2001}} | |
− | + | -- or positional like {{example|2001}}) that is not nil and is not empty. | |
− | + | -- If both are nil, return the current date component, if specified. | |
− | + | -- This translates empty arguments passed to the template to nil, and | |
− | + | -- optionally replaces a nil argument with a value from the current date. | |
− | + | named = strip_to_nil(named) | |
− | + | if named then | |
− | + | return named | |
− | + | end | |
− | + | positional = strip_to_nil(positional) | |
− | + | if positional then | |
− | + | return positional | |
− | + | end | |
− | + | if component then | |
− | + | return current[component] | |
− | + | end | |
− | + | return nil | |
end | end | ||
− | local function | + | local function get_dates(frame, want_mixture) |
− | + | -- Return date1, date2 from template parameters. | |
− | + | -- Either may be nil if given arguments are invalid. | |
− | + | -- If want_mixture 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. | |
− | + | -- TODO Accept a date as a string like "1 April 2016". | |
− | + | local date1, date2 | |
− | + | local args = frame:getParent().args | |
− | + | if want_mixture then | |
− | + | date1 = Date( | |
− | + | date_component(args.year1 , args[1], 'year' ), | |
− | + | date_component(args.month1, args[2], 'month'), | |
− | + | date_component(args.day1 , args[3], 'day' ) | |
− | + | ) | |
− | + | date2 = Date( | |
− | + | date_component(args.year2 , args[4], 'year' ), | |
− | + | date_component(args.month2, args[5], 'month'), | |
− | + | date_component(args.day2 , args[6], 'day' ) | |
− | + | ) | |
− | + | else | |
− | + | local fields = {} | |
− | + | for i = 1, 6 do | |
− | + | fields[i] = strip_to_nil(args[i]) | |
− | + | end | |
− | + | date1 = Date(fields[1], fields[2], fields[3]) | |
− | + | if fields[4] and fields[5] and fields[6] then | |
+ | date2 = Date(fields[4], fields[5], fields[6]) | ||
+ | else | ||
+ | date2 = Date('currentdate') | ||
+ | end | ||
+ | end | ||
+ | return date1, date2 | ||
end | end | ||
− | local | + | local function age_days(frame) |
− | + | -- Return age in days between two given dates, or | |
− | + | -- between given date and current date. | |
− | + | -- This code implements the logic in [[Template:Age in days]]. | |
− | + | -- Like {{Age in days}}, a missing argument is replaced from the current | |
− | + | -- date, so can get a bizarre mixture of specified/current y/m/d. | |
− | + | local date1, date2 = get_dates(frame, true) | |
− | + | if not (date1 and date2) then | |
− | + | return message('Need valid year, month, day') | |
− | + | end | |
− | + | local sign = '' | |
− | + | local result = date2.jd - date1.jd | |
− | + | if result < 0 then | |
+ | sign = '−' | ||
+ | result = -result | ||
+ | end | ||
+ | return sign .. tostring(result) | ||
+ | end | ||
− | function | + | local function _age_ym(diff) |
− | + | -- Return text specifying date difference in years, months. | |
− | + | local sign = diff.isnegative and MINUS or '' | |
− | + | local years, months = diff:age('ym') | |
− | + | local mtext = number_name(months, 'month') | |
− | + | local result | |
− | + | if years > 0 then | |
− | + | local ytext = number_name(years, 'year') | |
− | + | if months == 0 then | |
− | + | result = ytext | |
− | + | else | |
− | + | result = ytext .. ', ' .. mtext | |
− | + | end | |
− | + | else | |
− | + | if months == 0 then | |
+ | sign = '' | ||
+ | end | ||
+ | result = mtext | ||
+ | end | ||
+ | return sign .. result | ||
end | end | ||
− | function | + | local function age_ym(frame) |
− | + | -- Return age in years and months between two given dates, or | |
− | + | -- between given date and current date. | |
− | + | -- The older date is usually first; result is negative if the first is newer. | |
− | + | local date1, date2 = get_dates(frame, false) | |
− | + | if not date1 then | |
− | + | return message('Need valid year, month, day') | |
+ | end | ||
+ | if not date2 then | ||
+ | return message('Second date should be year, month, day') | ||
+ | end | ||
+ | return _age_ym(date2 - date1) | ||
end | end | ||
− | + | --[[ TODO | |
− | + | All these templates usually have the older date first (otherwise result is negative). | |
− | + | * = date1, date2 positional; date2 defaults to current | |
− | + | age_days(frame) * also accepts named y,m,d and text dates | |
− | + | age_years(frame) * | |
− | + | age_yd(frame) * also accepts text dates | |
− | + | age_ym(frame) * | |
− | + | age_ymd(frame) * also accepts named y,m,d; can omit d for "or" | |
− | + | age_ymwd(frame) . named only; defaults to current | |
− | + | ]] | |
− | + | local function age_years(frame) | |
− | + | -- TODO d1, d2 positional; d2 defaults to current; usually d1 < d2 | |
− | |||
− | |||
end | end | ||
− | + | local function age_yd(frame) | |
− | local | + | -- TODO |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | + | local function age_ymd(frame) | |
− | function | + | -- TODO |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | + | local function age_ymwd(frame) | |
− | local function | + | -- TODO |
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | local function | + | local function gsd_ymd(frame) |
− | + | -- Return Gregorian serial date of the given date, or the current date. | |
− | + | -- Like {{Gregorian serial date}}, a missing argument is replaced from the | |
− | + | -- current date, so can get a bizarre mixture of specified/current y/m/d. | |
− | + | -- This also accepts positional arguments, although the original template does not. | |
− | + | -- The returned value is negative for dates before 1 January 1 AD despite | |
− | + | -- the fact that GSD is not defined for earlier dates. | |
− | + | local args = frame:getParent().args | |
− | + | local date = Date( | |
− | + | date_component(args.year , args[1], 'year' ), | |
− | + | date_component(args.month, args[2], 'month'), | |
− | + | date_component(args.day , args[3], 'day' ) | |
− | + | ) | |
− | + | if date then | |
− | + | return tostring(date.gsd) | |
− | + | end | |
− | + | return message('Need valid year, month, day') | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | local function | + | local function ymd_from_jd(frame) |
− | + | -- Return formatted date from a Julian date. | |
− | + | -- The result is y-m-d or y-m-d H:M:S if input includes a fraction. | |
− | + | -- The word 'Julian' is accepted for the Julian calendar. | |
− | + | 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 | end | ||
− | local function | + | local function ymd_to_jd(frame) |
− | + | -- Return Julian date (a number) from a date (y-m-d), or datetime (y-m-d H:M:S), | |
− | + | -- or the current date ('currentdate') or current datetime ('currentdatetime'). | |
− | + | -- The word 'Julian' is accepted for the Julian calendar. | |
− | + | 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 | end | ||
− | return { age_days = age_days, age_ym = age_ym, gsd = gsd_ymd } | + | return { |
+ | age_days = age_days, | ||
+ | age_years = age_years, | ||
+ | age_yd = age_yd, | ||
+ | age_ym = age_ym, | ||
+ | age_ymd = age_ymd, | ||
+ | age_ymwd = age_ymwd, | ||
+ | gsd = gsd_ymd, | ||
+ | JULIANDAY = ymd_to_jd, | ||
+ | ymd_from_jd = ymd_from_jd, | ||
+ | ymd_to_jd = ymd_to_jd, | ||
+ | } |
Revision as of 00:48, 14 May 2016
Documentation for this module may be created at Module:Age/sandbox/doc
-- Implement various "age of" and other date-related templates.
local datemod = require('Module:Date')
local current = datemod._current
local Date = datemod._Date
local MINUS = '−' -- Unicode U+2212 MINUS SIGN
local function strip_to_nil(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 number_name(number, singular, plural, sep)
-- Return the given number, converted to a string, with the
-- separator (default space) and singular or plural name appended.
plural = plural or (singular .. 's')
sep = sep or ' '
return tostring(number) .. sep .. ((number == 1) and singular or plural)
end
local function message(msg, nocat)
-- Return formatted message text for an error.
-- Can append "#FormattingError" to URL of a page with a problem to find it.
local anchor = '<span id="FormattingError" />'
local category
if not nocat and mw.title.getCurrentTitle():inNamespaces(0, 10) then
-- Category only in namespaces: 0=article, 10=template.
category = '[[Category:Age error]]'
else
category = ''
end
return anchor ..
'<strong class="error">Error: ' ..
msg ..
'</strong>' ..
category .. '\n'
end
local function date_component(named, positional, component)
-- Return the first of the two arguments (named like {{example|year=2001}}
-- or positional like {{example|2001}}) that is not nil and is not empty.
-- If both are nil, return the current date component, if specified.
-- This translates empty arguments passed to the template to nil, and
-- optionally replaces a nil argument with a value from the current date.
named = strip_to_nil(named)
if named then
return named
end
positional = strip_to_nil(positional)
if positional then
return positional
end
if component then
return current[component]
end
return nil
end
local function get_dates(frame, want_mixture)
-- Return date1, date2 from template parameters.
-- Either may be nil if given arguments are invalid.
-- If want_mixture 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.
-- TODO Accept a date as a string like "1 April 2016".
local date1, date2
local args = frame:getParent().args
if want_mixture then
date1 = Date(
date_component(args.year1 , args[1], 'year' ),
date_component(args.month1, args[2], 'month'),
date_component(args.day1 , args[3], 'day' )
)
date2 = Date(
date_component(args.year2 , args[4], 'year' ),
date_component(args.month2, args[5], 'month'),
date_component(args.day2 , args[6], 'day' )
)
else
local fields = {}
for i = 1, 6 do
fields[i] = strip_to_nil(args[i])
end
date1 = Date(fields[1], fields[2], fields[3])
if fields[4] and fields[5] and fields[6] then
date2 = Date(fields[4], fields[5], fields[6])
else
date2 = Date('currentdate')
end
end
return date1, date2
end
local function age_days(frame)
-- Return age in days between two given dates, or
-- between given date and current date.
-- This code implements the logic in [[Template:Age in days]].
-- Like {{Age in days}}, a missing argument is replaced from the current
-- date, so can get a bizarre mixture of specified/current y/m/d.
local date1, date2 = get_dates(frame, true)
if not (date1 and date2) then
return message('Need valid year, month, day')
end
local sign = ''
local result = date2.jd - date1.jd
if result < 0 then
sign = '−'
result = -result
end
return sign .. tostring(result)
end
local function _age_ym(diff)
-- Return text specifying date difference in years, months.
local sign = diff.isnegative and MINUS or ''
local years, months = diff:age('ym')
local mtext = number_name(months, 'month')
local result
if years > 0 then
local ytext = number_name(years, 'year')
if months == 0 then
result = ytext
else
result = ytext .. ', ' .. mtext
end
else
if months == 0 then
sign = ''
end
result = mtext
end
return sign .. result
end
local function age_ym(frame)
-- Return age in years and months between two given dates, or
-- between given date and current date.
-- The older date is usually first; result is negative if the first is newer.
local date1, date2 = get_dates(frame, false)
if not date1 then
return message('Need valid year, month, day')
end
if not date2 then
return message('Second date should be year, month, day')
end
return _age_ym(date2 - date1)
end
--[[ TODO
All these templates usually have the older date first (otherwise result is negative).
* = date1, date2 positional; date2 defaults to current
age_days(frame) * also accepts named y,m,d and text dates
age_years(frame) *
age_yd(frame) * also accepts text dates
age_ym(frame) *
age_ymd(frame) * also accepts named y,m,d; can omit d for "or"
age_ymwd(frame) . named only; defaults to current
]]
local function age_years(frame)
-- TODO d1, d2 positional; d2 defaults to current; usually d1 < d2
end
local function age_yd(frame)
-- TODO
end
local function age_ymd(frame)
-- TODO
end
local function age_ymwd(frame)
-- TODO
end
local function gsd_ymd(frame)
-- Return Gregorian serial date of the given date, or the current date.
-- Like {{Gregorian serial date}}, a missing argument is replaced from the
-- current date, so can get a bizarre mixture of specified/current y/m/d.
-- This also accepts positional arguments, although the original template does not.
-- The returned value is negative for dates before 1 January 1 AD despite
-- the fact that GSD is not defined for earlier dates.
local args = frame:getParent().args
local date = Date(
date_component(args.year , args[1], 'year' ),
date_component(args.month, args[2], 'month'),
date_component(args.day , args[3], 'day' )
)
if date then
return tostring(date.gsd)
end
return message('Need valid year, month, day')
end
local function ymd_from_jd(frame)
-- Return formatted date from a Julian date.
-- The result is y-m-d or y-m-d H:M:S if input includes a fraction.
-- The word 'Julian' is accepted for the Julian calendar.
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 ymd_to_jd(frame)
-- Return Julian date (a number) from a date (y-m-d), or datetime (y-m-d H:M:S),
-- or the current date ('currentdate') or current datetime ('currentdatetime').
-- The word 'Julian' is accepted for the Julian calendar.
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
return {
age_days = age_days,
age_years = age_years,
age_yd = age_yd,
age_ym = age_ym,
age_ymd = age_ymd,
age_ymwd = age_ymwd,
gsd = gsd_ymd,
JULIANDAY = ymd_to_jd,
ymd_from_jd = ymd_from_jd,
ymd_to_jd = ymd_to_jd,
}