Module:Sandbox/trappist the monk/dualdate

require('strict'); local getArgs = require ('Module:Arguments').getArgs;							-- get parameters from frame

local months = { ['1'] = 'January', ['2'] = 'February', ['3'] = 'March', ['4'] = 'April', ['5'] = 'May', ['6'] = 'June', ['7'] = 'July', ['8'] = 'August', ['9'] = 'September', ['10'] = 'October', ['11'] = 'November', ['12'] = 'December' };

local islamic_months = { ['1'] = 'Muḥarram', ['2'] = 'Ṣafar', ['3'] = 'Rabīʿ al-Awwal', ['4'] = 'Rabīʿ ath-Thānī', ['5'] = 'Jumādá al-Ūlá', ['6'] = 'Jumādá al-Ākhirah', ['7'] = 'Rajab', ['8'] = 'Sha‘bān', ['9'] = 'Ramaḍān', ['10'] = 'Shawwāl', ['11'] = 'Dhū al-Qa‘dah', ['12'] = 'Dhū al-Ḥijjah' };

local formats = {																-- years different ['dmyspdmyp'] = '%s %s %s%s %s%s %s %s%s',									-- day month year suffix prefix day month year postfix ['mdyspmdyp'] = '%s %s, %s%s %s%s %s, %s%s',								-- month day, year suffix prefix month day, year postfix

-- months different ['dmpdmpy'] = '%s %s %s%s %s%s %s',											-- day month prefix day month postfix year ['mdpmdpy'] = '%s %s %s%s %s%s, %s',										-- month day prefix month day postfix, year

-- days different ['dpdpmy'] = '%s %s%s%s %s %s',												-- day prefix day month postfix year ['mdpdpy'] = '%s %s %s%s%s, %s',											-- month day prefix day postfix, year -- month and year dates ['mypmyp'] = '%s %s %s%s %s%s',												-- month year prefix month year postfix ['mpmpy'] = '%s %s%s%s %s',													-- month prefix month postfix year -- year dates ['ypyp'] = '%s %s%s%s',														-- year prefix year postfix }

--[[--< G E T _ D A T E S >

make sure that appropriate date parts of both dates are present. If ok, fills the dates table.

Returns error message on failure, nil else

TODO: validate dates? make sure that date1 is earlier or later than date2 as appropriate? support dates in natural language format? ISO format as input?

]]

local function get_dates (args, dates, m1, m2) if not (args[1] and args[4]) then											-- must have year1 and year2 return 'error: both years are required '; elseif args[2] and args[5] then												-- both months ... if (args[3] and not args[6]) or											-- ... and first day but not second day (not args[3] and args[6]) then										-- ... and second day but not first day return 'error: both days are required '; end if not ('0' < args[2] and '13' > args[2]) and ('0' < args[5] and '13' > args[5]) then return 'error: invalid month '; end elseif (args[2] and not args[5]) or											-- first month but not second month (not args[2] and args[5]) then											-- second month but not first month return 'error: both months are required '; end

dates = { year1 = args[1],														-- first date parts month1 = args[2] and m1[args[2]], day1 = args[3], year2 = args[4],														-- second date parts month2 = args[5] and m2[args[5]], day2 = args[6], dmy = not args.df or ('dmy' == args.df)									-- make boolean; default to dmy format }

return nil, dates; end

--[[--< _ R E N D E R _ D A T E S >

renders both dates according to which date parts are present and which are not equal

]]

local function _render_dates (dates, prefix, suffix) local prefix = ' (' .. prefix .. ': ';	local postfix = ') '; if not suffix then suffix = '';															-- for concatenation end

if dates.year1 and dates.month1 and dates.day1 then							-- year, month, and day dates if dates.year1 ~= dates.year2 then										-- annotation wraps second date if dates.dmy then return string.format (formats['dmyspdmyp'], dates.day1, dates.month1, dates.year1, suffix, prefix, dates.day2, dates.month2, dates.year2, postfix); else return string.format (formats['mdyspmdyp'], dates.month1, dates.day1, dates.year1, suffix, prefix, dates.month2, dates.day2, dates.year2, postfix); end elseif dates.month1 ~= dates.month2 then								-- annotation wraps second month and day if dates.dmy then return string.format (formats['dmpdmpy'], dates.day1, dates.month1, prefix, dates.day2, dates.month2, postfix, dates.year2); else return string.format (formats['mdpmdpy'], dates.month1, dates.day1, prefix, dates.month2, dates.day2, postfix, dates.year2); end elseif dates.day1 ~= dates.day2 then									-- annotation wraps second day if dates.dmy then return string.format (formats['dpdpmy'], dates.day1, prefix, dates.day2, postfix, dates.month2, dates.year2); else return string.format (formats['mdpdpy'], dates.month1, dates.day1, prefix, dates.day2, postfix, dates.year2); end else return 'error: identical YMD dates '; end elseif dates.year1 and dates.month1 then									-- year and month dates if dates.year1 ~= dates.year2 then										-- annotation wraps second month year return string.format (formats['mypmyp'], dates.month1, dates.year1, prefix, dates.month2, dates.year2, postfix); elseif dates.month1 ~= dates.month2 then								-- annotation wraps second month return string.format (formats['mpmpy'], dates.month1, prefix, dates.month2, postfix, dates.year2); else return 'error: identical YM dates '; end else																		-- year dates if dates.year1 ~= dates.year2 then										-- annotation wraps second year return string.format (formats['ypyp'], dates.year1, prefix, dates.year2, postfix); else return 'error: identical Y dates '; end end end

--[[--< N S O S _ D A T E >

module entry point

ns date listed first followed by os date; os date has os annotation

]]

local function nsos_date (frame) local args = getArgs(frame);												-- get parameters local dates = {}; local msg;

msg, dates = get_dates (args, dates, months, months); if msg then return message;															-- date check failed show a message end

local prefix = (args.alt and args.alt) or ('no' == args.link and 'O.S.' or 'O.S.'); return _render_dates (dates, prefix) end

--[[--< O S N S _ D A T E >

module entry point

os date listed first followed by ns date; ns date has ns annotation

]]

local function osns_date (frame) local args = getArgs(frame);												-- get parameters local dates = {}; local msg;

msg, dates = get_dates (args, dates, months, months); if msg then return message;															-- date check failed show a message end

local prefix = (args.alt and args.alt) or ('no' == args.link and 'N.S.' or 'N.S.'); return _render_dates (dates, prefix) end

--[[--< C E A H _ D A T E >

module entry point

ce date listed first followed by ah date; ah date has ah annotation

]]

local function ceah_date (frame) local args = getArgs(frame);												-- get parameters local dates = {}; local msg;

msg, dates = get_dates (args, dates, months, islamic_months); if msg then return message;															-- date check failed show a message end

local prefix = 'no' == args.link and 'A.H.' or 'A.H.'; return _render_dates (dates, prefix) end

--[[--< A H C E _ D A T E >

module entry point

ah date listed first followed by ce date; ce date has ce annotation

]]

local function ahce_date (frame) local args = getArgs(frame);												-- get parameters local dates = {}; local msg; local prefix, suffix;

msg, dates = get_dates (args, dates, islamic_months, months); if msg then return message;															-- date check failed show a message end

prefix = 'no' == args.link and 'CE' or 'CE'; suffix = ' ' .. ('no' == args.link and 'A.H.' or 'A.H.');	-- leading space required return _render_dates (dates, prefix, suffix); end

----< E X P O R T E D  F U N C T I O N S >--

return { ahce_date = ahce_date, ceah_date = ceah_date, nsos_date = nsos_date, osns_date = osns_date }