Module:Sandbox/Ajuanca/Dates
< Module:Sandbox | Ajuanca
-- Task 9 of GCI 2019
local p = {}
-- Main function
function p.formatDates(frame)
local inputDate = tostring(frame.args.name)
local inputFormat=tostring(frame.args.format)
local inputTable = p.divideString(inputDate)
local str=""
local myDate = p.getDate(inputTable)
local dateResults = p.checkDate(myDate)
if(not(dateResults.isDateCorrect))then
return dateResults.errorMessage
else
local outputFormat=p.chooseFormat(myDate, inputFormat)
local completeMonths={"January", "February", "March", "April", "May", "June", "July", "August",
"September", "October", "November", "December"}
if(myDate.isAprox)then
str="circa "
end
if(outputFormat=="dmy")then
str = str .. myDate.day .. " " .. completeMonths[myDate.month] .. " " .. myDate.year
elseif(outputFormat=="mdy")then
str = str .. completeMonths[myDate.month] .. " " .. myDate.day .. ", " .. myDate.year
elseif(outputFormat=="iso")then
str = str .. myDate.year .. "-" .. myDate.month .. "-" .. myDate.day
elseif(outputFormat=="y")then
str=str..myDate.year
elseif(outputFormat=="my")then
str=str.. completeMonths[myDate.month] .. " " .. myDate.year
end
if(myDate.specifiedEra~= nil)then
str = str .." "..myDate.specifiedEra
end
end
return str
end
-- Selects correct output format.
-- Returns a String
function p.chooseFormat(dateObject, prefference)
local correctFormat=""
if(dateObject.dateFormat~= nil)then
correctFormat=dateObject.dateFormat
else
correctFormat="#error"
end
local compatibleFormats = {"dmy", "mdy", "iso", "y", "my"}
for n, actualFormat in ipairs(compatibleFormats) do
if (prefference:lower() == actualFormat) then
correctFormat=prefference:lower()
break
end
end
return correctFormat
end
-- Truncates the input by spaces.
-- Returns a table
function p.divideString(stringSentence)
local nameTable = {}
stringSentence = string.gsub(stringSentence, "-", " ")
stringSentence = string.gsub(stringSentence, "/", " ")
for m in string.gmatch(stringSentence, ("%S+")) do
table.insert(nameTable, m)
end
return nameTable
end
-- Detects input date.
-- Returns a Object
function p.getDate(stringTable)
local comparison = ""
local finalDate = {
day = 0,
month = 0,
year = 0,
isAprox=false,
specifiedEra=nil,
dateFormat=nil
}
local dateIsStored = false
-- Matchs string month with correspoding number.
-- Returns number
function getMonth(strInput)
local months = {"jan", "feb", "mar", "apr", "may", "june", "july", "aug", "sept",
"oct", "nov", "dec"}
if(strInput:find("(%a+)"))then
for w, month in ipairs(months)do
if(strInput:lower():find(month)~= nil)then
return tonumber(w)
end
end
end
return 0
end
-- Match given ordinal number to its cardinal value.
-- Returns a number
function getCardinal(inputStr)
if(inputStr:find("(%d+)(%a+)"))then
local number, termination=inputStr:lower():match("(%d+)(%a+)")
suffixes = {"st", "nd", "rd", "th"}
for g, suffix in ipairs(suffixes)do
if(termination:find(suffix))then
return tonumber(number)
end
end
end
return 0
end
-- Start comparison
for z, number in ipairs(stringTable) do
-- Creates a string of three followed items.
if(z<=#stringTable-2)then
local comparison = stringTable[z] .. " " .. stringTable[z+1] .. " " .. stringTable[z+2]
-- Search for the format 'typedMonth Day, Year'.
if(comparison:lower():find("(%a+) (%d+), (%d+)"))then
local m, d, y = comparison:lower():match("(%a+) (%d+), (%d+)")
finalDate.month=tonumber(getMonth(m))
finalDate.year=tonumber(y)
finalDate.day=tonumber(d)
finalDate.dateFormat="mdy"
dateIsStored=true
end
-- Search for the format 'Day typedMonth Year'
if(comparison:lower():find("(%d+) (%a+) (%d+)") and not(dateIsStored))then
local d, m, y = comparison:lower():match("(%d+) (%a+) (%d+)")
finalDate.month=getMonth(m:lower())
finalDate.year=tonumber(y)
finalDate.day=tonumber(d)
finalDate.dateFormat="dmy"
if(finalDate.month==0)then
dateIsStored=false
else
dateIsStored=true
end
end
-- Search for the format 'Day Month Year' or 'Year Month Day'
if(comparison:lower():find("(%d+) (%d+) (%d+)") and not(dateIsStored))then
local d, m, y= comparison:lower():match("(%d+) (%d+) (%d+)")
-- If day and year are both less than 31, it'll be considered d-m-y
if(tonumber(d)>31)then
local temporalDay=y
y=d
d=temporalDay
end
finalDate.month=tonumber(m)
finalDate.year=tonumber(y)
finalDate.day=tonumber(d)
-- This format isn't allowed. The most similar is ISO.
finalDate.dateFormat="iso"
if(finalDate.month==0)then
dateIsStored=false
else
dateIsStored=true
end
end
-- Search for format 'ordinalDay typedMonth Year'
if(comparison:lower():find("(%w+) (%a+) (%d+)") and not(dateIsStored))then
local d, m, y=comparison:lower():match("(%w+) (%a+) (%d+)")
finalDate.day=getCardinal(d)
finalDate.month=getMonth(m)
finalDate.year=y
-- This format isn't allowed. The most similar is ISO.
finalDate.dateFormat="iso"
if(finalDate.month==0)then
dateIsStored=false
else
dateIsStored=true
end
end
end
-- Searchs for aproximations indicators
local aproxIndicators = {"circa", "approx", "around", "uncertain"}
for z, indicator in ipairs(aproxIndicators)do
if(number:lower():match(indicator))then
finalDate.isAprox=true
end
end
-- Searchs for era indicators. Note that, althought it isn't used,
-- the value g gives us the calendar used.
local eraIndicators={{"bce", "bc"}, {"ce", "ad"}}
for d, era in ipairs(eraIndicators)do
for g, specific in ipairs(era)do
if(specific==number:lower())then
finalDate.specifiedEra=specific:upper()
end
end
end
end
if(not(dateIsStored))then
for k, number in ipairs(stringTable) do
-- Creates a string of two followed elements.
if(k<=#stringTable-1)then
local comparison = stringTable[k] .. " " .. stringTable[k+1]
-- Search for a date in format 'Day typedMonth'
if(comparison:lower():find("(%d+) (%a+)"))then
local d, m=comparison:lower():match("(%d+) (%a+)")
finalDate.day=tonumber(d)
finalDate.month=tonumber(getMonth(m))
-- This format isn't allowed. The most similar is ISO.
finalDate.dateFormat="iso"
if(finalDate.month==0)then
dateIsStored=false
else
dateIsStored=true
end
-- Search for a date in format 'ordinalDay typedMonth'
elseif(comparison:lower():find("(%w+) (%a+)"))then
local d, m=comparison:lower():match("(%w+) (%a+)")
finalDate.day=getCardinal(d)
finalDate.month=getMonth(m)
-- This format isn't allowed. The most similar is ISO.
finalDate.dateFormat="iso"
if(finalDate.day==0)then
dateIsStored=false
else
dateIsStored=true
end
end
-- Search for a date in format 'typedMonth Year'
if(comparison:lower():find("(%a+) (%d+)"))then
local m, y=comparison:lower():match("(%a+) (%d+)")
finalDate.month=getMonth(m)
finalDate.year=tonumber(y)
finalDate.dateFormat="my"
if(finalDate.month==0)then
dateIsStored=false
else
dateIsStored=true
end
end
end
end
end
-- Search for a result if nothing is still found.
if(not(dateIsStored))then
local numbersFound = {}
for n, number in ipairs(stringTable)do
-- Search for possible given values in an non-regulated order.
if(number:find("(%a+)")~=nil)then
if(finalDate.day==0 and number:find("(%d+)"))then
-- Search for ordinal day
finalDate.day=getCardinal(number)
if(not(finalDate.day==0))then
finalDate.dateFormat="dmy"
end
elseif(finalDate.month==0)then
-- Search for months
finalDate.month=tonumber(getMonth(number))
if(not(finalDate.month==0))then
finalDate.dateFormat="my"
end
end
else
table.insert(numbersFound, tonumber(number))
end
if(#numbersFound==1 and #stringTable==n)then
finalDate.year=numbersFound[1]
if((finalDate.month~=0) and (finalDate.day~=0))then
finalDate.dateFormat="dmy"
elseif(finalDate.month~=0)then
finalDate.dateFormat="my"
else
finalDate.dateFormat="y"
end
elseif(#numbersFound==2 and #stringTable==n)then
if(numbersFound[1]<=31) then
finalDate.day=numbersFound[1]
finalDate.year=numbersFound[2]
else
finalDate.year=numberFound[1]
finalDate.day=numberFound[2]
end
finalDate.dateFormat="y"
end
end
end
return finalDate
end
-- Checks that given date
-- Returns a object
function p.checkDate(givenDate)
-- Detects is the given year is leap.
-- Returns a boolean
function isLeap(yearInput)
local yearIsLeap = (tonumber(yearInput)%4)==0
if((tonumber(yearInput)%100)==0 )then
yearIsLeap = (tonumber(yearInput)%400)==0
end
return yearIsLeap
end
local finalObject = {
isDateCorrect=true,
errorMessage=""
}
-- Check if day is correct
if(not(givenDate.day==nil))then
if(givenDate.day>31)then
finalObject.isDateCorrect=false
finalObject.errorMessage="That day doesn't exists!"
return finalObject
end
end
if(not(givenDate.month==nil))then
if(givenDate.month==2)then
if(isLeap(givenDate.year))then
if(givenDate.day>29)then
finalObject.isDateCorrect=false
finalObject.errorMessage="That day doesn't exists!"
end
else
if(givenDate.day>=29)then
finalObject.isDateCorrect=false
finalObject.errorMessage="That day doesn't exists!"
end
end
end
end
-- Check if month is correct
if(not(givenDate.month==nil))then
if(givenDate.month>12)then
finalObject.isDateCorrect=false
finalObject.errorMessage="That month doesn't exist!"
end
end
-- Check if date is nil
if(givenDate.year==0 and givenDate.day==0 and givenDate.month==0)then
finalObject.isDateCorrect=false
finalObject.errorMessage="No date was detected."
end
return finalObject
end
return p