وحدة:Year in other calendars

2024 في التقاويم الأخرى
التقويم الگريگوري2024
MMXXIV
آب أوربه كونديتا2777
التقويم الأرمني1473
ԹՎ ՌՆՀԳ
التقويم الآشوري6774
التقويم البهائي180–181
التقويم البنغالي1431
التقويم الأمازيغي2974
سنة العهد البريطاني72 إليز. 2 – 73 إليز. 2
التقويم البوذي2568
التقويم البورمي1386
التقويم البيزنطي7532–7533
التقويم الصيني癸卯(الماء الأرنب)
4720 أو 4660
    — إلى —
甲辰年 (الخشب التنين)
4721 أو 4661
التقويم القبطي1740–1741
التقويم الديسكوردي3190
التقويم الإثيوپي2016–2017
التقويم العبري5784–5785
التقاويم الهندوسية
 - ڤيكرام سامڤات2080–2081
 - شاكا سامڤات1946–1947
 - كالي يوگا5125–5126
تقويم الهولوسين12024
تقويم الإگبو1024–1025
التقويم الإيراني1402–1403
التقويم الهجري1445–1446
التقويم اليابانيHeisei 36
(平成36年)
تقويم جوچى113
التقويم اليوليوسيالگريگوري ناقص 13 يوم
التقويم الكوري4357
تقويم مينگووجمهورية الصين 113
民國113年
التقويم الشمسي التايلندي2567
توقيت يونيكس1704067200–1735689599


This module produces a sidebar displaying a given Gregorian year in various different calendars.

Syntax

