Difference between revisions of "Module:Dts"
From Timelines
(overhaul parsing for individual date parameters) |
(try using formatDate for parsing) |
||
Line 37: | Line 37: | ||
"Dec" | "Dec" | ||
} | } | ||
+ | |||
+ | function Dts._makeMonthSearch(t) | ||
+ | local ret = {} | ||
+ | for i, month in ipairs(t) do | ||
+ | ret[month:lower()] = i | ||
+ | end | ||
+ | return ret | ||
+ | end | ||
+ | Dts.monthSearch = Dts._makeMonthSearch(Dts.months) | ||
+ | Dts.monthSearchAbbr = Dts._makeMonthSearch(Dts.monthsAbbr) | ||
function Dts.new(args) | function Dts.new(args) | ||
Line 59: | Line 69: | ||
elseif type(args[2]) == 'string' then | elseif type(args[2]) == 'string' then | ||
local lower = args[2]:lower() | local lower = args[2]:lower() | ||
− | + | self.month = Dts.monthSearch[lower] | |
− | + | if not self.month then | |
− | + | self.month = Dts.monthSearchAbbr[lower] | |
− | + | if self.month then | |
− | + | self.abbr = true | |
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | |||
− | |||
− | |||
− | |||
end | end | ||
end | end | ||
Line 106: | Line 108: | ||
elseif args[1] then | elseif args[1] then | ||
-- args[1] is the entire date that we need to parse. | -- args[1] is the entire date that we need to parse. | ||
+ | args[1] = tostring(args[1]) | ||
+ | local date = Dts.formatDate('Y-m-d', args[1]) | ||
+ | if date then | ||
+ | self.year, self.month, self.day = date:match('^(%d%d%d%d)%-(%d%d)%-(%d%d)$') | ||
+ | self.year = tonumber(self.year) | ||
+ | self.month = tonumber(self.month) | ||
+ | self.day = tonumber(self.day) | ||
+ | |||
+ | -- Try to detect whether the values have been normalised, e.g. the | ||
+ | -- user specified "February 2012" but formatDate added the day for | ||
+ | -- us, making 2012-02-01. | ||
+ | local function stringHasNumber(s, num) | ||
+ | num = tostring(num) | ||
+ | return s:find('%D0*' .. num .. '%D') or | ||
+ | s:find('^0*' .. num .. '%D') or | ||
+ | s:find('%D0*' .. num .. '$') or | ||
+ | s:find('^0*' .. num .. '$') | ||
+ | end | ||
+ | local currentYear = os.date('*t').year | ||
+ | if self.year == currentYear and not stringHasNumber(args[1], currentYear) then | ||
+ | self.year = nil | ||
+ | end | ||
+ | if self.month == 1 and | ||
+ | not args[1]:lower():find('jan') and | ||
+ | not stringHasNumber(args[1], 1) | ||
+ | then | ||
+ | self.month = nil | ||
+ | end | ||
+ | if self.day == 1 and not stringHasNumber(args[1], 1) then | ||
+ | self.day = nil | ||
+ | end | ||
+ | else | ||
+ | error(string.format( | ||
+ | "'%s' is an invalid date", | ||
+ | tostring(args[1]) | ||
+ | ), 3) | ||
+ | end | ||
else | else | ||
error('no date parameters detected', 3) | error('no date parameters detected', 3) | ||
end | end | ||
− | if args | + | -- Whether to output abbreviated month names |
− | + | if args.abbr then | |
− | + | self.abbr = args.abbr == 'on' | |
− | |||
else | else | ||
− | + | self.abbr = self.abbr or false | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
+ | |||
if (self.year==0) then --not valid. placeholder for no-year | if (self.year==0) then --not valid. placeholder for no-year | ||
self.year = nil | self.year = nil |
Revision as of 19:32, 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._makeMonthSearch(t) local ret = {} for i, month in ipairs(t) do ret[month:lower()] = i end return ret end Dts.monthSearch = Dts._makeMonthSearch(Dts.months) Dts.monthSearchAbbr = Dts._makeMonthSearch(Dts.monthsAbbr) 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() self.month = Dts.monthSearch[lower] if not self.month then self.month = Dts.monthSearchAbbr[lower] if self.month then self.abbr = true end 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. args[1] = tostring(args[1]) local date = Dts.formatDate('Y-m-d', args[1]) if date then self.year, self.month, self.day = date:match('^(%d%d%d%d)%-(%d%d)%-(%d%d)$') self.year = tonumber(self.year) self.month = tonumber(self.month) self.day = tonumber(self.day) -- Try to detect whether the values have been normalised, e.g. the -- user specified "February 2012" but formatDate added the day for -- us, making 2012-02-01. local function stringHasNumber(s, num) num = tostring(num) return s:find('%D0*' .. num .. '%D') or s:find('^0*' .. num .. '%D') or s:find('%D0*' .. num .. '$') or s:find('^0*' .. num .. '$') end local currentYear = os.date('*t').year if self.year == currentYear and not stringHasNumber(args[1], currentYear) then self.year = nil end if self.month == 1 and not args[1]:lower():find('jan') and not stringHasNumber(args[1], 1) then self.month = nil end if self.day == 1 and not stringHasNumber(args[1], 1) then self.day = nil end else error(string.format( "'%s' is an invalid date", tostring(args[1]) ), 3) end else error('no date parameters detected', 3) end -- Whether to output abbreviated month names if args.abbr then self.abbr = args.abbr == 'on' else self.abbr = self.abbr or false 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] = ' 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