Difference between revisions of "Module:Dts"

From Timelines
Jump to: navigation, search
(self.abbr needs to be run after self:annonval)
(overhaul parsing for individual date parameters)
Line 1: Line 1:
local getArgs = require('Module:Arguments').getArgs
+
local lang = mw.language.getContentLanguage()
  
 
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
Line 8: Line 8:
 
Dts.__index = Dts
 
Dts.__index = Dts
  
Dts.monthsSearch = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }
+
Dts.months = {
Dts.monthsAbr = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }
+
"January",
Dts.months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }
+
"February",
 +
"March",
 +
"April",
 +
"May",
 +
"June",
 +
"July",
 +
"August",
 +
"September",
 +
"October",
 +
"November",
 +
"December"
 +
}
 +
 
 +
Dts.monthsAbbr = {
 +
"Jan",
 +
"Feb",
 +
"Mar",
 +
"Apr",
 +
"May",
 +
"Jun",
 +
"Jul",
 +
"Aug",
 +
"Sep",
 +
"Oct",
 +
"Nov",
 +
"Dec"
 +
}
  
 
function Dts.new(args)
 
function Dts.new(args)
Line 16: Line 42:
 
self.format = args.format or "mdy"
 
self.format = args.format or "mdy"
 
self.abbr = false -- Default
 
self.abbr = false -- Default
 +
 +
if args[2] or args[3] or args[4] then
 +
-- YMD parameters are specified individually.
 +
if args[1] then
 +
self.year = tonumber(args[1])
 +
if not self.year then
 +
error(string.format(
 +
"'%s' is not a valid year",
 +
tostring(args[1])
 +
), 3)
 +
end
 +
end
 +
if args[2] then
 +
if tonumber(args[2]) then
 +
self.month = tonumber(args[2])
 +
elseif type(args[2]) == 'string' then
 +
local lower = args[2]:lower()
 +
local function setMonth(months, isAbbr)
 +
for i, month in ipairs(months) do
 +
if month:lower() == lower then
 +
self.month = i
 +
if isAbbr then
 +
self.abbr = true
 +
end
 +
break
 +
end
 +
end
 +
end
 +
setMonth(Dts.months)
 +
if not self.month then
 +
setMonth(Dts.monthsAbbr, true)
 +
end
 +
end
 +
if not self.month then
 +
error(string.format(
 +
"'%s' is not a valid month",
 +
tostring(args[2])
 +
), 3)
 +
end
 +
end
 +
if args[3] then
 +
self.day = tonumber(args[3])
 +
if not self.day then
 +
error(string.format(
 +
"'%s' is not a valid day",
 +
tostring(args[3])
 +
), 3)
 +
end
 +
end
 +
if args[4] then
 +
local bc = type(args[4]) == 'string' and args[4]:lower()
 +
if bc == 'bc' or bc == 'bce' then
 +
if self.year and self.year > 0 then
 +
self.year = -self.year
 +
end
 +
elseif bc ~= 'ad' or bc ~= 'ce' then
 +
error(string.format(
 +
"'%s' is not a valid era code (expected 'BC', 'BCE', 'AD' or 'CE')",
 +
tostring(args[4])
 +
), 3)
 +
end
 +
end
 +
elseif args[1] then
 +
-- args[1] is the entire date that we need to parse.
 +
else
 +
error('no date parameters detected', 3)
 +
end
 +
 
if args[1] and (not args[2]) then
 
if args[1] and (not args[2]) then
 
for _, val in pairs(mw.text.split(args[1],"[%s/-]")) do
 
for _, val in pairs(mw.text.split(args[1],"[%s/-]")) do
Line 38: Line 132:
 
end
 
end
 
return self
 
return self
 +
end
 +
 +
function Dts.formatDate(format, timestamp)
 +
local success, ret = pcall(lang.formatDate, lang, format, timestamp)
 +
if success then
 +
return ret
 +
end
 
end
 
end
  

Revision as of 18:41, 1 July 2015

Documentation for this module may be created at Module:Dts/doc

local lang = mw.language.getContentLanguage()

--------------------------------------------------------------------------------
-- Dts class
--------------------------------------------------------------------------------

local Dts = {}
Dts.__index = Dts

Dts.months = {
	"January",
	"February",
	"March",
	"April",
	"May",
	"June",
	"July",
	"August",
	"September",
	"October",
	"November",
	"December"
}

Dts.monthsAbbr = {
	"Jan",
	"Feb",
	"Mar",
	"Apr",
	"May",
	"Jun",
	"Jul",
	"Aug",
	"Sep",
	"Oct",
	"Nov",
	"Dec"
}

