Modul:Year in other calendars
Dokumentasi untuk modul ini dapat dibuat di Modul:Year in other calendars/doc
-- 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 japaneseEra = require( 'Module:Japanese calendar' ).era() -- 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 ) -- Mengubah string dalam format "n SM" ke -- nilai angka. if type( s ) ~= 'string' then return nil end s = mw.ustring.match( mw.ustring.upper( s ), '^([1-9]%d*)%s*SM$' ) 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 SM', 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', '22em' ) .tag( 'caption' ) .css( 'font-size', '125%' ) .tag( 'span' ) .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( '<small>%s</small>', 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|%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:setYearCouple( year1, year2 ) -- Same as setYearRange, only with a slash (/) in the middle. Must be passed two numbers. -- Additional text possible, must be defined as follows: addtext = string.format( 'additional text or link') -- See example in Seleucid era calendar if type( year1 ) == 'number' and type( year2 ) == 'number' then local year if year1 < 0 or year2 < 0 then -- Leave no gap for negative years. year = string.format( '%d/%d %s', year1, year2, addtext ) year = formatNegative( year ) else year = string.format( '%d/%d %s', year1, year2, addtext ) 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 = 'Tahun dalam kalender lain' local box = calendarBox:new( init ) local year = box.year local yearText = box.yearText -- Set the caption. box:setCaption( box.caption .. ' dalam kalender lain' ) ---------------------------------------------------------------------- -- Gregorian calendar ---------------------------------------------------------------------- local gregorian = calendar:new() gregorian:setLink( 'Kalender Gregorian' ) -- Get the year link. local gregcal = args.gregcal if type( gregcal ) == 'string' and gregcal ~= '' then gregorian.yearLink = string.format( '[[%s|%s]]', gregcal, yearText ) else gregorian.yearLink = yearText end -- Set the year. gregorian.romanYear = numToRoman{ math.abs(year) } .. (year < 0 and ' BC' or '') if gregorian.romanYear then gregorian:setYear( string.format( [[%s<br /><span style="font-family: serif;">''%s''</span>]], gregorian.yearLink, gregorian.romanYear ) ) else gregorian:setYear( gregorian.yearLink ) end box:addCalendar( gregorian ) ---------------------------------------------------------------------- -- Bahá'í calendar ---------------------------------------------------------------------- local bahai = calendar:new() bahai:setLink( "Kalender Bahá'í" ) bahai:setYearRange( year - 1844, year - 1843 ) box:addCalendar( bahai ) ---------------------------------------------------------------------- -- Balinese saka calendar ---------------------------------------------------------------------- local balinese = calendar:new() balinese:setLink( 'Kalender Bali' ) if year - 76 > 0 then balinese:setYearRange( year - 79, year - 78 ) end box:addCalendar( balinese ) ---------------------------------------------------------------------- -- Bengali calendar ---------------------------------------------------------------------- local bengali = calendar:new() bengali:setLink( 'Kalender Bengali' ) bengali:setYear( year - 593 ) box:addCalendar( bengali ) ---------------------------------------------------------------------- -- Berber calendar ---------------------------------------------------------------------- local berber = calendar:new() berber:setLink( 'Kalender Berber' ) berber:setYear( year + 950 ) box:addCalendar( berber ) ---------------------------------------------------------------------- -- Buddhist calendar ---------------------------------------------------------------------- local buddhist = calendar:new() buddhist:setLink( 'Kalender Buddha' ) buddhist:setYear( year + 544 ) box:addCalendar( buddhist ) ---------------------------------------------------------------------- -- Burmese calendar ---------------------------------------------------------------------- local burmese = calendar:new() burmese:setLink( 'Kalender Burma' ) burmese:setYear( year - 638 ) box:addCalendar( burmese ) ---------------------------------------------------------------------- -- Chinese calendar ---------------------------------------------------------------------- local chinese = calendar:new() chinese:setLink( 'Kalender Tionghoa' ) -- Define the information for the "heavenly stems" and "earthly branches" year cycles. -- See [[Chinese calendar#Cycle of years]] for information. local heavenlyStems = { { '甲', '[[Kayu]]' }, -- 1 { '乙', '[[Kayu]]' }, -- 2 { '丙', '[[Api]]' }, -- 3 { '丁', '[[Api]]' }, -- 4 { '戊', '[[Tanah]]' }, -- 5 { '己', '[[Tanah]]' }, -- 6 { '庚', '[[Logam]]' }, -- 7 { '辛', '[[Logam]]' }, -- 8 { '壬', '[[Air]]' }, -- 9 { '癸', '[[Air]]' } -- 10 } local earthlyBranches = { { '子', '[[Tikus (shio)|Tikus]]' }, -- 1 { '丑', '[[Kerbau (shio)|Kerbau]]' }, -- 2 { '寅', '[[Macan (shio)|Macan]]' }, -- 3 { '卯', '[[Kelinci (shio)|Kelinci]]' }, -- 4 { '辰', '[[Naga (shio)|Naga]]' }, -- 5 { '巳', '[[Ular (shio)|Ular]]' }, -- 6 { '午', '[[Kuda (shio)|Kuda]]' }, -- 7 { '未', '[[Kambing (shio)|Kambing]]' }, -- 8 { '申', '[[Kera (shio)|Kera]]' }, -- 9 { '酉', '[[Ayam (shio)|Ayam]]' }, -- 10 { '戌', '[[Anjing (shio)|Anjing]]' }, -- 11 { '亥', '[[Babi (shio)|Babi]]' } -- 12 } -- Menghitung angka siklus suatu tahun. 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. (Lihat [[Chinese calendar#Continuously numbered years]].) local year1 = year + 2696 local year2 = year + 2697 local year1Alt = year1 - 2146 local year2Alt = year2 - 2146 -- 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( [=[[[Ganzhi|%s%s]]年 <small>(%s %s)</small><br />%s atau %s<br /> ''— sampai —''<br />%s%s年 <small>(%s %s)</small><br />%s atau %s]=], heavenlyTable1[ 1 ], earthlyTable1[ 1 ], earthlyTable1[ 2 ], heavenlyTable1[ 2 ], year1, year1Alt, heavenlyTable2[ 1 ], earthlyTable2[ 1 ], earthlyTable2[ 2 ], heavenlyTable2[ 2 ], year2, year2Alt ) ) box:addCalendar( chinese ) ---------------------------------------------------------------------- -- Coptic calendar ---------------------------------------------------------------------- local coptic = calendar:new() coptic:setLink( 'Kalender Koptik' ) coptic:setYearRange( year - 284, year - 283 ) box:addCalendar( coptic ) ---------------------------------------------------------------------- -- Ethiopian calendar ---------------------------------------------------------------------- local ethiopian = calendar:new() ethiopian:setLink( 'Kalender Etiopia' ) ethiopian:setYearRange( year - 8, year - 7 ) box:addCalendar( ethiopian ) ---------------------------------------------------------------------- -- Hebrew calendar ---------------------------------------------------------------------- local hebrew = calendar:new() hebrew:setLink( 'Kalender Ibrani' ) hebrew:setYearRange( year + 3760, year + 3761 ) box:addCalendar( hebrew ) ---------------------------------------------------------------------- -- Hindu calendars ---------------------------------------------------------------------- local hindu = calendarGroup:new{ heading = '[[Kalender Hindu]]' } -- 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( 'Kalender Saka', '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 Yuga' ) -- use italics kaliYuga:setYearRange( year + 3101, year + 3102 ) hindu:addCalendar( kaliYuga ) box:addCalendarGroup( hindu ) ---------------------------------------------------------------------- -- Holocene calendar ---------------------------------------------------------------------- local holocene = calendar:new() holocene:setLink( 'Kalender Holosen' ) holocene:setYear( year + 10000 ) box:addCalendar( holocene ) ---------------------------------------------------------------------- -- Iranian calendar ---------------------------------------------------------------------- local iranian = calendar:new() iranian:setLink( 'Kalender Iran') 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( 'Kalender Islam' ) 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 ) ---------------------------------------------------------------------- -- Japanese calendar ---------------------------------------------------------------------- local japanese = calendar:new() japanese:setLink( 'Kalender Jepang' ) japanese.thisEra = japaneseEra:new{ year = year } if japanese.thisEra then local japaneseYearText = {} japanese.oldEra = japanese.thisEra:getOldEra() if japanese.oldEra and japanese.oldEra.eraYear and japanese.thisEra.article ~= japanese.oldEra.article then japanese.oldText = mw.ustring.format( '%s %d', japanese.oldEra.link, japanese.oldEra.eraYear ) table.insert( japaneseYearText, japanese.oldText ) table.insert( japaneseYearText, ' / ' ) end if japanese.thisEra.eraYear then table.insert( japaneseYearText, mw.ustring.format( '%s %d', japanese.thisEra.link, japanese.thisEra.eraYear ) ) end table.insert( japaneseYearText, mw.ustring.format( '<br /><small>(%s%s年)</small>', japanese.thisEra.kanji, japanese.thisEra.eraYearKanji ) ) japanese:setYear( table.concat( japaneseYearText ) ) end -- Japanese imperial year local imperial = calendar:new() imperial:setLink( 'Tahun Jepang' ) imperial:setYear( year + 660 ) box:addCalendar( japanese ) ---------------------------------------------------------------------- -- Javanese calendar ---------------------------------------------------------------------- local javanese = calendar:new() javanese:setLink( 'Kalender Jawa' ) local javaneseMult = 1.030684 -- the factor to multiply by local javaneseSub = 124.9 -- the factor to subtract by if year - 124 > 0 then local year1 = math.floor( javaneseMult * ( year - javaneseSub ) ) local year2 = math.floor( javaneseMult * ( year - javaneseSub + 1 ) ) javanese:setYearRange( year1, year2 ) else local year1 = math.ceil( -javaneseMult * ( year - javaneseSub ) ) local year2 = math.ceil( -javaneseMult * ( year - javaneseSub + 1 ) ) end box:addCalendar( javanese ) ---------------------------------------------------------------------- -- Julian calendar ---------------------------------------------------------------------- local julian = calendar:new() julian:setLink( 'Kalender Julian' ) julian.yearVals = { { 1901, 'Gregorian dikurangi 13 hari' }, { 1900, 'Gregorian dikurangi 12 atau 13 hari'}, { 1801, 'Gregorian dikurangi 12 hari' }, { 1800, 'Gregorian dikurangi 11 atau 12 hari' }, { 1701, 'Gregorian dikurangi 11 hari' }, { 1700, 'Gregorian dikurangi 10 atau 11 hari' }, { 1582, 'Gregorian dikurangi 10 hari' }, { -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( 'Kalender Korea' ) korean:setYear( year + 2333 ) box:addCalendar( korean ) ---------------------------------------------------------------------- -- Nanakshahi calendar ---------------------------------------------------------------------- local nanakshahi = calendar:new() nanakshahi:setLink( 'Kalender Nanakshahi' ) nanakshahi:setYear( year - 1468 ) box:addCalendar( nanakshahi ) ---------------------------------------------------------------------- -- Thai solar calendar ---------------------------------------------------------------------- local thai = calendar:new() thai:setLink( 'Suriyakhati' ) thai:setYear( year + 543 ) box:addCalendar( thai ) ---------------------------------------------------------------------- -- 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 time' ) 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 ) -- Pass year argument with 'year' parameter or without any name but first argument args.year = args.year or args[1] return makeCalendarBox( args ) end return p