Modulis:Year in other calendars
Wikimedia module / From Wikipedia, the free encyclopedia
-- Load dependencies. local getArgs = require('Module:Arguments').getArgs local htmlBuilder = require( 'Module:HtmlBuilder' ) local makeNavbar = require( 'Module:Navbar' )._navbar local numToRoman = require( 'Module:Roman' ).main local numToArmenian = require( 'Module:Armenian' ).main
-- Define constants. local lang = mw.language.getContentLanguage() local currentYear = tonumber( lang:formatDate( 'Y' ) )
-- Helper functions
local function isInteger( num ) -- Checks if a value is an integer. If so, returns the value converted to a number. -- If not, returns false.
num = tonumber( num ) if num and math.floor( num ) == num and num ~= math.huge then return num else return false end
end
local function BCToNum( s ) -- Converts strings of the format "n BC" to their corresponding -- numerical values.
if type( s ) ~= 'string' then return nil end s = mw.ustring.match( mw.ustring.upper( s ), '^([1-9]%d*)%s*BC$' ) if not s then return nil end local num = tonumber( s ) num = ( num - 1 ) * -1 return num
end
local function numToBC( num ) -- For BC years, returns a string with the year name appended with " BC". -- Otherwise returns nil.
num = isInteger( num ) if not num then return end if num <= 0 then return mw.ustring.format( '%d. gads p.m.ē.', 1 - num ) end
end
local function formatNegative(s) -- Replaces hyphens in a string with minus signs if the hyphen comes before a number. s = mw.ustring.gsub( s, '%-(%d)', '−%1' ) return s end
-- Calendar box class definition
local calendarBox = {} calendarBox.__index = calendarBox
function calendarBox:new( init )
init = type( init ) == 'table' and init or {} local obj = {} local pagename = mw.title.getCurrentTitle().text -- Set the year. If the year is specified as an argument, use that. -- Otherwise, use the page name if it is valid. If the pagename isn't -- valid, use the current year. local yearNum = isInteger( init.year ) if yearNum then -- First, see if the year parameter is a number. self.year = yearNum else local yearBC = BCToNum( init.year ) if yearBC then -- Second, see if the year parameter is a "yyyy BC" string. self.year = yearBC else local pageNum = isInteger( pagename ) if pageNum then -- Third, see if the pagename is an integer. self.year = pageNum else local pageBC = BCToNum( pagename ) if pageBC then -- Fourth, see if the pagename is a "yyyy BC" string. self.year = pageBC else self.year = currentYear -- If none of the above apply, use the current year. end end end end -- Set year text values. self.BCYearName = numToBC( self.year ) if self.BCYearName then self.yearText = self.BCYearName else self.yearText = tostring( self.year ) end -- Set other fields. self.caption = self.yearText self.footnotes = init.footnotes self.navbar = init.navbar return setmetatable( obj, { __index = self })
end
function calendarBox:setCaption( s ) -- Sets the calendar box caption.
if type( s ) ~= 'string' or s == then return end self.caption = s
end
function calendarBox:addCalendar( obj ) -- Adds a calendar or a calendar group.
if type( obj ) ~= 'table' and type( obj.new ) ~= 'function' then return end -- Exit if the object is invalid. self.calendars = self.calendars or {} table.insert( self.calendars, obj )
end
-- Add an alias for adding calendar groups. The function is the same, but it might be confusing for users -- to have to use the name "addCalendar" for a calendar group. calendarBox.addCalendarGroup = calendarBox.addCalendar
function calendarBox:export() -- Outputs the calendar box wikitext.
local root = htmlBuilder.create( 'table' ) -- Export the calendar box headers. root .addClass( 'infobox' ) .addClass( 'vevent' ) .css( 'width', '18%' ) .tag( 'caption' ) .css( 'font-size', '115%' ) .tag( 'span' ) .css( 'font-weight', 'bold' ) .addClass( 'summary' ) .addClass( 'dtstart' ) .wikitext( self.caption )
-- Export the calendars and calendar groups. "calendar:export()" works for both kinds -- of objects. Some export functions can return nil, so we need to check for that. if type( self.calendars ) == 'table' then for i, calendar in ipairs( self.calendars ) do local calendarText = calendar:export() if type( calendarText ) == 'string' then root.wikitext( calendarText ) end end end -- Add footnotes. if type( self.footnotes ) == 'string' and self.footnotes ~= then root .tag( 'tr' ) .tag( 'td' ) .attr( 'colspan', '2' ) .wikitext( mw.ustring.format( '%s', self.footnotes ) ) end -- Add navbar. if type( self.navbar ) == 'string' and self.navbar ~= then root .tag( 'tr' ) .tag( 'td' ) .attr( 'colspan', '2' ) .css( 'text-align', 'center' ) .wikitext( makeNavbar{ self.navbar } ) end return tostring( root )
end
-- Calendar group class definition
-- Calendar groups are used to group different calendars together. -- Previously, the template did this by including a table row with -- no year value. By using objects we can do the same thing more -- semantically.
local calendarGroup = {} calendarGroup.__index = calendarGroup
function calendarGroup:new( init )
init = type( init ) == 'table' and init or {} local obj = {} -- Get the heading and throw an error if it is invalid. obj.heading = init.heading if type( obj.heading ) ~= 'string' then error( 'calendarGroup: no heading detected' ) end -- Set the metatable and return the object. self.__index = self return setmetatable( obj, { __index = self })
end
function calendarGroup:addCalendar( calendar ) -- Adds a calendar object to the calendar group.
self.calendars = self.calendars or {} if type( calendar ) == 'table' and type( calendar.getLink ) == 'function' then table.insert( self.calendars, calendar ) end
end
function calendarGroup:export() -- Exports the calendar group's wikitext.
-- Indent and italicise each calendar's link if it exists.
for i, calendar in ipairs( self.calendars ) do
local link = calendar:getLink()
if type( link ) == 'string' then
self.calendars[ i ]:setRawLink( mw.ustring.format( " - %s", link ) )
end
end
-- Create the heading row html and export the calendar objects.
local ret = htmlBuilder.create()
ret
.tag( 'tr' )
.tag( 'td' )
.wikitext( self.heading )
.done()
.tag( 'td' ) -- Use a blank tag to make the html look nice.
.allDone()
for i, calendar in ipairs( self.calendars ) do
ret.wikitext( calendar:export() )
end
return tostring( ret )
end
-- Calendar class definition
local calendar = {} calendar.__index = calendar calendar.type = 'calendar'
function calendar:new()
local obj = {} return setmetatable( obj, { __index = self })
end
function calendar:setLink( link, display ) -- Sets the calendar's wikilink, with optional display text and italics.
if type( link ) ~= 'string' or link == then return end display = type( display ) == 'string' and display ~= and display if display then self.link = mw.ustring.format( '%s', link, display ) else self.link = mw.ustring.format( '%s', link ) end
end
function calendar:setRawLink( s ) -- Sets the calendar's wikilink as raw wikitext.
if type( s ) ~= 'string' or s == then return end self.link = s
end
function calendar:getLink() -- Returns the calendar's link value.
return self.link
end
function calendar:setYear( year ) -- Sets a single year. Can be passed either a string or a number. -- If passed as a number, it is formatted with minus signs instead of hyphens. -- If passed as a string, no minus-sign formatting occurs; this should be done in the individual calendar definitions.
if type( year ) == 'number' then year = tostring( year )
self.year = formatNegative( year )
elseif type( year ) == 'string' then self.year = year end
end
function calendar:setYearRange( year1, year2 ) -- Sets a year range. Must be passed two numbers.
if type( year1 ) == 'number' and type( year2 ) == 'number' then
local year
if year1 < 0 or year2 < 0 then -- Leave a gap for negative years to avoid having a minus sign and a dash right next to each other. year = mw.ustring.format( '%d — %d', year1, year2 )
year = formatNegative( year )
else year = mw.ustring.format( '%d—%d', year1, year2 ) end
self.year = year
end
end
function calendar:export() -- Outputs the calendar wikitext.
-- Exit if no link has been specified. local link = self.link if type( link ) ~= 'string' or link == then return end
-- If no year has been specified, set the year value to N/A. local year = self.year if type( year ) ~= 'string' or year == then year = "N/A" end
-- Build the table row. local ret = htmlBuilder.create() ret .tag( 'tr' ) .tag( 'td' ) .wikitext( link ) .done() .tag( 'td' ) .wikitext( year ) .allDone() return tostring( ret )
end
-- Build the box
local function makeCalendarBox( args )
-- Initiate the box and get the year values. local init = args init.navbar = 'Gads citos kalendāros' local box = calendarBox:new( init ) local year = box.year local yearText = box.yearText
-- Set the caption. box:setCaption( box.caption .. '. gads citos kalendāros' )
---------------------------------------------------------------------- -- Gregorian calendar ----------------------------------------------------------------------
local gregorian = calendar:new() gregorian:setLink( 'Gregora kalendārs' ) -- Get the year link. local gregcal = args.gregcal if type( gregcal ) == 'string' and gregcal ~= then gregorian.yearLink = mw.ustring.format( '%s', gregcal, yearText ) else gregorian.yearLink = yearText end -- Set the year. gregorian.romanYear = numToRoman{ year } if gregorian.romanYear then gregorian:setYear( mw.ustring.format( [[%s
%s]], gregorian.yearLink, gregorian.romanYear ) ) else gregorian:setYear( gregorian.yearLink ) end box:addCalendar( gregorian )
---------------------------------------------------------------------- -- Ab urbe condita ---------------------------------------------------------------------- local abUrbe = calendar:new() abUrbe:setLink( 'Ab urbe condita' ) abUrbe:setYear( year + 753 ) box:addCalendar( abUrbe )
---------------------------------------------------------------------- -- Armenian calendar ---------------------------------------------------------------------- local armenian = calendar:new() armenian:setLink( 'Armēņu kalendārs' ) if year > 551 then local armenianYear = year - 551 armenian:setYear( mw.ustring.format( '%s
ԹՎ %s', armenianYear, numToArmenian( armenianYear ) ) ) end box:addCalendar( armenian )
---------------------------------------------------------------------- -- Assyrian calendar ----------------------------------------------------------------------
local assyrian = calendar:new() assyrian:setLink( 'Asīriešu kalendārs' ) assyrian:setYear( year + 4750 ) box:addCalendar( assyrian ) ---------------------------------------------------------------------- -- Bahá'í calendar ---------------------------------------------------------------------- local bahai = calendar:new() bahai:setLink( "Bahāju kalendārs" ) bahai:setYearRange( year - 1844, year - 1843 ) box:addCalendar( bahai )
---------------------------------------------------------------------- -- Bengali calendar ----------------------------------------------------------------------
local bengali = calendar:new() bengali:setLink( 'Bengāliešu kalendārs' ) bengali:setYear( year - 593 ) box:addCalendar( bengali )
---------------------------------------------------------------------- -- Berber calendar ---------------------------------------------------------------------- local berber = calendar:new() berber:setLink( 'Berberu kalendārs' ) berber:setYear( year + 950 ) box:addCalendar( berber )
---------------------------------------------------------------------- -- Buddhist calendar ----------------------------------------------------------------------
local buddhist = calendar:new() buddhist:setLink( 'Budistu kalendārs' ) buddhist:setYear( year + 544 ) box:addCalendar( buddhist )
---------------------------------------------------------------------- -- Burmese calendar ----------------------------------------------------------------------
local burmese = calendar:new() burmese:setLink( 'Tradicionālais birmiešu kalendārs', 'Birmiešu kalendārs' ) burmese:setYear( year - 638 ) box:addCalendar( burmese )
---------------------------------------------------------------------- -- Byzantine calendar ----------------------------------------------------------------------
local byzantine = calendar:new() byzantine:setLink( 'Bizantiešu kalendārs' ) byzantine:setYearRange( year + 5508, year + 5509 ) box:addCalendar( byzantine )
---------------------------------------------------------------------- -- Chinese calendar ---------------------------------------------------------------------- local chinese = calendar:new() chinese:setLink( 'Ķīniešu kalendārs' )
-- Define the information for the "heavenly stems" and "earthly branches" year cycles.
-- See Chinese calendar#Cycle of years for information.
local heavenlyStems = { { '甲', 'Koka' }, -- 1 { '乙', 'Koka' }, -- 2 { '丙', 'Uguns' }, -- 3 { '丁', 'Uguns' }, -- 4 { '戊', 'Zemes' }, -- 5 { '己', 'Zemes' }, -- 6 { '庚', 'Metāla' }, -- 7 { '辛', 'Metāla' }, -- 8 { '壬', 'Ūdens' }, -- 9 { '癸', 'Ūdens' } -- 10 }
local earthlyBranches = { { '子', 'žurka' }, -- 1 { '丑', 'vērsis' }, -- 2 { '寅', 'tīģeris' }, -- 3 { '卯', 'trusis' }, -- 4 { '辰', 'drakons' }, -- 5 { '巳', 'čūska' }, -- 6 { '午', 'zirgs' }, -- 7 { '未', 'kaza' }, -- 8 { '申', 'pērtiķis' }, -- 9 { '酉', 'gailis' }, -- 10 { '戌', 'suns' }, -- 11 { '亥', 'cūka' } -- 12 }
-- Calculate the cycle numbers from the year. The first sexagenary year corresponds to the previous year's entry
-- in Chinese calendar correspondence table, as the Chinese New Year doesn't happen until Jan/Feb in
-- Gregorian years.
local sexagenaryYear1 = ( year - 4 ) % 60
local sexagenaryYear2 = ( year - 3 ) % 60
local heavenlyNum1 = sexagenaryYear1 % 10
local heavenlyNum2 = sexagenaryYear2 % 10
local earthlyNum1 = sexagenaryYear1 % 12
local earthlyNum2 = sexagenaryYear2 % 12
-- If the value is 0 increase it by one cycle so that we can use it with Lua arrays. if heavenlyNum1 == 0 then heavenlyNum1 = 10 end if heavenlyNum2 == 0 then heavenlyNum2 = 10 end if earthlyNum1 == 0 then earthlyNum1 = 12 end if earthlyNum2 == 0 then earthlyNum2 = 12 end
-- Get the data tables for each permutation. local heavenlyTable1 = heavenlyStems[ heavenlyNum1 ] local heavenlyTable2 = heavenlyStems[ heavenlyNum2 ] local earthlyTable1 = earthlyBranches[ earthlyNum1 ] local earthlyTable2 = earthlyBranches[ earthlyNum2 ]
-- Work out the continously-numbered year. (See Chinese calendar#Continuously numbered years.)
local year1 = year + 2696
local year2 = year + 2697
local year1Alt = year1 - 60
local year2Alt = year2 - 60
-- Format any negative numbers. year1 = formatNegative( tostring( year1 ) ) year2 = formatNegative( tostring( year2 ) ) year1Alt = formatNegative( tostring( year1Alt ) ) year2Alt = formatNegative( tostring( year2Alt ) )
-- Return all of that data in a (hopefully) reader-friendly format. chinese:setYear( mw.ustring.format( [=[%s%s年 (%s %s)
%s vai %s
— līdz —
%s%s年 (%s %s)
%s vai %s]=], heavenlyTable1[ 1 ], earthlyTable1[ 1 ], heavenlyTable1[ 2 ], earthlyTable1[ 2 ], year1, year1Alt, heavenlyTable2[ 1 ], earthlyTable2[ 1 ], heavenlyTable2[ 2 ], earthlyTable2[ 2 ], year2, year2Alt ) )
box:addCalendar( chinese ) ---------------------------------------------------------------------- -- Coptic calendar ----------------------------------------------------------------------
local coptic = calendar:new() coptic:setLink( 'Koptu kalendārs' ) coptic:setYearRange( year - 284, year - 283 ) box:addCalendar( coptic ) ---------------------------------------------------------------------- -- Ethiopian calendar ----------------------------------------------------------------------
local ethiopian = calendar:new() ethiopian:setLink( 'Etiopu kalendārs' ) ethiopian:setYearRange( year - 8, year - 7 ) box:addCalendar( ethiopian )
---------------------------------------------------------------------- -- Hebrew calendar ----------------------------------------------------------------------
local hebrew = calendar:new() hebrew:setLink( 'Ebreju kalendārs' ) hebrew:setYearRange( year + 3760, year + 3761 ) box:addCalendar( hebrew )
---------------------------------------------------------------------- -- Hindu calendars ----------------------------------------------------------------------
local hindu = calendarGroup:new{ heading = 'Hindu kalendāri' }
-- Vikram Samvat
local vikramSamvat = calendar:new() vikramSamvat:setLink( 'Vikram Samvat' ) vikramSamvat:setYearRange( year + 56, year + 57 ) hindu:addCalendar( vikramSamvat )
-- Shaka Samvat
local shakaSamvat = calendar:new() shakaSamvat:setLink( 'Indijas nacionālais kalendārs', 'Shaka Samvat' ) if year - 76 > 0 then shakaSamvat:setYearRange( year - 78, year - 77 ) end hindu:addCalendar( shakaSamvat )
-- Kali Yuga local kaliYuga = calendar:new() kaliYuga:setLink( 'Kali Juga' ) -- use italics kaliYuga:setYearRange( year + 3101, year + 3102 ) hindu:addCalendar( kaliYuga )
box:addCalendarGroup( hindu )
---------------------------------------------------------------------- -- Holocene calendar ----------------------------------------------------------------------
local holocene = calendar:new() holocene:setLink( 'Holocēna kalendārs' ) holocene:setYear( year + 10000 ) box:addCalendar( holocene )
---------------------------------------------------------------------- -- Igbo calendar ---------------------------------------------------------------------- -- In the old template this was a calendar group with just one calendar; intentionally adding this as a single -- calendar here, as the previous behaviour looked like a mistake. local igbo = calendar:new() igbo:setLink( 'Igbo kalendārs' ) igbo:setYearRange( year - 1000, year - 999 ) box:addCalendar( igbo )
---------------------------------------------------------------------- -- Iranian calendar ----------------------------------------------------------------------
local iranian = calendar:new()
iranian:setLink( 'Irānas kalendārs' )
if year - 621 > 0 then
iranian:setYearRange( year - 622, year - 621 )
else
iranian:setYear( mw.ustring.format( '%d BP – %d BP', 622 - year, 621 - year ) )
end
box:addCalendar( iranian )
---------------------------------------------------------------------- -- Islamic calendar ----------------------------------------------------------------------
local islamic = calendar:new()
islamic:setLink( 'Islāma kalendārs' )
local islamicMult = 1.030684 -- the factor to multiply by
local islamicSub = 621.5643 -- the factor to subtract by
if year - 621 > 0 then
local year1 = math.floor( islamicMult * ( year - islamicSub ) )
local year2 = math.floor( islamicMult * ( year - islamicSub + 1 ) )
islamic:setYearRange( year1, year2 )
else
local year1 = math.ceil( -islamicMult * ( year - islamicSub ) )
local year2 = math.ceil( -islamicMult * ( year - islamicSub + 1 ) )
islamic:setYear( mw.ustring.format( '%d BH – %d BH', year1, year2 ) )
end
box:addCalendar( islamic )
---------------------------------------------------------------------- -- Julian calendar ----------------------------------------------------------------------
local julian = calendar:new() julian:setLink( 'Jūlija kalendārs' )
julian.yearVals = { { 1901, 'Gregora kalendārs mīnus 13 dienas' }, { 1900, 'Gregora kalendārs mīnus 12 vai 13 dienas'}, { 1801, 'Gregora kalendārs mīnus 12 dienas' }, { 1800, 'Gregora kalendārs mīnus 11 vai 12 dienas' }, { 1701, 'Gregora kalendārs mīnus 11 dienas' }, { 1700, 'Gregora kalendārs mīnus 10 vai 11 dienas' }, { 1582, 'Gregora kalendārs mīnus 10 dienas' }, { -45, gregorian.year } }
for i, t in ipairs( julian.yearVals ) do if year >= t[ 1 ] then julian:setYear( t[ 2 ] ) break end end
box:addCalendar( julian ) ---------------------------------------------------------------------- -- Korean calendar ----------------------------------------------------------------------
local korean = calendar:new() korean:setLink( 'Korejas kalendārs' ) korean:setYear( year + 2333 ) box:addCalendar( korean ) ---------------------------------------------------------------------- -- Unix time ---------------------------------------------------------------------- local unix = calendar:new()
local function getUnixTime( year ) if year < 1970 then return end local noError, unixTime = pcall( lang.formatDate, lang, 'U', '1 Jan ' .. tostring( year ) ) if not noError or noError and not unixTime then return end unixTime = tonumber( unixTime ) if unixTime and unixTime >= 0 then return unixTime end end
unix.thisYear = getUnixTime( year ) unix.nextYear = getUnixTime( year + 1 ) if unix.thisYear and unix.nextYear then unix:setLink( 'UNIX laiks' ) unix:setYearRange( unix.thisYear, unix.nextYear - 1 ) end
box:addCalendar( unix )
return box:export()
end
-- Process arguments from #invoke
local p = {}
function p.main( frame ) -- Process the arguments and pass them to the box-building function. local args = getArgs( frame )
return makeCalendarBox( args )
end
return p