Module:Utility

--[=[

LUA Module for Utility Functions

wordinal: Word-Ordinal form of number Parameters: 1 = Number (0-99) Return: Ordinal in Word form of number "??" for 0 or non-numeric input Input number for number >= 100

ordinal: Ordinal Numbers Parameters: 1 = Number 2 = Cardinal (return cardinal number if true) Return: Ordinal Number (Or simply number if cardinal is true) it does not seem logical to have an ordinal function return cardinal numbers, but it saves 'Ifs' in the calling module --]=]

local p = {}

local date = require('Dev:Date')

local tOnesOrd = { [0] = '??',	[1] = 'first', [2] = 'second', [3] = 'third', [4] = 'fourth', [5] = 'fifth', [6] = 'sixth', [7] = 'seventh', [8] = 'eighth', [9] = 'ninth', [10] = 'tenth', [11] = 'eleventh', [12] = 'twelfth', [13] = 'thirteenth', [14] = 'fourteenth', [15] = 'fifteenth', [16] = 'sixteenth', [17] = 'seventeenth', [18] = 'eighteenth', [19] = 'nineteenth' }

local tTensOrd = { [2] = 'twentieth', [3] = 'thirtieth', [4] = 'fortieth', [5] = 'fiftieth', [6] = 'sixtieth', [7] = 'seventieth', [8] = 'eightieth', [9] = 'ninetieth' }

local tTens = { [2] = 'twenty', [3] = 'thirty', [4] = 'forty', [5] = 'fifty', [6] = 'sixty', [7] = 'seventy', [8] = 'eighty', [9] = 'ninety' }

-- return word form ordinal number function p.wordinal(num) local n = tonumber(num) or 0 if n >= 100 then return num end

if n < 20 then return tOnesOrd[n] elseif n % 10 == 0 then return tTensOrd[n / 10] else return tTens[math.floor(n / 10)] .. '-' .. tOnesOrd[n % 10] end end

local function _ordinal(num, cardinal) local n = tonumber(num) or 0 local i = math.fmod(n,10) local o = ""

if not cardinal then o = (i==1 and n~=11 and "st") or (i==2 and n~=12 and "nd") or (i==3 and n~=13 and "rd") or (n~=0 and "th") or "" end

return tostring(n) .. o end

-- return ordinal number function p.ordinal(num, cardinal) return _ordinal(num, cardinal) end

-- getArgs -- if no frame then local call -- if parameter 2: preferParent = true, the parent arguments take -- precedence over the frame arguments -- otherwise frame arguments take precedence over parent arguments function p.getArgs(frame, preferParent) local tArgs = {}

if       type(frame.args) == 'table' and type(frame.getParent) == 'function' then local fArgs = frame.args local parent = frame:getParent local pArgs = parent and parent.args or {}

if preferParent then fArgs, pArgs = pArgs, fArgs end for k, v in pairs(fArgs) do			if type(v) == 'string' then v = v:match('^%s*(.-)%s*$') if v == "" then v = nil end end tArgs[k] = v		end for k, v in pairs(pArgs) do			if not tArgs[k] then if type(v) == 'string' then v = v:match('^%s*(.-)%s*$') if v == "" then v = nil end end tArgs[k] = v			end end else tArgs = frame end

return tArgs end

-- Date Functions function p.dayStart -- This specifies the UTC time the events start -- Used to be 15:00:00 UTC, but currently has been changed to 16:00:00 UTC return " 16:00:00" end

-- Output Date in text format function p.strDate(sDate, days, noYear, cardinal, whenTo) if not sDate or sDate == "" then return "" end

local theDate = date(sDate)

local nDays = tonumber(days) or 0

local day = "" local retText = {}

if whenTo then if theDate < date then table.insert(retText, " started on ") else table.insert(retText, " will start on ") end end

if nDays > 0 then local start = theDate:copy theDate:adddays(nDays) table.insert(retText, _ordinal(start:getday, cardinal))

if whenTo or start:getmonth ~= theDate:getmonth then table.insert(retText, start:fmt(" %B")) end

if whenTo or (not noYear and start:getyear ~= theDate:getyear) then table.insert(retText, start:fmt(" %Y")) end

if whenTo then if theDate < date then table.insert(retText, " and ended on ") else table.insert(retText, " and will end on ") end else table.insert(retText, " - ") end end

table.insert(retText, _ordinal(theDate:getday, cardinal))

table.insert(retText, theDate:fmt(" %B"))

if not noYear then table.insert(retText, theDate:fmt(" %Y")) end

if whenTo and nDays > 0 then table.insert(retText, ", lasting ") table.insert(retText, tostring(nDays)) if nDays > 1 then table.insert(retText, " days") else table.insert(retText, " day") end end return table.concat(retText) end

-- output multiple dates in text format function p.strMultiDate(tDates, cardinal) if type(tDates) ~= 'table' then return "" end

table.sort(tDates)

local multiDates = {}

local prevDate = nil

local monAnd = nil local yearAnd = nil

for i = #tDates, 1, -1 do       local curDate = date(tDates[i]) local parts = {_ordinal(curDate:getday, cardinal), "", "", ""}

if prevDate then if prevDate:getyear ~= curDate:getyear then parts[2] = curDate:fmt(" %B") parts[3] = curDate:fmt(" %Y") if yearAnd then parts[4] = ", " else if monAnd then parts[4] = "; and " else parts[4] = " and " yearAnd = true end end monAnd = nil else if prevDate:getmonth ~= curDate:getmonth then parts[2] = curDate:fmt(" %B")

if monAnd then parts[4] = "; and " else parts[4] = " and " end monAnd = nil else if monAnd then parts[4] = ", " else parts[4] = " and " monAnd = true end end end else parts[2] = curDate:fmt(" %B") parts[3] = curDate:fmt(" %Y") end

multiDates[i] = parts prevDate = curDate end

prevDate = nil

monAnd = nil yearAnd = nil

for i = 1, #tDates do       local curDate = date(tDates[i])

if prevDate then if prevDate:getyear == curDate:getyear then if prevDate:getmonth == curDate:getmonth then if multiDates[i][4] == " and " and monAnd then multiDates[i][4] = "; and " end end end end

if multiDates[i][4] == " and " then monAnd = true end prevDate = curDate end

local retText = {}

for _, sDate in ipairs(multiDates) do       table.insert(retText, table.concat(sDate)) end

return table.concat(retText) end

return p