<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://stockhub.co/index.php?action=history&amp;feed=atom&amp;title=Module%3ACurrent_events_monthly_archive</id>
	<title>Module:Current events monthly archive - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://stockhub.co/index.php?action=history&amp;feed=atom&amp;title=Module%3ACurrent_events_monthly_archive"/>
	<link rel="alternate" type="text/html" href="https://stockhub.co/index.php?title=Module:Current_events_monthly_archive&amp;action=history"/>
	<updated>2026-05-24T18:38:26Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://stockhub.co/index.php?title=Module:Current_events_monthly_archive&amp;diff=135881&amp;oldid=prev</id>
		<title>imported&gt;Mr. Stradivarius: detect sandbox pages when parsing the current title</title>
		<link rel="alternate" type="text/html" href="https://stockhub.co/index.php?title=Module:Current_events_monthly_archive&amp;diff=135881&amp;oldid=prev"/>
		<updated>2017-10-18T14:16:56Z</updated>

		<summary type="html">&lt;p&gt;detect sandbox pages when parsing the current title&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- This module generates the monthly archives [[Portal:Current events]].&lt;br /&gt;
-- See a sample archive at [[Portal:Current events/September 2011]].&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Helper functions&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Return true if num is a positive integer; otherwise return false&lt;br /&gt;
local function isPositiveInteger(num)&lt;br /&gt;
	return num &amp;gt; 0 and num == math.floor(num)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Make an ordinal number from an integer.&lt;br /&gt;
local function makeOrdinalNumber(num)&lt;br /&gt;
	local suffix&lt;br /&gt;
	local rem100 = num % 100&lt;br /&gt;
	if rem100 == 11 or rem100 == 12 or rem100 == 13 then&lt;br /&gt;
		suffix = &amp;#039;th&amp;#039;&lt;br /&gt;
	else&lt;br /&gt;
		local rem10 = num % 10&lt;br /&gt;
		if rem10 == 1 then&lt;br /&gt;
			suffix = &amp;#039;st&amp;#039;&lt;br /&gt;
		elseif rem10 == 2 then&lt;br /&gt;
			suffix = &amp;#039;nd&amp;#039;&lt;br /&gt;
		elseif rem10 == 3 then&lt;br /&gt;
			suffix = &amp;#039;rd&amp;#039;&lt;br /&gt;
		else&lt;br /&gt;
			suffix = &amp;#039;th&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return tostring(num) .. suffix&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Try to parse the year and month from the current title.&lt;br /&gt;
