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

th_comma: sec2dur: getArgs: dayStart: dayEnd: strDate: strMultiDate: --]=]

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

-- return string with "," as thousands seperator function p.th_comma(n) -- credit http://richard.warburton.it	local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$') return left..(num:reverse:gsub('(%d%d%d)','%1,'):reverse)..right end

-- return duration string from number of seconds function p.sec2dur(seconds, full) local dur if type(seconds) ~= "number" then return seconds end if (seconds <= 60) then dur = seconds .. (full and " Seconds" or "s") elseif (seconds <= 3600) then dur = math.floor(seconds/60) .. (full and " Minutes" or "m") else dur = math.floor(seconds/3600) .. (full and " Hours" or "h") end return dur 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 elseif type(frame) == 'table' then for k, v in pairs(frame) do			if type(v) == 'string' then v = v:match('^%s*(.-)%s*$') if v == "" then v = nil end end tArgs[k] = v		end else tArgs = frame end

return tArgs end

-- Date Functions local eventstarttime = " 16:00:00 UTC" 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 eventstarttime end function p.dayEnd -- This specifies the UTC time the events start -- Used to be 18:00:00 UTC, but currently has been changed to 19:00:00 UTC return " 19:00:00 UTC" 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 .. eventstarttime)

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