function Dts.new(args)
	local self = setmetatable({}, Dts)
	self.format = args.format or "mdy"
	self.abbr = false -- Default

	if args[2] or args[3] or args[4] then
		-- YMD parameters are specified individually.
		if args[1] then
			self.year = tonumber(args[1])
			if not self.year then
				error(string.format(
					"'%s' is not a valid year",
					tostring(args[1])
				), 3)
			end
		end
		if args[2] then
			if tonumber(args[2]) then
				self.month = tonumber(args[2])
			elseif type(args[2]) == 'string' then
				local lower = args[2]:lower()
				local function setMonth(months, isAbbr)
					for i, month in ipairs(months) do
						if month:lower() == lower then
							self.month = i
							if isAbbr then
								self.abbr = true
							end
							break
						end
					end
				end
				setMonth(Dts.months)
				if not self.month then
					setMonth(Dts.monthsAbbr, true)
				end
			end
			if not self.month then
				error(string.format(
					"'%s' is not a valid month",
					tostring(args[2])
				), 3)
			end
		end
		if args[3] then
			self.day = tonumber(args[3])
			if not self.day then
				error(string.format(
					"'%s' is not a valid day",
					tostring(args[3])
				), 3)
			end
		end
		if args[4] then
			local bc = type(args[4]) == 'string' and args[4]:lower()
			if bc == 'bc' or bc == 'bce' then
				if self.year and self.year > 0 then
					self.year = -self.year
				end
			elseif bc ~= 'ad' or bc ~= 'ce' then
				error(string.format(
					"'%s' is not a valid era code (expected 'BC', 'BCE', 'AD' or 'CE')",
					tostring(args[4])
				), 3)
			end
		end
	elseif args[1] then
		-- args[1] is the entire date that we need to parse.
	else
		error('no date parameters detected', 3)
	end

	if args[1] and (not args[2]) then
		for _, val in pairs(mw.text.split(args[1],"[%s/-]")) do
			self:annonval(val, true)
		end
	else
		for key, val in pairs(args) do
			if tonumber(key) then
				self:annonval(val)
			end
		end
	end
	if args.abbr then
		if args.abbr == "on" then
			self.abbr=true
		else
			self.abbr=false
		end
	end
	if (self.year==0) then --not valid. placeholder for no-year
		self.year = nil
	end
	return self
end

function Dts.formatDate(format, timestamp)
	local success, ret = pcall(lang.formatDate, lang, format, timestamp)
	if success then
		return ret
	end
end

function Dts:setmonth(raw)
	if not raw then
		self.month = nil
		return false
	end
	local numbermonth = tonumber(raw)
	if numbermonth and numbermonth > 0 and numbermonth < 13 then
		self.month = numbermonth
		return true
	end
	for i, mon in pairs(self.monthsSearch) do
		if string.find(string.lower(raw),mon) then
			self.month=i
			if string.find(string.lower(raw),string.lower(self.months[i])) then
				self.abbr=false
			else
				self.abbr=true
			end
			return true
		end
	end
	return false
end

function Dts:annonval(val, dayfirst)
	local numberval
	if val then
		numberval = tonumber(mw.text.trim(val,"%s%t,"))
	end
	if (not val) or (type(val)=="table") or (mw.text.trim(val)=="") then
		numberval = 0
	end
	if not numberval then
		if mw.text.trim(string.lower(val)) == "bc" then
			if (not self.year) then
				self.year = self.day
				self.day = nil
			end
			if self.year then
				self.year = 0 - self.year
			end
		else
			if self:setmonth(val,dayfirst) and dayfirst and self.year and (not self.day) and (self.year > 0) and (self.year<31) then
				self.day = self.year
				self.year = nil
				self.format = "dmy"
			end
		end
		return
	end
	if self.month and (not self.day) and (numberval < 32) and (numberval > 0) then
		self.day = numberval
		return
	end
	if self.year and (not self.month) then
		self.month = numberval
		return
	end
	if (not self.year) then
		self.year = numberval
		return
	end
end

function Dts:monthName()
	if (not self.month) or (self.month < 0) or (self.month > 12) then
		return ""
	end
	if self.abbr then
		return self.monthsAbr[self.month]
	else
		return self.months[self.month]
	end
end

function Dts:makeSortKey()
	local year = self.year or os.date("*t").year
	year = year > 0 and year or -10000 - year
	return string.format(
		"%05d-%02d-%02d-%02d%02d",
		year,
		self.month or 1,
		self.day or 1,
		0,
		0
	)
end

function Dts:makeDisplay()
	local ret = {}
	if self.day then
		if self.format == "mdy" then
			ret[#ret + 1] = self:monthName()
			ret[#ret + 1] = ' '
			ret[#ret + 1] = self.day
			if self.year then
				ret[#ret + 1] = ','
			end
		else
			ret[#ret + 1] = self.day
			ret[#ret + 1] = ' '
			ret[#ret + 1] = self:monthName()
		end
	elseif self.month then
		ret[#ret + 1] = self:monthName()
	end
	if self.year then
		if self.month then
			ret[#ret + 1] = ' '
		end
		ret[#ret + 1] = math.abs(self.year)
		if self.year < 0 then
			ret[#ret + 1] = '&nbsp;BC'
		end
	end
	return table.concat(ret)
end

function Dts:__tostring()
	local root = mw.html.create()
	root
		:tag('span')
			:addClass('sortkey')
			:css('display', 'none')
			:css('speak', 'none')
			:wikitext(self:makeSortKey())
			:done()
		:tag('span')
			:css('white-space', 'nowrap')
			:wikitext(self:makeDisplay())
	return tostring(root)
end

--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------

local p = {}

function p._main(args)
	local dts = Dts.new(args)
	return tostring(dts)
end

function p.main(frame)
	local args = getArgs(frame, {
		wrappers = 'Template:Dts',
		removeBlanks = false
	})
	return p._main(args)
end

return p