Module:Date table sorting
- Anarâškielâ
- العربية
- Արեւմտահայերէն
- অসমীয়া
- Azərbaycanca
- Basa Bali
- বাংলা
- 閩南語 / Bân-lâm-gí
- Беларуская (тарашкевіца)
- भोजपुरी
- Bosanski
- Català
- Dagbanli
- Dansk
- الدارجة
- فارسی
- Føroyskt
- ગુજરાતી
- 한국어
- Հայերեն
- हिन्दी
- Hrvatski
- Ilokano
- Bahasa Indonesia
- Íslenska
- עברית
- ಕನ್ನಡ
- ქართული
- Kurdî
- Ladin
- Lietuvių
- Magyar
- मैथिली
- Македонски
- മലയാളം
- मराठी
- Bahasa Melayu
- မြန်မာဘာသာ
- नेपाली
- 日本語
- Нохчийн
- Oʻzbekcha / ўзбекча
- ਪੰਜਾਬੀ
- Português
- Română
- Русский
- Scots
- Shqip
- සිංහල
- سنڌي
- Slovenščina
- کوردی
- Српски / srpski
- Suomi
- தமிழ்
- တႆး
- తెలుగు
- ไทย
- Türkçe
- اردو
- Tiếng Việt
- 粵語
- 中文
- Kadazandusun
Appearance
From Wikipedia, the free encyclopedia
Warning This Lua module is used on approximately 46,000 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them.
This module implements {{Date table sorting }}. Please see the template page for documentation.
The above documentation is transcluded from Module:Date table sorting/doc. (edit | history)
Editors can experiment in this module's sandbox (edit | diff) and testcases (edit | run) pages.
Subpages of this module.
Editors can experiment in this module's sandbox (edit | diff) and testcases (edit | run) pages.
Subpages of this module.
localyesno=require('Module:Yesno') locallang=mw.language.getContentLanguage() localN_YEAR_DIGITS=12 localMAX_YEAR=10^N_YEAR_DIGITS-1 -------------------------------------------------------------------------------- -- Dts class -------------------------------------------------------------------------------- localDts={} 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" } functionDts._makeMonthSearch(t) localret={} fori,monthinipairs(t)do ret[month:lower()]=i end returnret end Dts.monthSearch=Dts._makeMonthSearch(Dts.months) Dts.monthSearchAbbr=Dts._makeMonthSearch(Dts.monthsAbbr) Dts.monthSearchAbbr['sept']=9-- Allow "Sept" to match September Dts.formats={ dmy=true, mdy=true, dm=true, md=true, my=true, y=true, m=true, d=true, hide=true } functionDts.new(args) localself=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. ifargs[2]orargs[3]orargs[4]then self:parseDateParts(args[1],args[2],args[3],args[4]) elseifargs[1]then self:parseDate(args[1]) end -- Raise an error on invalid values ifself.yearthen ifself.year==0then error('years cannot be zero',0) elseifself.year<-MAX_YEARthen error(string.format( 'years cannot be less than %s', lang:formatNum(-MAX_YEAR) ),0) elseifself.year>MAX_YEARthen error(string.format( 'years cannot be greater than %s', lang:formatNum(MAX_YEAR) ),0) elseifmath.floor(self.year)~=self.yearthen error('years must be an integer',0) end end ifself.monthand( self.month<1 orself.month>12 ormath.floor(self.month)~=self.month )then error('months must be an integer between 1 and 12',0) end ifself.dayand( self.day<1 orself.day>31 ormath.floor(self.day)~=self.day )then error('days must be an integer between 1 and 31',0) end -- Set month abbreviation behaviour, i.e. whether we are outputting -- "January" or "Jan". ifargs.abbrthen self.isAbbreviated=args.abbr=='on'oryesno(args.abbr)orfalse else self.isAbbreviated=self.isAbbreviatedorfalse end -- Set the format string ifargs.formatthen self.format=args.format else self.format=self.formator'mdy' end ifnotDts.formats[self.format]then error(string.format( "'%s' is not a valid 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. ifargs.addkeythen self.addkey=tonumber(args.addkey) ifnotself.addkeyor self.addkey<0or self.addkey>9999or math.floor(self.addkey)~=self.addkey then error("the 'addkey' parameter must be an integer between 0 and 9999",0) end end -- Set whether the displayed date is allowed to wrap or not. self.isWrapping=args.nowrap=='off'oryesno(args.nowrap)==false returnself end functionDts:hasDate() return(self.yearorself.monthorself.day)~=nil end -- Find the month number for a month name, and set the isAbbreviated flag as -- appropriate. functionDts:parseMonthName(s) s=s:lower() localmonth=Dts.monthSearch[s] ifmonththen returnmonth else month=Dts.monthSearchAbbr[s] ifmonththen self.isAbbreviated=true returnmonth end end returnnil end -- Parses separate parameters for year, month, day, and era. functionDts:parseDateParts(year,month,day,bc) ifyearthen self.year=tonumber(year) ifnotself.yearthen error(string.format( "'%s' is not a valid year", tostring(year) ),0) end end ifmonththen iftonumber(month)then self.month=tonumber(month) elseiftype(month)=='string'then self.month=self:parseMonthName(month) end ifnotself.monththen error(string.format( "'%s' is not a valid month", tostring(month) ),0) end end ifdaythen self.day=tonumber(day) ifnotself.daythen error(string.format( "'%s' is not a valid day", tostring(day) ),0) end end ifbcthen localbcLower=type(bc)=='string'andbc:lower() ifbcLower=='bc'orbcLower=='bce'then ifself.yearandself.year>0then self.year=-self.year end elseifbcLower~='ad'andbcLower~='ce'then error(string.format( "'%s' is not a valid era code (expected 'BC', 'BCE', 'AD' or 'CE')", 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. functionDts:parseDate(date) -- Generic error message. localfunctiondateError() error(string.format( "'%s' is an invalid date", date ),0) end localfunctionparseDayOrMonth(s) ifs:find('^%d%d?$')then returntonumber(s) end end localfunctionparseYear(s) ifs:find('^%d%d%d%d?$')then returntonumber(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) ifself.yearthen return end -- Split the string using non-word characters as boundaries. date=tostring(date) localparts=mw.text.split(date,'%W+') localnParts=#parts ifparts[1]==''orparts[nParts]==''ornParts>3then -- 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() elseifnParts<1then -- If we have less than one element, then something has gone horribly -- wrong. error(string.format( "an unknown error occurred while parsing the date '%s'", date ),0) end ifnParts==1then -- This can be either a month name or a year. self.month=self:parseMonthName(parts[1]) ifnotself.monththen self.year=parseYear(parts[1]) ifnotself.yearthen dateError() end end elseifnParts==2then -- This can be any of the following formats: -- DD Month -- Month DD -- Month YYYY -- YYYY-MM self.month=self:parseMonthName(parts[1]) ifself.monththen -- This is either Month DD or Month YYYY. self.year=parseYear(parts[2]) ifnotself.yearthen -- This is Month DD. self.format='mdy' self.day=parseDayOrMonth(parts[2]) ifnotself.daythen dateError() end end else self.month=self:parseMonthName(parts[2]) ifself.monththen -- This is DD Month. self.format='dmy' self.day=parseDayOrMonth(parts[1]) ifnotself.daythen dateError() end else -- This is YYYY-MM. self.year=parseYear(parts[1]) self.month=parseDayOrMonth(parts[2]) ifnotself.yearornotself.monththen dateError() end end end elseifnParts==3then -- 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]) ifself.monththen -- This is Month DD, YYYY. self.format='mdy' self.day=parseDayOrMonth(parts[2]) self.year=parseYear(parts[3]) ifnotself.dayornotself.yearthen dateError() end else self.day=parseDayOrMonth(parts[1]) ifself.daythen self.month=self:parseMonthName(parts[2]) ifself.monththen -- This is DD Month YYYY. self.format='dmy' self.year=parseYear(parts[3]) ifnotself.yearthen dateError() end else -- This is DD-MM-YYYY. self.format='dmy' self.month=parseDayOrMonth(parts[2]) self.year=parseYear(parts[3]) ifnotself.monthornotself.yearthen dateError() end end else -- This is YYYY-MM-DD self.year=parseYear(parts[1]) self.month=parseDayOrMonth(parts[2]) self.day=parseDayOrMonth(parts[3]) ifnotself.yearornotself.monthornotself.daythen dateError() end end end end end functionDts:makeSortKey() localyear,month,day localnYearDigits=N_YEAR_DIGITS ifself:hasDate()then year=self.yearoros.date("*t").year ifyear<0then year=-MAX_YEAR-1-year nYearDigits=nYearDigits+1-- For the minus sign end month=self.monthor1 day=self.dayor1 else -- Blank {{dts}} transclusions should sort last. year=MAX_YEAR month=99 day=99 end returnstring.format( '%0'..nYearDigits..'d-%02d-%02d-%04d', year,month,day,self.addkeyor0 ) end functionDts:getMonthName() ifnotself.monththen return'' end ifself.isAbbreviatedthen returnself.monthsAbbr[self.month] else returnself.months[self.month] end end functionDts:makeDisplay() ifself.format=='hide'then return'' end localhasYear=self.yearandself.format:find('y') localhasMonth=self.monthandself.format:find('m') localhasDay=self.dayandself.format:find('d') localisMonthFirst=self.format:find('md') localret={} ifhasDayandhasMonthandisMonthFirstthen ret[#ret+1]=self:getMonthName() ret[#ret+1]=' ' ret[#ret+1]=self.day ifhasYearthen ret[#ret+1]=',' end elseifhasDayandhasMonththen ret[#ret+1]=self.day ret[#ret+1]=' ' ret[#ret+1]=self:getMonthName() elseifhasDaythen ret[#ret+1]=self.day elseifhasMonththen ret[#ret+1]=self:getMonthName() end ifhasYearthen ifhasDayorhasMonththen ret[#ret+1]=' ' end localdisplayYear=math.abs(self.year) ifdisplayYear>9999then displayYear=lang:formatNum(displayYear) else displayYear=tostring(displayYear) end ret[#ret+1]=displayYear ifself.year<0then ret[#ret+1]=' BC' end end returntable.concat(ret) end functionDts:__tostring() localroot=mw.html.create() localspan=root:tag('span') :attr('data-sort-value',self:makeSortKey()) -- Display ifself:hasDate()andself.format~='hide'then span:wikitext(self:makeDisplay()) ifnotself.isWrappingthen span:css('white-space','nowrap') end end returntostring(root) end -------------------------------------------------------------------------------- -- Exports -------------------------------------------------------------------------------- localp={} functionp._exportClasses() return{ Dts=Dts } end functionp._main(args) localsuccess,ret=pcall(function() localdts=Dts.new(args) returntostring(dts) end) ifsuccessthen returnret else ret=string.format( '<strong class="error">Error in [[Template:Date table sorting]]: %s</strong>', ret ) ifmw.title.getCurrentTitle().namespace==0then -- Only categorise in the main namespace ret=ret..'[[Category:Date table sorting templates with errors]]' end returnret end end functionp.main(frame) localargs=require('Module:Arguments').getArgs(frame,{ wrappers='Template:Date table sorting', }) returnp._main(args) end returnp