Simple
{{#invoke:Year in other calendars|main}}
All parameters
{{#invoke:Year in other calendars|main|year=n|footnotes=footnotes|gregcal=article name}}

Parameters

  • year - a number specifying the year to be displayed. This can be negative, and can also be in the format "n BC" to display BC dates. If not specified, the current year is used.
  • footnotes - footnotes to be placed at the bottom of the sidebar.
  • gregcal - an article to be displayed for Gregorian and Julian years. Only specify the article name, e.g. "2000 BC". Do not include square brackets.

Microformat

The HTML mark up produced by this template includes an hCalendar microformat, which makes the event/ date details parsable by computers, either acting automatically to catalogue article across Wikipedia, or via a browser tool operated by a person, to (for example) add the subject to a calendar or diary application. For more information about the use of microformats on Wikipedia, please see the microformat project.

hCalendar uses HTML classes including:

  • description
  • dtend
  • dtstart
  • location
  • summary
  • vevent

Please do not rename or remove these classes; nor collapse nested elements which use them.

Adding new calendars

The module is set up to allow for easy addition of new calendars. Just scroll down to the "Build the box" section of the module code, and add your calendar as follows:

To display one year:

    local myCalendar = calendar:new()
    myCalendar:setLink( 'My calendar article' ) -- The name of the calendar's Wikipedia article.
    myCalendar:setYear( year + 10 ) -- Lua code linking the Gregorian calendar year to your calendar's year.
    box:addCalendar( myCalendar )

To display a year range:

    local myCalendar = calendar:new()
    myCalendar:setLink( 'My calendar article' ) -- The name of the calendar's Wikipedia article.
    myCalendar:setYearRange( year + 10, year + 11 ) -- Lua code outputting the start year and the end year of the year range.
    box:addCalendar( myCalendar )

More complicated calendars can be passed as a string to calendar:setYear().

Technical details

The module defines three classes which do the work of setting up the sidebar and displaying the data provided by the calendar functions. These are the calendarBox class, which defines the sidebar; the calendar class, which holds the data for one calendar; and the calendarGroup object, which defines a group of calendar objects with a heading.

To load these classes from another module, use the following:

local yearInOtherCalendars = require( 'Module:Year in other calendars' )
local calendarBox = yearInOtherCalendars.calendarBox
local calendarGroup = yearInOtherCalendars.calendarGroup
local calendar = yearInOtherCalendars.calendar

calendarBox class

A calendarBox object is initiated with:

local myCalendarBox = calendarBox:new{ year = yyyy, footnotes = footnotes, navbar = page name }
  • year - sets the Gregorian year to base calendar calculations on. If not specified, the current year is used.
  • footnotes - sets text to be displayed in a footnotes section at the bottom of the sidebar.
  • navbar - sets the page name to be used by the navbar.

Calendar box objects have the following properties:

  • calendarBox.year - the Gregorian year number. This is negative for BC years; for example, for the year 100 BC the value of calendarBox.year is -99. (BC years are calculated by "1 - n" rather than "0 - n", as there is no year zero.)
  • calendarBox.yearText - the Gregorian year text. This is a string value of the format "n" for AD years and "n BC" for BC years.
  • calendarBox.caption - the text of the box caption (the bold text that appears directly above the box). The default caption is the value of calendarBox.yearText.
  • calendarBox.footnotes - the text of the box footnotes.
  • calendarBox.navbar - the page name used by the navbar.

Calendar box objects have the following methods:

  • calendarBox:setCaption( caption ) - sets the box caption (the bold text that appears directly above the box). The default caption is the value of calendarBox.yearText.
  • calendarBox:addCalendar( obj ) - adds a calendar object or a calendar group object to the calendar box.
  • calendarBox:addCalendarGroup( obj ) - an alias for myCalendarBox:addCalendar().
  • calendarBox:export() - converts the calendar box object to wikicode. This calls calendar:export() and calendarGroup:export() to export calendar objects and calendar group objects.

calendar class

A calendar object is initiated with:

local myCalendar = calendar:new()

Calendar objects have the following properties:

  • calendar.link - the link name.
  • calendar.year - the year value. This is always a string value.

Calendar objects have the following methods:

  • calendar:setLink( link, display ) - sets the link name for the calendar object. link is the name of Wikipedia's article about the calendar, and display is an optional display name for the article link.
  • calendar:setRawLink( wikitext ) - sets the calendar link as raw wikitext.
  • calendar:getLink() - gets the link value.
  • calendar:setYear( year ) - sets the year value for the calendar. year can be a number or a string.
  • calendar:setYearRange( startYear, endYear ) - sets the year value for the calendar as a year range. Both startYear and endYear must be number values.
  • calendar:export() - exports the calendar to wikitext. If no link value was found, this returns nil. If a link was found but no year value was found, the calendar is output with a value of N/A for the year.

calendarGroup class

A calendarGroup object is initiated with:

local myCalendarGroup = calendarGroup:new{ heading = heading }
  • heading - the wikitext heading for the calendar group (e.g. [[Hindu calendar]]s).

Calendar group objects have one property:

  • calendarGroup.heading - the calendar group heading text.

Calendar group objects have the following methods:

  • calendarGroup:addCalendar( obj ) - adds a calendar object to the calendar group.
  • calendarGroup:export() - converts a calendar group to wikitext. Calls calendar:export() to export individual calendar objects.

See also

{{Year in other calendars}}


-- Load dependencies.
local getArgs = require('Module:Arguments').getArgs
local numToRoman = require( 'Module:Roman' ).main
local getOlympiad = require( 'Module:Ancient Olympiads' )._main
local getDynasty = require( 'Module:Ancient Egypt era' )._main
local getPharaoh = require( 'Module:Ancient Egypt kings' )._main
local numToArmenian = require( 'Module:Armenian' ).main
local getRegnal = require( 'Module:British regnal year' ).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 )
	-- Converts strings of the format "n ق.م." 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*ق.م.$' )
	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 " ق.م.".
	-- Otherwise returns nil.
	num = isInteger( num )
	if not num then return end
	if num <= 0 then
		return string.format( '%d ق.م.', 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 ق.م." 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 ق.م." 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 = mw.html.create( 'table' )
	-- Export the calendar box headers.
	root
	:addClass( 'infobox vevent' )
	:css( 'width', '19em' )
	:tag( 'caption' )
	:css( 'font-size', '125%' )
	:tag( 'span' )
	:addClass( 'summary 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 _, 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( string.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( require('Module:Navbar')._navbar{ 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( string.format( "&nbsp;- ''%s''", link ) )
		end
	end
	-- Create the heading row html and export the calendar objects.
	local ret = mw.html.create()
	ret
	:tag( 'tr' )
	:tag( 'td' )
	:wikitext( self.heading )
	:done()
	:tag( 'td' ) -- Use a blank tag to make the html look nice.
	:allDone()
	for _, 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 = string.format( '[[%s|%s]]', link, display )
	else
		self.link = string.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 = string.format( '%d – %d', year1, year2 )
			year = formatNegative( year )
		else
			year = string.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 = mw.html.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 = 'Year in other calendars'
    local box = calendarBox:new( init )
    local year = box.year
    local yearText = box.yearText

    -- Set the caption.
    box:setCaption( box.caption .. ' في التقاويم الأخرى' )

    ----------------------------------------------------------------------
    -- Gregorian calendar
    ----------------------------------------------------------------------

    local gregorian = calendar:new()
    gregorian:setLink( 'التقويم الگريگوري' )
    -- Get the year link.
    local gregcal = args.gregcal
    if type( gregcal ) == 'string' and gregcal ~= '' then
        gregorian.yearLink = mw.ustring.format( '[[%s|%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<br /><span style="font-family: serif;">''%s''</span>]],
            gregorian.yearLink, gregorian.romanYear
        ) )
    else
        gregorian:setYear( gregorian.yearLink )
    end
    box:addCalendar( gregorian ) 

    ----------------------------------------------------------------------
    -- Ab urbe condita
    ----------------------------------------------------------------------
    
    local abUrbe = calendar:new()
    abUrbe:setLink( 'آب أوربه كونديتا' )
    abUrbe:setYear( year + 753 )
    box:addCalendar( abUrbe )

    ----------------------------------------------------------------------
    -- Armenian calendar
    ----------------------------------------------------------------------
    
    local armenian = calendar:new()
    armenian:setLink( 'التقويم الأرمني' )
    if year > 551 then
        local armenianYear = year - 551
        armenian:setYear( mw.ustring.format( '%s<br />ԹՎ %s', armenianYear, numToArmenian( armenianYear ) ) )
    end
    box:addCalendar( armenian )

    ----------------------------------------------------------------------
    -- Assyrian calendar
    ----------------------------------------------------------------------

    local assyrian = calendar:new()
    assyrian:setLink( 'التقويم الآشوري' )
    assyrian:setYear( year + 4750 )
    box:addCalendar( assyrian )
    
    ----------------------------------------------------------------------
    -- Bahá'í calendar
    ----------------------------------------------------------------------
    
    local bahai = calendar:new()
    bahai:setLink( "التقويم البهائي" )
    bahai:setYearRange( year - 1844, year - 1843 )
    box:addCalendar( bahai )

    ----------------------------------------------------------------------
    -- Bengali calendar
    ----------------------------------------------------------------------

    local bengali = calendar:new()
    bengali:setLink( 'التقويم البنغالي' )
    bengali:setYear( year - 593 )
    box:addCalendar( bengali )

    ----------------------------------------------------------------------
    -- Berber calendar
    ----------------------------------------------------------------------
    
    local berber = calendar:new()
    berber:setLink( 'التقويم الأمازيغي' )
    berber:setYear( year + 950 )
    box:addCalendar( berber )

    ----------------------------------------------------------------------
    -- Regnal year
    ----------------------------------------------------------------------

    local regnal = calendar:new()
    local regnalName
    if year > 1706 then
        regnalName = 'البريطاني'
    else
        regnalName = 'الإنگليزي'
    end
    regnal:setLink( ' سنوات عهود الملوك الإنگليز',  'سنة العهد ' .. regnalName  )
    regnal:setYear( getRegnal( year ) )
    box:addCalendar( regnal )

    ----------------------------------------------------------------------
    -- Buddhist calendar
    ----------------------------------------------------------------------

    local buddhist = calendar:new()
    buddhist:setLink( 'التقويم البوذي' )
    buddhist:setYear( year + 544 )
    box:addCalendar( buddhist )

    ----------------------------------------------------------------------
    -- Burmese calendar
    ----------------------------------------------------------------------

    local burmese = calendar:new()
    burmese:setLink( 'التقويم البورمي' )
    burmese:setYear( year - 638 )
    box:addCalendar( burmese )

    ----------------------------------------------------------------------
    -- Byzantine calendar
    ----------------------------------------------------------------------

    local byzantine = calendar:new()
    byzantine:setLink( 'التقويم البيزنطي' )
    byzantine:setYearRange( year + 5508, year + 5509 )
    box:addCalendar( byzantine )

    ----------------------------------------------------------------------
    -- Chinese calendar
    ----------------------------------------------------------------------
    
    local chinese = calendar:new()
    chinese:setLink( 'التقويم الصيني' )

    -- Define the information for the "heavenly stems" and "earthly branches" year cycles.
    -- انظر [[التقويم الصيني#دورة الأعوام]] لمعلومات.

    local heavenlyStems = {
        { '甲', 'الخشب' },   -- 1
        { '乙', 'الخشب' },   -- 2
        { '丙', 'النار' },   -- 3
        { '丁', 'النار' },   -- 4
        { '戊', 'التراب' },  -- 5
        { '己', 'التراب' },  -- 6
        { '庚', 'المعدن' },  -- 7
        { '辛', 'المعدن' },  -- 8
        { '壬', 'الماء' },  -- 9
        { '癸', 'الماء' }   -- 10
    }

    local earthlyBranches = {
        { '子', '[[Rat (zodiac)|الفأر]]' },           -- 1
        { '丑', '[[Ox (zodiac)|الثور]]' },             -- 2
        { '寅', '[[Tiger (zodiac)|النمر]]' },       -- 3
        { '卯', '[[Rabbit (zodiac)|الأرنب]]' },     -- 4
        { '辰', '[[Dragon (zodiac)|التنين]]' },     -- 5
        { '巳', '[[Snake (zodiac)|الثعبان]]' },       -- 6
        { '午', '[[Horse (zodiac)|الحصان]]' },       -- 7
        { '未', '[[Goat (zodiac)|الماعز]]' },         -- 8
        { '申', '[[Monkey (zodiac)|القرد]]' },     -- 9
        { '酉', '[[Rooster (zodiac)|الديك]]' },   -- 10
        { '戌', '[[Dog (zodiac)|الكلب]]' },           -- 11
        { '亥', '[[Pig (zodiac)|الخنزير]]' }            -- 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. (انظر [[التقويم الصيني#الأعوام المرقمة بتواصل]].)
    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(
        [=[[[Sexagenary cycle|%s%s]]年 <small>(%s&nbsp;%s)</small><br />%s أو %s<br />&nbsp;&nbsp;&nbsp;&nbsp;''—&nbsp;إلى&nbsp;—''<br />%s%s年 <small>(%s&nbsp;%s)</small><br />%s أو %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( 'التقويم القبطي' )
    coptic:setYearRange( year - 284, year - 283 )
    box:addCalendar( coptic )
    
    ----------------------------------------------------------------------
    -- Discordian calendar
    ----------------------------------------------------------------------
    
    local discordian = calendar:new()
    discordian:setLink( 'التقويم الديسكوردي' )
    discordian:setYear( year + 1166 )
    box:addCalendar( discordian )

    ----------------------------------------------------------------------
    -- Ethiopian calendar
    ----------------------------------------------------------------------

    local ethiopian = calendar:new()
    ethiopian:setLink( 'التقويم الإثيوپي' )
    ethiopian:setYearRange( year - 8, year - 7 )
    box:addCalendar( ethiopian )

    ----------------------------------------------------------------------
    -- Hebrew calendar
    ----------------------------------------------------------------------

    local hebrew = calendar:new()
    hebrew:setLink( 'التقويم العبري' )
    hebrew:setYearRange( year + 3760, year + 3761 )
    box:addCalendar( hebrew )

    ----------------------------------------------------------------------
    -- Hindu calendars
    ----------------------------------------------------------------------

    local hindu = calendarGroup:new{ heading = '[[تقويم هندوسي|التقاويم الهندوسية]]' }

    -- Vikram Samvat

    local vikramSamvat = calendar:new()
    vikramSamvat:setLink( 'ڤيكرام سامڤات' )
    vikramSamvat:setYearRange( year + 56, year + 57 )
    hindu:addCalendar( vikramSamvat )

    -- Shaka Samvat

    local shakaSamvat = calendar:new()
    shakaSamvat:setLink( 'التقويم الوطني الهندي', 'شاكا سامڤات' )
    if year - 76 > 0 then
        shakaSamvat:setYearRange( year - 78, year - 77 )
    end
    hindu:addCalendar( shakaSamvat )

    -- Kali Yuga
    
    local kaliYuga = calendar:new()
    kaliYuga:setLink( 'كالي يوگا' ) -- use italics
    kaliYuga:setYearRange( year + 3101, year + 3102 )
    hindu:addCalendar( kaliYuga )

    box:addCalendarGroup( hindu )

    ----------------------------------------------------------------------
    -- Holocene calendar
    ----------------------------------------------------------------------

    local holocene = calendar:new()
    holocene:setLink( 'تقويم الهولوسين' )
    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:setYearRange( year - 1000, year - 999 )
    box:addCalendar( igbo )

    ----------------------------------------------------------------------
    -- Iranian calendar
    ----------------------------------------------------------------------

    local iranian = calendar:new()
    iranian:setLink( 'التقاويم الإيرانية', 'التقويم الإيراني' )
    if year - 621 > 0 then
        iranian:setYearRange( year - 622, year - 621 )
    else
        iranian:setYear( mw.ustring.format( '%d ق.ر.&nbsp;– %d ق.ر.', 622 - year, 621 - year ) )
    end
    box:addCalendar( iranian )

    ----------------------------------------------------------------------
    -- Islamic calendar
    ----------------------------------------------------------------------

    local islamic = calendar:new()
    islamic:setLink( 'التقويم الهجري' )
    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 ق.هـ.&nbsp;– %d ق.هـ.', year1, year2 ) )
    end
    box:addCalendar( islamic )

    ----------------------------------------------------------------------
    -- Japanese calendar
    ----------------------------------------------------------------------

    local japanese = calendar:new()
    japanese:setLink( 'التقويم الياباني' )

    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

    box:addCalendar( japanese )

    ----------------------------------------------------------------------
    -- Juche calendar
    ----------------------------------------------------------------------

    local juche = calendar:new()
    juche:setLink( 'تقويم كوريا الشمالية', 'تقويم جوچى' )
    if year > 1911 then
        juche:setYear( year - 1911 )
    end
    box:addCalendar( juche )
    
    ----------------------------------------------------------------------
    -- Julian calendar
    ----------------------------------------------------------------------

    local julian = calendar:new()
    julian:setLink( 'التقويم اليوليوسي' )

    julian.yearVals = {
        { 1901, 'الگريگوري ناقص 13 يوم' },
        { 1900, 'الگريگوري ناقص 12 or 13 يوم'},
        { 1801, 'الگريگوري ناقص 12 يوم' },
        { 1800, 'الگريگوري ناقص 11 أو 12 يوم' },
        { 1701, 'الگريگوري ناقص 11 يوم' },
        { 1700, 'الگريگوري ناقص 10 أو 11 يوم' },
        { 1582, 'الگريگوري ناقص 10 يوم' },
        { -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( 'التقويم الكوري' )
    korean:setYear( year + 2333 )
    box:addCalendar( korean )
    
    ----------------------------------------------------------------------
    -- Minguo calendar
    ----------------------------------------------------------------------

    local minguo = calendar:new()
    minguo:setLink( 'تقويم مينگوو' )
    if year > 1911 then
        local minguoYear = year - 1911
        minguo:setYear( mw.ustring.format( '[[تايوان|جمهورية الصين]] %d<br /><small>民國%d年</small>', minguoYear, minguoYear ) )
    else
        local minguoYear = 1911 - year + 1
        minguo:setYear( mw.ustring.format( '%d قبل [[جمهورية الصين]]<br /><small>民前%d年</small>', minguoYear, minguoYear ) )
    end
    box:addCalendar( minguo )
    
    ----------------------------------------------------------------------
    -- Thai solar calendar
    ----------------------------------------------------------------------

    local thai = calendar:new()
    thai:setLink( 'التقويم الشمسي التايلندي' )
    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: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