跳至內容
主選單
主選單
移至側邊欄
隱藏
導覽
首頁
最近修改
新手使用指南
隨機頁面
貢獻分數
建立新頁面
工具
上傳檔案
特殊頁面
搜尋
搜尋
建立賬號
登入
個人工具
暗色模式
建立賬號
登入
用於已登出編輯者的頁面
了解更多
貢獻
討論
正在編輯
模組:Date table sorting
模組
討論
English
閱讀
編輯原始碼
檢視歷史
工具
工具
移至側邊欄
隱藏
操作
閱讀
編輯原始碼
檢視歷史
一般
連結至此的頁面
相關變更
特殊頁面
頁面資訊
取得短網址
警告:
您尚未登入。 若您進行任何的編輯您的 IP 位址將會被公開。 若您
登入
或
建立帳號
,您的編輯將會以您的使用者名稱標示,並能擁有另外的益處。
防垃圾訊息檢查用。
請勿
填寫此欄位!
local yesno = require('Module:Yesno') local lang = mw.language.getContentLanguage() local N_YEAR_DIGITS = 12 local MAX_YEAR = 10^N_YEAR_DIGITS - 1 -------------------------------------------------------------------------------- -- Dts class -------------------------------------------------------------------------------- local Dts = {} function Dts.GetErrMsgFromOther (frame, args) local msg = '' local args = {} if frame == mw.getCurrentFrame() then msg = frame.args[1] or frame.args['1'] or '' args[1] = frame.args[2] or frame.args['2'] or '' args[2] = frame.args[3] or frame.args['3'] or '' args[3] = frame.args[4] or frame.args['4'] or '' args['cat'] = 0 else return Dts.errmsg (frame, args) end return Dts.errmsg (msg, args) end function Dts.errmsg (msg, args) msg = msg or '' args = args or {} local needcat = args['cat'] or 1 local function arg (x) if args[x] then return args[x] end return '' end local msglist = { ['error'] = '<strong class="error">[[Module:Date table sorting]]錯誤:%s</strong>', ['valid-err'] = '解析日期格式「%s」時出現未知錯誤。', ['valid-year'] = '給出的年份「%s」年不合理。', ['valid-month'] = '給出的月份「%s」月不合理。', ['valid-day'] = '給出的日「%s」日不合理。', ['valid-bc'] = '給出的西元前後判斷值「%s」不合理(僅能使用「BC」, 「BCE」, 「AD」 或「CE」)。', ['valid-date'] = '給出的日期「%s」不合理。', ['valid-args-addkey'] = '參數<code>addkey</code>的值「%s」不合理。', ['year-zero'] = '年份不得為零。', ['year-min'] = '給出的年份「%s年」低於最小值%s年。', ['year-max'] = '給出的年份「%s年」超出最大值%s年。', ['year-integer'] = '給出的年份「%s年」不是整數。', ['unknown-month'] = '月份只有1月到12月,沒有「第%s月」!', ['unknown-day-31'] = '%s月只有31天,沒有「第%s天」!', ['unknown-day-30'] = '%s月只有30天,沒有「第%s天」!', ['unknown-day-Feb'] = '%s年2月只有%s天,沒有「第%s天」!', ['unknown-format'] = '無法識別格式「%s」。', ['args-addkey'] = '參數<code>addkey</code>的值應介於0到9999之間。' } return string.format((msglist[msg] or ''), (args[1] or ''), (args[2] or ''), (args[3] or ''), (args[4] or '')) end 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) Dts.monthSearchAbbr['sept'] = 9 -- Allow "Sept" to match September Dts.formats = { ymd = true, dmy = true, mdy = true, ym = true, dm = true, md = true, my = true, y = true, m = true, d = true, hide = true } function Dts.new(args) local self = setmetatable({}, Dts) -- Parse date parameters. -- In this step we also record whether the date was in DMY or YMD format, -- and whether the month name was abbreviated. if args[2] or args[3] or args[4] then self:parseDateParts(args[1], args[2], args[3], args[4]) elseif args[1] then self:parseDate(args[1]) end -- Raise an error on invalid values if self.year then if self.year == 0 then error(self.errmsg('year-zero') , 0) elseif self.year < -MAX_YEAR then error(self.errmsg('year-min', {self.year, lang:formatNum(-MAX_YEAR)}), 0) elseif self.year > MAX_YEAR then error(self.errmsg('year-max', {self.year, lang:formatNum(MAX_YEAR)}), 0) elseif math.floor(self.year) ~= self.year then error(self.errmsg('year-integer', {self.year}), 0) end end if self.month and ( self.month < 1 or self.month > 12 or math.floor(self.month) ~= self.month ) then error(self.errmsg('unknown-month', {self.month}), 0) end if self.day then if ( (self.month == 1) or (self.month == 3) or (self.month == 5) or (self.month == 7) or (self.month == 8) or (self.month == 10) or (self.month == 12) ) and (self.day > 31) then error(self.errmsg('unknown-day-31', {self.month, self.day}), 0) elseif ( (self.month == 4) or (self.month == 6) or (self.month == 9) or (self.month == 11) ) and (self.day > 30) then error(self.errmsg('unknown-day-30', {self.month, self.day}), 0) elseif (self.month == 2) and (self.day > 29) and (self.year % 400 == 0 or self.year % 4 == 0 and self.year % 100 ~= 0) then error(self.errmsg('unknown-day-Feb', {self.year, 29, self.day}), 0) elseif (self.month == 2) and (self.day > 28) and not (self.year % 400 == 0 or self.year % 4 == 0 and self.year % 100 ~= 0) then error(self.errmsg('unknown-day-Feb', {self.year, 28, self.day}), 0) end end --]=] -- Set debug mode if args.debug then self.isdebug = args.debug end -- Set the format string if args.format then self.format = args.format else self.format = self.format or 'ymd' end if not Dts.formats[self.format] then error(self.errmsg('unknown-format', {tostring(self.format)}), 0) end -- Set addkey. This adds a value at the end of the sort key, allowing users -- to manually distinguish between identical dates. if args.addkey then self.addkey = tonumber(args.addkey) if not self.addkey or math.floor(self.addkey) ~= self.addkey then error(self.errmsg('valid-args-addkey', {args.addkey}), 0) elseif self.addkey < 0 or self.addkey > 9999 then error(self.errmsg('args-addkey'), 0) end end -- Set whether the displayed date is allowed to wrap or not. self.isWrapping = args.nowrap == 'off' or yesno(args.nowrap) == false -- Set whether the abbreviated or not. self.isAbbreviated = args.abbr == 'on' or yesno(args.abbr) == true -- Check for deprecated parameters. if args.link then self.hasDeprecatedParameters = true end return self end function Dts:hasDate() return (self.year or self.month or self.day) ~= nil end -- Find the month number for a month name, and set the isAbbreviated flag as -- appropriate. function Dts:parseMonthName(s) s = s:lower() local month = Dts.monthSearch[s] if month then return month else month = Dts.monthSearchAbbr[s] if month then self.isAbbreviated = true return month end end return nil end -- Parses separate parameters for year, month, day, and era. function Dts:parseDateParts(year, month, day, bc) if year then self.year = tonumber(year) if not self.year then error(self.errmsg('valid-year', {tostring(year)}), 0) end end if month then if tonumber(month) then self.month = tonumber(month) elseif type(month) == 'string' then self.month = self:parseMonthName(month) end if not self.month then error(self.errmsg('valid-month', {tostring(month)}), 0) end end if day then self.day = tonumber(day) if not self.day then error(self.errmsg('valid-day', {tostring(day)})) end end if bc then local bcLower = type(bc) == 'string' and bc:lower() if bcLower == 'bc' or bcLower == 'bce' then if self.year and self.year > 0 then self.year = -self.year end elseif bcLower ~= 'ad' and bcLower ~= 'ce' then error(self.errmsg('valid-bc', {tostring(bc)}), 0) end end end -- This method parses date strings. This is a poor man's alternative to -- mw.language:formatDate, but it ends up being easier for us to parse the date -- here than to use mw.language:formatDate and then try to figure out after the -- fact whether the month was abbreviated and whether we were DMY or MDY. function Dts:parseDate(date) -- Generic error message. local function dateError() error(self.errmsg('valid-date', {tostring(date)}), 0) end local function parseDayOrMonth(s) if s:find('^%d%d?$') then return tonumber(s) end end local function parseMonth(s) if s:find('^%d%d?$') and tonumber(s) >=1 and tonumber(s) <= 12 then return tonumber(s) end end local function parseDay(s) if self.month then lastday = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} if s:find('^%d%d?$') and tonumber(s) >=1 and tonumber(s) <= lastday[self.month] then return tonumber(s) end end end local function parseYear(s) if s:find('^%d%d?%d?%d?$') then return tonumber(s) end end -- Deal with year-only dates first, as they can have hyphens in, and later -- we need to split the string by all non-word characters, including -- hyphens. Also, we don't need to restrict years to 3 or 4 digits, as on -- their own they can't be confused as a day or a month number. self.year = tonumber(date) if self.year then return end -- Split the string using non-word characters as boundaries. date = tostring(date) local parts = mw.text.split(date, '%W+') local nParts = #parts if parts[1] == '' or parts[nParts] == '' or nParts > 3 then -- We are parsing a maximum of three elements, so raise an error if we -- have more. If the first or last elements were blank, then the start -- or end of the string was a non-word character, which we will also -- treat as an error. dateError() elseif nParts < 1 then -- If we have less than one element, then something has gone horribly -- wrong. error(self.errmsg('valid-err', {tostring(date)}), 0) end if nParts == 1 then -- This can be either a month name or a year. self.month = self:parseMonthName(parts[1]) if not self.month then self.year = parseYear(parts[1]) if not self.year then dateError() end end elseif nParts == 2 then -- This can be any of the following formats: -- DD Month -- Month DD -- Month YYYY -- YYYY-MM -- MM-DD self.month = self:parseMonthName(parts[1]) if self.month then -- This is either Month DD or Month YYYY. self.year = parseYear(parts[2]) if not self.year then -- This is Month DD. self.format = 'mdy' self.day = parseDayOrMonth(parts[2]) if not self.day then dateError() end end else self.month = self:parseMonthName(parts[2]) if self.month then -- This is DD Month. self.format = 'ymd' self.day = parseDayOrMonth(parts[1]) if not self.day then dateError() end else -- This is MM-DD. self.month = parseMonth(parts[1]) if self.month then self.day = parseDay(parts[2]) else -- This is YYYY-MM. self.year = parseYear(parts[1]) self.month = parseMonth(parts[2]) if not (self.year and self.month) then dateError() end end end end elseif nParts == 3 then -- This can be any of the following formats: -- DD Month YYYY -- Month DD, YYYY -- YYYY-MM-DD -- DD-MM-YYYY self.month = self:parseMonthName(parts[1]) if self.month then -- This is Month DD, YYYY. self.format = 'mdy' self.day = parseDayOrMonth(parts[2]) self.year = parseYear(parts[3]) if not self.day or not self.year then dateError() end else self.day = parseDayOrMonth(parts[1]) if self.day then self.month = self:parseMonthName(parts[2]) if self.month then -- This is DD Month YYYY. self.format = 'ymd' self.year = parseYear(parts[3]) if not self.year then dateError() end else -- This is DD-MM-YYYY. self.format = 'ymd' self.month = parseDayOrMonth(parts[2]) self.year = parseYear(parts[3]) if not self.month or not self.year then dateError() end end else -- This is YYYY-MM-DD self.year = parseYear(parts[1]) self.month = parseDayOrMonth(parts[2]) self.day = parseDayOrMonth(parts[3]) if not self.year or not self.month or not self.day then dateError() end end end end end function Dts:makeSortKey() local year, month, day local nYearDigits = N_YEAR_DIGITS if self:hasDate() then year = self.year or os.date("*t").year if year < 0 then year = -MAX_YEAR - 1 - year nYearDigits = nYearDigits + 1 -- For the minus sign end month = self.month or 1 day = self.day or 1 else -- Blank {{dts}} transclusions should sort last. year = MAX_YEAR month = 99 day = 99 end return string.format( '%0' .. nYearDigits .. 'd-%02d-%02d-%04d', year, month, day, self.addkey or 0 ) end function Dts:getMonthName() if not self.month then return '' end if self.isAbbreviated then return self.monthsAbbr[self.month] else return self.months[self.month] end end function Dts:makeDisplay() if self.format == 'hide' then return '' end local hasYear = self.year and self.format:find('y') local hasMonth = self.month and self.format:find('m') local hasDay = self.day and self.format:find('d') local ret = {} if hasYear then if self.year < 0 then ret[#ret + 1] = '公元前' end local displayYear = math.abs(self.year) displayYear = displayYear > 9999 and lang:formatNum(displayYear) or tostring(displayYear) ret[#ret + 1] = displayYear ret[#ret + 1] = '年' end if hasMonth then ret[#ret + 1] = self.month ret[#ret + 1] = '月' end if hasDay then ret[#ret + 1] = self.day ret[#ret + 1] = '日' end return table.concat(ret) end function Dts:makeDisplayAbbr() if self.format == 'hide' then return '' end local hasYear = self.year and self.format:find('y') local hasMonth = self.month and self.format:find('m') local hasDay = self.day and self.format:find('d') local ret = {} if hasYear then if self.year < 0 then ret[#ret + 1] = 'BC' end local displayYear = math.abs(self.year) displayYear = displayYear > 9999 and lang:formatNum(displayYear) or tostring(displayYear) ret[#ret + 1] = displayYear if hasMonth or hasDay then ret[#ret + 1] = '/' end end if hasMonth then ret[#ret + 1] = self.month if hasDay then ret[#ret + 1] = '/' end end if hasDay then ret[#ret + 1] = self.day end return table.concat(ret) end function Dts:makeDisplayAbbrOrNoAbbr() if self.isAbbreviated then return self:makeDisplayAbbr() else return self:makeDisplay() end end function Dts:renderTrackingCategories() if self.hasDeprecatedParameters then return '[[Category:Template:Date_table_sorting錯誤|廢]]' else return '' end end function Dts:__tostring() local root = mw.html.create() -- Sort key if self.isdebug then root:tag('span') :css('border', 'dotted 1px') :wikitext(self:makeSortKey()) else root:tag('span') :addClass('sortkey') :css('display', 'none') :css('speak', 'none') :wikitext(self:makeSortKey()) end -- Display if self:hasDate() then if self.isWrapping then root:wikitext(self:makeDisplayAbbrOrNoAbbr()) else root:tag('span') :css('white-space', 'nowrap') :wikitext(self:makeDisplayAbbrOrNoAbbr()) end end -- Tracking categories root:wikitext(self:renderTrackingCategories()) return tostring(root) end -------------------------------------------------------------------------------- -- Exports -------------------------------------------------------------------------------- local p = {} function p._exportClasses() return { Dts = Dts } end p.errmsg = Dts.GetErrMsgFromOther function p._main(args) --由於技術問題,無法處理中文的年月日,因此將其丟進 Module:Date_Convert 轉成 ISODate 再傳入,就不會錯 -- not tonumber(args[1]) 排掉年分,{{fact|可以解析的2000-01-01}} if args[1] and not tonumber(args[1]) then --iferror預防丟入 Module:Date_Convert 的格式不支援但 Module:Date_table_sorting 支援 args[1] = mw.getCurrentFrame():callParserFunction('#iferror', require('Module:Date_Convert')._converttime(args[1]), args[1]) end --end local success, ret = pcall(function () local dts = Dts.new(args) return tostring(dts) end) if success then return ret else ret = Dts.errmsg('error', {ret}) if mw.title.getCurrentTitle().namespace == 0 then -- Only categorise in the main namespace ret = ret .. '[[Category:Template:Date_table_sorting錯誤]]' end return ret end end function p.main(frame) local args = require('Module:Arguments').getArgs(frame, { wrappers = 'Template:Date table sorting', }) return p._main(args) end return p
摘要:
請注意,所有於合眾百科 Unitedbook所做的貢獻會依據CC BY-NC-SA(創用CC 姓名標示─非商業性─相同方式分享)授權條款發佈(詳情請見
合眾百科:版權
)。若您不希望您的著作被任意修改與散佈,請勿在此發表文章。
您同時向我們保證在此的著作內容是您自行撰寫,或是取自不受版權保護的公開領域或自由資源。
請勿在未經授權的情況下發表文章!
取消
編輯說明
(在新視窗開啟)
此頁面使用了以下模板:
模組:Date table sorting/doc
(
編輯
)
切換限制內容寬度