-- This template is usually used on pages with titles like&lt;br /&gt;
-- [[Portal:Current events/September 2011]], so our general approach will be to&lt;br /&gt;
-- pass the subpage name to lang:formatDate and see if we get something that&amp;#039;s&lt;br /&gt;
-- not an error.&lt;br /&gt;
local function parseYearAndMonthFromCurrentTitle()&lt;br /&gt;
	local title = mw.title.getCurrentTitle()&lt;br /&gt;
	local lang = mw.language.getContentLanguage()&lt;br /&gt;
	-- Detect if we are on a sandbox page, and if so, use the base page.&lt;br /&gt;
	if title.subpageText:find(&amp;#039;^[sS]andbox%d*$&amp;#039;) then&lt;br /&gt;
		title = title.basePageTitle&lt;br /&gt;
	end&lt;br /&gt;
	-- Try to parse the date.&lt;br /&gt;
	local success, date = pcall(function ()&lt;br /&gt;
		-- lang:formatDate throws errors if it gets strange input,&lt;br /&gt;
		-- so use pcall to catch them, as random subpage names will&lt;br /&gt;
		-- usually not be well-formed dates.&lt;br /&gt;
		return lang:formatDate(&amp;#039;Y-m&amp;#039;, title.subpageText)&lt;br /&gt;
	end)&lt;br /&gt;
	if not success then&lt;br /&gt;
		-- We couldn&amp;#039;t parse the date, so return nil.&lt;br /&gt;
		return nil, nil&lt;br /&gt;
	end&lt;br /&gt;
	-- Parse the year and month numbers from the date we got from&lt;br /&gt;
	-- lang:formatDate. If we can&amp;#039;t parse them, then something has gone&lt;br /&gt;
	-- wrong with either lang:formatDate or our pattern.&lt;br /&gt;
	local year, month = date:match(&amp;#039;^(%d%d%d%d)%-(%d%d)$&amp;#039;)&lt;br /&gt;
	year = tonumber(year)&lt;br /&gt;
	month = tonumber(month)&lt;br /&gt;
	if not year or not month then&lt;br /&gt;
		error(&amp;#039;Internal error in [[Module:Current events &amp;#039;&lt;br /&gt;
			.. &amp;#039;monthly archive]]: couldn\&amp;#039;t match date &amp;#039;&lt;br /&gt;
			.. &amp;#039;from lang:formatDate output&amp;#039;&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
	return year, month&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Date info&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Get a table of information about the date for the monthly archive.&lt;br /&gt;
local function getDateInfo(year, month)&lt;br /&gt;
	local lang = mw.language.getContentLanguage()&lt;br /&gt;
	local dateFuncs = {}&lt;br /&gt;
	local dateInfo = setmetatable({}, {&lt;br /&gt;
		__index = function (t, key)&lt;br /&gt;
			-- Memoize values so we only have to calculate them once.&lt;br /&gt;
			if dateFuncs[key] then&lt;br /&gt;
				local val = dateFuncs[key]()&lt;br /&gt;
				t[key] = val&lt;br /&gt;
				return val&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	})&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.currentYear()&lt;br /&gt;
		-- The current year (number)&lt;br /&gt;
		return tonumber(os.date(&amp;#039;%Y&amp;#039;))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.currentMonthNumber()&lt;br /&gt;
		-- The current month (number)&lt;br /&gt;
		return tonumber(os.date(&amp;#039;%m&amp;#039;))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.year()&lt;br /&gt;
		-- The year (number)&lt;br /&gt;
		return tonumber(year) or dateInfo.currentYear&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.monthNumber()&lt;br /&gt;
		-- The month (number)&lt;br /&gt;
		return tonumber(month) or dateInfo.currentMonthNumber&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.monthNumberZeroPadded()&lt;br /&gt;
		-- The month, zero-padded to two digits (string)&lt;br /&gt;
		return string.format(&amp;#039;%02d&amp;#039;, dateInfo.monthNumber)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.date()&lt;br /&gt;
		-- The date in YYYY-MM-DD format (string)&lt;br /&gt;
		return string.format(&lt;br /&gt;
			&amp;#039;%04d-%02d-01&amp;#039;,&lt;br /&gt;
			dateInfo.year, dateInfo.monthNumber&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.monthName()&lt;br /&gt;
		-- The month name, e.g. &amp;quot;September&amp;quot; (string)&lt;br /&gt;
		return lang:formatDate(&amp;#039;F&amp;#039;, dateInfo.date)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.monthOrdinal()&lt;br /&gt;
		-- The ordinal month as an English word (string)&lt;br /&gt;
		local ordinals = {&lt;br /&gt;
			&amp;quot;first&amp;quot;,   &amp;quot;second&amp;quot;,   &amp;quot;third&amp;quot;,&lt;br /&gt;
			&amp;quot;fourth&amp;quot;,  &amp;quot;fifth&amp;quot;,    &amp;quot;sixth&amp;quot;,&lt;br /&gt;
			&amp;quot;seventh&amp;quot;, &amp;quot;eighth&amp;quot;,   &amp;quot;ninth&amp;quot;,&lt;br /&gt;
			&amp;quot;tenth&amp;quot;,   &amp;quot;eleventh&amp;quot;, &amp;quot;twelfth and final&amp;quot;,&lt;br /&gt;
		}&lt;br /&gt;
		return ordinals[dateInfo.monthNumber]&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.beVerb()&lt;br /&gt;
		-- If the month is the current month or a month in the future, then this&lt;br /&gt;
		-- is the string &amp;quot;is&amp;quot;; otherwise, &amp;quot;was&amp;quot; (string)&lt;br /&gt;
		if dateInfo.year &amp;gt; dateInfo.currentYear&lt;br /&gt;
			or (&lt;br /&gt;
				dateInfo.year == dateInfo.currentYear&lt;br /&gt;
				and dateInfo.monthNumber &amp;gt;= dateInfo.currentMonthNumber&lt;br /&gt;
			)&lt;br /&gt;
		then&lt;br /&gt;
			return &amp;#039;is&amp;#039;&lt;br /&gt;
		else&lt;br /&gt;
			return &amp;#039;was&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.leapDesc()&lt;br /&gt;
		-- The year&amp;#039;s leap year status; either &amp;quot;common&amp;quot;, &amp;quot;leap&amp;quot; or&lt;br /&gt;
		-- &amp;quot;century leap&amp;quot; (string)&lt;br /&gt;
		local isLeapYear = tonumber(lang:formatDate(&amp;#039;L&amp;#039;, dateInfo.date)) == 1&lt;br /&gt;
		if isLeapYear and dateInfo.year % 400 == 0 then&lt;br /&gt;
			return &amp;#039;century leap&amp;#039;&lt;br /&gt;
		elseif isLeapYear then&lt;br /&gt;
			return &amp;#039;leap&amp;#039;&lt;br /&gt;
		else&lt;br /&gt;
			return &amp;#039;common&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.decadeNote()&lt;br /&gt;
		-- If the month is the first or last of a decade, century, or&lt;br /&gt;
		-- millennium, a note to that effect; otherwise the empty string&lt;br /&gt;
		-- (string)&lt;br /&gt;
		local function getMillennium(year)&lt;br /&gt;
			return math.floor((year - 1) / 1000) + 1 -- Fenceposts&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local function getCentury(year)&lt;br /&gt;
			return math.floor((year - 1) / 100) + 1 -- Fenceposts&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local year = dateInfo.year&lt;br /&gt;
		local month = dateInfo.monthNumber&lt;br /&gt;
		local firstOrLast = month == 12 and &amp;quot;last&amp;quot; or &amp;quot;first&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		if year % 1000 == 0 and month == 12&lt;br /&gt;
			or year % 1000 == 1 and month == 1&lt;br /&gt;
		then&lt;br /&gt;
			local millennium = makeOrdinalNumber(getMillennium(year))&lt;br /&gt;
			local century = makeOrdinalNumber(getCentury(year))&lt;br /&gt;
			return string.format(&lt;br /&gt;
				--Millenniums always overlap centuries.&lt;br /&gt;
				&amp;quot;It %s the %s month of the [[%s millennium]] and the [[%s century]].&amp;quot;,&lt;br /&gt;
				dateInfo.beVerb, firstOrLast, millennium, century&lt;br /&gt;
			)&lt;br /&gt;
		elseif year % 100 == 0 and month == 12&lt;br /&gt;
			or year % 100 == 1 and month == 1&lt;br /&gt;
		then&lt;br /&gt;
			local century = makeOrdinalNumber(getCentury(year))&lt;br /&gt;
			return string.format(&lt;br /&gt;
				&amp;quot;It %s the %s month of the [[%s century]].&amp;quot;,&lt;br /&gt;
				dateInfo.beVerb, firstOrLast, century&lt;br /&gt;
			)&lt;br /&gt;
		elseif year % 10 == 9 and month == 12&lt;br /&gt;
			or year % 10 == 0 and month == 1&lt;br /&gt;
		then&lt;br /&gt;
			local decadeNumber = math.floor(dateInfo.year / 10) * 10&lt;br /&gt;
			return string.format(&lt;br /&gt;
				&amp;quot;It %s the %s month of the [[%ds]] decade.&amp;quot;,&lt;br /&gt;
				dateInfo.beVerb, firstOrLast, decadeNumber&lt;br /&gt;
			)&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.moonNote()&lt;br /&gt;
		-- If the month had no full moon, a note to that effect; otherwise the&lt;br /&gt;
		-- empty string (string)&lt;br /&gt;
		if dateInfo.monthNumber == 2 then&lt;br /&gt;
			-- https://www.quora.com/When-was-the-last-time-the-entire-month-of-February-passed-without-a-Full-Moon/answer/Alan-Marble&lt;br /&gt;
			local year = dateInfo.year&lt;br /&gt;
			if year == 1961&lt;br /&gt;
				or year == 1999&lt;br /&gt;
				or year == 2018&lt;br /&gt;
				or year == 2037&lt;br /&gt;
				or year == 2067&lt;br /&gt;
				or year == 2094&lt;br /&gt;
			then&lt;br /&gt;
				return &amp;#039;This month had no full moon.&amp;#039;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.firstDayOfMonth()&lt;br /&gt;
		-- Weekday of the first day of the month, e.g. &amp;quot;Tuesday&amp;quot; (string)&lt;br /&gt;
		return lang:formatDate(&amp;#039;l&amp;#039;, dateInfo.date)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.lastDayOfMonth()&lt;br /&gt;
		-- Weekday of the last day of the month, e.g. &amp;quot;Thursday&amp;quot; (string)&lt;br /&gt;
		return lang:formatDate(&amp;#039;l&amp;#039;, dateInfo.date .. &amp;#039; +1 month -1 day&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.daysInMonth()&lt;br /&gt;
		-- Number of days in the month (number)&lt;br /&gt;
		return tonumber(lang:formatDate(&lt;br /&gt;
			&amp;#039;j&amp;#039;,&lt;br /&gt;
			dateInfo.date .. &amp;#039; +1 month -1 day&amp;#039;)&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function dateFuncs.mainContent()&lt;br /&gt;
		-- The rendered content of all the current events portal pages for the&lt;br /&gt;
		-- month (string)&lt;br /&gt;
		local ret = {}&lt;br /&gt;
		local frame = mw.getCurrentFrame()&lt;br /&gt;
		local year = dateInfo.year&lt;br /&gt;
		local monthName = dateInfo.monthName&lt;br /&gt;
		for date = 1, 31 do&lt;br /&gt;
			local portalTitle = mw.title.new(string.format(&lt;br /&gt;
				&amp;#039;Portal:Current events/%d %s %d&amp;#039;,&lt;br /&gt;
				year, monthName, date&lt;br /&gt;
			))&lt;br /&gt;
			if portalTitle.exists then&lt;br /&gt;
				table.insert(&lt;br /&gt;
					ret,&lt;br /&gt;
					frame:expandTemplate{title = portalTitle.prefixedText}&lt;br /&gt;
				)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		return table.concat(ret, &amp;#039;\n&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return dateInfo&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Exports&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	-- Get the arguments&lt;br /&gt;
	local args = require(&amp;#039;Module:Arguments&amp;#039;).getArgs(frame, {&lt;br /&gt;
		wrappers = &amp;#039;Template:Current events monthly archive&amp;#039;,&lt;br /&gt;
	})&lt;br /&gt;
	local year = tonumber(args.year)&lt;br /&gt;
	local month = tonumber(args.month)&lt;br /&gt;
&lt;br /&gt;
	-- Validate the arguments&lt;br /&gt;
	if year and not isPositiveInteger(year) then&lt;br /&gt;
		error(&amp;#039;invalid year argument (must be a positive integer)&amp;#039;, 2)&lt;br /&gt;
	end&lt;br /&gt;
	if month then&lt;br /&gt;
		if not isPositiveInteger(month) then&lt;br /&gt;
			error(&amp;#039;invalid month argument (must be a positive integer)&amp;#039;, 2)&lt;br /&gt;
		elseif month &amp;gt; 12 then&lt;br /&gt;
			error(&amp;#039;invalid month argument (must be 12 or less)&amp;#039;, 2)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- If we weren&amp;#039;t passed a month or a year, try to get them from the&lt;br /&gt;
	-- page title.&lt;br /&gt;
	if not year and not month then&lt;br /&gt;
		year, month = parseYearAndMonthFromCurrentTitle()&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Convert the dateInfo table values into arguments to pass to the current&lt;br /&gt;
	-- events monthly archive display template&lt;br /&gt;
	local dateInfo = getDateInfo(year, month)&lt;br /&gt;
	local displayArgs = {}&lt;br /&gt;
	displayArgs[&amp;#039;year&amp;#039;]                     = dateInfo.year&lt;br /&gt;
	displayArgs[&amp;#039;month-name&amp;#039;]               = dateInfo.monthName&lt;br /&gt;
	displayArgs[&amp;#039;month-number&amp;#039;]             = dateInfo.monthNumber&lt;br /&gt;
	displayArgs[&amp;#039;month-number-zero-padded&amp;#039;] = dateInfo.monthNumberZeroPadded&lt;br /&gt;
	displayArgs[&amp;#039;be-verb&amp;#039;]                  = dateInfo.beVerb&lt;br /&gt;
	displayArgs[&amp;#039;month-ordinal&amp;#039;]            = dateInfo.monthOrdinal&lt;br /&gt;
	displayArgs[&amp;#039;leap-desc&amp;#039;]                = dateInfo.leapDesc&lt;br /&gt;
	displayArgs[&amp;#039;moon-note&amp;#039;]                = dateInfo.moonNote&lt;br /&gt;
	displayArgs[&amp;#039;decade-note&amp;#039;]              = dateInfo.decadeNote&lt;br /&gt;
	displayArgs[&amp;#039;first-day-of-month&amp;#039;]       = dateInfo.firstDayOfMonth&lt;br /&gt;
	displayArgs[&amp;#039;last-day-of-month&amp;#039;]        = dateInfo.lastDayOfMonth&lt;br /&gt;
	displayArgs[&amp;#039;days-in-month&amp;#039;]            = dateInfo.daysInMonth&lt;br /&gt;
	displayArgs[&amp;#039;main-content&amp;#039;]             = dateInfo.mainContent&lt;br /&gt;
&lt;br /&gt;
	-- Expand the display template with the arguments from dateInfo, and return&lt;br /&gt;
	-- it&lt;br /&gt;
	return frame:expandTemplate{&lt;br /&gt;
		title = &amp;#039;Current events monthly archive/display&amp;#039;,&lt;br /&gt;
		args = displayArgs,&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Export getDateInfo so that we can use it in unit tests.&lt;br /&gt;
p.getDateInfo = getDateInfo&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>imported&gt;Mr. Stradivarius</name></author>
	</entry>
</feed>