Module:Calendar date
Appearance
![]() | This module is rated as alpha. It is ready for third-party input, and may be used on a few pages to see if problems arise, but should be watched. Suggestions for new features or changes in their input and output mechanisms are welcome. |
![]() | This module depends on the following other modules: |
This module implements Template:Calendar date (talk · links · edit).
Usage
{{#invoke:Calendar date|function_name}}
--[[
Display non-Gregorian holiday dates using equivalent Gregorian date
]]
local p = {}
--[[--------------------------< inlineError >-----------------------
Critical error. Render output completely in red. Add to tracking category.
]]
local function inlineError(arg, msg, tname)
track["Category:Calendar date template errors"] = 1
return '<span style="font-size:100%" class="error citation-comment">Error in {{' .. tname .. '}} - Check <code style="color:inherit; border:inherit; padding:inherit;">|' .. arg .. '=</code> ' .. msg .. '</span>'
end
--[[--------------------------< trimArg >-----------------------
trimArg returns nil if arg is "" while trimArg2 returns 'true' if arg is ""
trimArg2 is for args that might accept an empty value, as an on/off switch like nolink=
]]
local function trimArg(arg)
if arg == "" or arg == nil then
return nil
else
return mw.text.trim(arg)
end
end
local function trimArg2(arg)
if arg == nil then
return nil
else
return mw.text.trim(arg)
end
end
--[[--------------------------< tableLength >-----------------------
Given a 1-D table, return number of elements
]]
local function tableLength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
--[[--------------------------< createTracking >-----------------------
Return data in track[] ie. tracking categories
]]
local function createTracking()
local sand = ""
if tableLength(track) > 0 then
for key,_ in pairs(track) do
sand = sand .. "[[" .. key .. "]]"
end
end
return sand
end
--[[--------------------------< verifyDate >-----------------------
Given the date arg, return true if within date range 2000-2050 else return false
]]
local function verifyDate(date)
if not date or date == "" then
return nil
end
if tonumber(date) > 1999 and tonumber(date) < 2051 then
return "true"
else
return nil
end
end
--[[--------------------------< makeDate >-----------------------
Given a zero-padded 4-digit year, 2-digit month and 2-digit day, return a full date in df format
df = mdy, dmy, iso, ymd
]]
local function makeDate(year, month, day, df, format)
if not year or year == "" or not month or month == "" or not day or day == "" then
return nil
end
local zmonth = month -- month with leading 0
month = month:match("0*(%d+)") -- month without leading 0
if tonumber(month) < 1 or tonumber(month) > 12 then
return year
end
local nmonth = os.date("%B", os.time{year=2000, month=month, day=1} ) -- month in name form
if not nmonth then
return year
end
local zday = day
day = zday:match("0*(%d+)")
if tonumber(day) < 1 or tonumber(day) > 31 then
if df == "mdy" or df == "dmy" then
return nmonth .. " " .. year
elseif df == "iso" then
return year .. "-" .. zmonth
elseif df == "ymd" then
return year .. " " .. nmonth
else
return nmonth .. " " .. year
end
end
if format ~= "infobox" then
if df == "mdy" then
return nmonth .. " " .. day .. ", " .. year -- September 1, 2016
elseif df == "dmy" then
return day .. " " .. nmonth .. " " .. year -- 1 September 2016
elseif df == "iso" then
return year .. "-" .. zmonth .. "-" .. zday -- 2016-09-01
elseif df == "ymd" then
return year .. " " .. nmonth .. " " .. day -- 2016 September 1
else
return nmonth .. " " .. day .. ", " .. year -- September 1, 2016
end
else
if df == "mdy" then
return nmonth .. " " .. day -- September 1
elseif df == "dmy" then
return day .. " " .. nmonth -- 1 September
elseif df == "iso" then
return year .. "-" .. zmonth .. "-" .. zday -- 2018-09-01
elseif df == "ymd" then
return nmonth .. " " .. day -- September 1
else
return nmonth .. " " .. day -- September 1
end
end
end
--[[--------------------------< dateOffset >-----------------------
Given a 'origdate' in ISO format, return the date offset by number of days in 'offset'
eg. given "2018-02-01" and "-1" it will return "2018-01-30"
On error, return origdate
]]
function dateOffset(origdate, offset)
local datesplit = {}
datesplit = mw.text.split(origdate, "-")
datesplit[1], datesplit[2], datesplit[3] = tonumber(datesplit[1]), tonumber(datesplit[2]), tonumber(datesplit[3])
local now = os.time{year = datesplit[1], month = datesplit[2], day = datesplit[3]}
local newdate = os.date("%Y-%m-%d", now + (tonumber(offset) * 24 * 3600))
if not newdate then
return origdate
else
return newdate
end
end
--[[--------------------------< renderHoli >-----------------------
Render the data
]]
function renderHoli(json,holiday,date,df,format,tname)
local numRecords = tableLength(json.items)
local hits = 0
local matchdate = "^" .. date
local startdate,enddate,outoffset,endoutoffset = nil
local starttitle,endtitle = ""
-- Get first and last date of holiday
for i = 1, numRecords do
if mw.ustring.find( json.items[i].date, matchdate ) then
if hits == 0 then
startdate = json.items[i].date
starttitle = json.items[i].title
hits = 1
end
if hits >= tonumber(json.days) then
enddate = json.items[i].date
endtitle = json.items[i].title
break
end
hits = hits + 1
end
end
-- Verify data is OK
if startdate == nil or enddate == nil then
if mw.ustring.find( starttitle, "Chanukah" ) then -- Hanukkah bug, template doesn't support cross-year boundary
enddate = dateOffset(startdate, 8)
else
return nil
end
end
-- Generate start-date offset (ie. holiday starts the evening before the given date)
if json.startoffset then
startdate = dateOffset(startdate, json.startoffset)
if startdate ~= enddate then
enddate = dateOffset(enddate, json.startoffset)
else
if json.days == "1" then
json.days = "2"
end
end
end
-- Generate end-date outside-Irael offset (ie. outside Israel the holiday ends +1 day later)
if json.endoutoffset then
endoutoffset = dateOffset(enddate, json.endoutoffset)
end
-- Format dates into df format
local datesplit = {}
datesplit = mw.text.split(startdate, "-")
startdate = makeDate(datesplit[1], datesplit[2], datesplit[3], df, format)
datesplit = mw.text.split(enddate, "-")
enddate = makeDate(datesplit[1], datesplit[2], datesplit[3], df, format)
if startdate == nil or enddate == nil then return nil end
-- Add "outside of Israel" notices
if endoutoffset then
datesplit = mw.text.split(endoutoffset, "-")
local leader = " "
if format == "infobox" then leader = "<br>" end
endoutoffset = leader .. "(" .. makeDate(datesplit[1], datesplit[2], datesplit[3], df, "infobox") .. " outside of Israel)"
end
if not endoutoffset then
endoutoffset = ""
end
-- generate format string
if format == "infobox" then
format = " –<br>"
else
format = " – "
end
-- return output
if startdate == enddate or json.days == "1" then -- single date
return json.prepend1 .. startdate .. endoutoffset
else
return json.prepend1 .. startdate .. format .. json.prepend2 .. enddate .. endoutoffset
end
end
--[[--------------------------< calendardate >-----------------------
Main function
]]
function p.calendardate(frame)
local pframe = frame:getParent()
local args = pframe.args
local tname = "Calendar date" -- name of calling template. Change if template rename.
local holiday = nil -- name of holiday
local date = nil -- date of holiday (year)
local df = nil -- date format (mdy, dmy, iso - default: iso)
local format = nil -- template display format options
track = {} -- global tracking-category table
--- Determine holiday
holiday = trimArg(args.holiday) -- required
if not holiday then
holiday = trimArg(args.event) -- event alias
if not holiday then
return inlineError("holiday", "Missing holiday argument", tname) .. createTracking()
end
end
--- Determine date
date = trimArg(args.year) -- required
if not date then
return inlineError("year", "Missing year argument", tname) .. createTracking()
elseif not verifyDate(date) then
return inlineError("year", "Invalid year", tname) .. createTracking()
end
--- Determine format type
format = trimArg(args.format)
if not format then
format = "none"
elseif format ~= "infobox" then
format = "none"
end
--- Parse JSON file
local version = mw.title.makeTitle( 'Template', tname .. '/holidays/' .. holiday .. '.js' )
if not version.exists then
return inlineError("holiday", "File missing Template:" .. tname .. "/holidays/" .. holiday .. ".js", tname) .. createTracking()
end
local json = nil
if version.isRedirect then
json = mw.text.jsonDecode( version.redirectTarget:getContent() )
else
json = mw.text.jsonDecode( version:getContent() )
end
--- Determine df - priority to |df in template, otherwise df in datafile, otherwise default to dmy
df = trimArg(args.df)
if not df then
if json.df then
df = json.df
else
df = "dmy"
end
end
if df ~= "mdy" and df ~= "dmy" and df ~= "iso" then
df = "dmy"
end
--- Determine pre-pended text eg. "sunset, <date>"
if not json.prepend1 then
json.prepend1 = ""
else
json.prepend1 = json.prepend1 .. ", "
end
if not json.prepend2 then
json.prepend2 = ""
else
json.prepend2 = json.prepend2 .. ", "
end
-- Render
local rend = renderHoli(json,holiday,date,df,format,tname)
if not rend then
rend = '<span style="font-size:100%" class="error citation-comment">Error in [[:Template:' .. tname .. ']]: Unknown problem. Please report on template talk page.</span>'
track["Category:Webarchive template errors"] = 1
end
return rend .. createTracking()
end
return p