Jump to content
Wikipedia The Free Encyclopedia

Module:Date table sorting

From Wikipedia, the free encyclopedia
Module documentation[view] [edit] [history] [purge]
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.

 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]='&nbsp;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

AltStyle によって変換されたページ (->オリジナル) /