<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://forgloryorgold.com/index.php?action=history&amp;feed=atom&amp;title=Module%3ACitation%2FCS1%2FDate_validation</id>
	<title>Module:Citation/CS1/Date validation - Revision history</title>
	<link rel="self" type="application/atom+xml" href="http://forgloryorgold.com/index.php?action=history&amp;feed=atom&amp;title=Module%3ACitation%2FCS1%2FDate_validation"/>
	<link rel="alternate" type="text/html" href="http://forgloryorgold.com/index.php?title=Module:Citation/CS1/Date_validation&amp;action=history"/>
	<updated>2026-04-08T00:19:39Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.40.1</generator>
	<entry>
		<id>http://forgloryorgold.com/index.php?title=Module:Citation/CS1/Date_validation&amp;diff=261&amp;oldid=prev</id>
		<title>Cilraaz: 1 revision imported</title>
		<link rel="alternate" type="text/html" href="http://forgloryorgold.com/index.php?title=Module:Citation/CS1/Date_validation&amp;diff=261&amp;oldid=prev"/>
		<updated>2015-01-06T19:02:31Z</updated>

		<summary type="html">&lt;p&gt;1 revision imported&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- returns a number according to the month in a date: 1 for January, etc.  Capitalization and spelling must be correct. If not a valid month, returns 0&lt;br /&gt;
function get_month_number (month)&lt;br /&gt;
local long_months = {['January']=1, ['February']=2, ['March']=3, ['April']=4, ['May']=5, ['June']=6, ['July']=7, ['August']=8, ['September']=9, ['October']=10, ['November']=11, ['December']=12};&lt;br /&gt;
local short_months = {['Jan']=1, ['Feb']=2, ['Mar']=3, ['Apr']=4, ['May']=5, ['Jun']=6, ['Jul']=7, ['Aug']=8, ['Sep']=9, ['Oct']=10, ['Nov']=11, ['Dec']=12};&lt;br /&gt;
local temp;&lt;br /&gt;
	temp=long_months[month];&lt;br /&gt;
	if temp then return temp; end				-- if month is the long-form name&lt;br /&gt;
	temp=short_months[month];&lt;br /&gt;
	if temp then return temp; end				-- if month is the short-form name&lt;br /&gt;
	return 0;									-- misspelled, improper case, or not a month name&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- returns a number according to the sequence of seasons in a year: 1 for Winter, etc.  Capitalization and spelling must be correct. If not a valid season, returns 0&lt;br /&gt;
function get_season_number (season)&lt;br /&gt;
local season_list = {['Winter']=1, ['Spring']=2, ['Summer']=3, ['Fall']=4, ['Autumn']=4}&lt;br /&gt;
local temp;&lt;br /&gt;
	temp=season_list[season];&lt;br /&gt;
	if temp then return temp; end				-- if season is a valid name return its number&lt;br /&gt;
	return 0;									-- misspelled, improper case, or not a season name&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--returns true if month or season is valid (properly spelled, capitalized, abbreviated)&lt;br /&gt;
function is_valid_month_or_season (month_season)&lt;br /&gt;
	if 0 == get_month_number (month_season) then		-- if month text isn't one of the twelve months, might be a season&lt;br /&gt;
		if 0 == get_season_number (month_season) then	-- not a month, is it a season?&lt;br /&gt;
			return false;								-- return false not a month or one of the five seasons&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return true;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ V A L I D _ Y E A R &amp;gt;----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Function gets current year from the server and compares it to year from a citation parameter.  Years more than one year in the future are not acceptable.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
function is_valid_year(year)&lt;br /&gt;
	if not is_set(year_limit) then&lt;br /&gt;
		year_limit = tonumber(os.date(&amp;quot;%Y&amp;quot;))+1;			-- global variable so we only have to fetch it once&lt;br /&gt;
	end&lt;br /&gt;
	return tonumber(year) &amp;lt;= year_limit;				-- false if year is in the future more than one year&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Returns true if day is less than or equal to the number of days in month and year is no farther into the future than next year; else returns false.&lt;br /&gt;
&lt;br /&gt;
Assumes Julian calendar prior to year 1582 and Gregorian calendar thereafter. Accounts for Julian calendar leap years before 1582 and Gregorian leap years after 1582.&lt;br /&gt;
Where the two calendars overlap (1582 to approximately 1923) dates are assumed to be Gregorian.&lt;br /&gt;
]]&lt;br /&gt;
function is_valid_date (year, month, day)&lt;br /&gt;
local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};&lt;br /&gt;
local month_length;&lt;br /&gt;
	if not is_valid_year(year) then	-- no farther into the future than next year&lt;br /&gt;
		return false;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if (2==month) then							-- if February&lt;br /&gt;
		month_length = 28;						-- then 28 days unless&lt;br /&gt;
		if 1582 &amp;gt; tonumber(year) then			-- Julian calendar&lt;br /&gt;
			if 0==(year%4) then&lt;br /&gt;
				month_length = 29;&lt;br /&gt;
			end&lt;br /&gt;
		else									-- Gregorian calendar&lt;br /&gt;
			if (0==(year%4) and (0~=(year%100) or 0==(year%400))) then	-- is a leap year?&lt;br /&gt;
				month_length = 29;				-- if leap year then 29 days in February&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		month_length=days_in_month[month];&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if tonumber (day) &amp;gt; month_length then&lt;br /&gt;
		return false;&lt;br /&gt;
	end&lt;br /&gt;
	return true;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Check a pair of months or seasons to see if both are valid members of a month or season pair.&lt;br /&gt;
&lt;br /&gt;
Month pairs are expected to be left to right, earliest to latest in time.  Similarly, seasons are also left to right, earliest to latest in time.  There is&lt;br /&gt;
an oddity with seasons.  Winter is assigned a value of 1, spring 2, ..., fall and autumn 4.  Because winter can follow fall/autumn at the end of a calender year, a special test&lt;br /&gt;
is made to see if |date=Fall-Winter yyyy (4-1) is the date.&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
function is_valid_month_season_range(range_start, range_end)&lt;br /&gt;
	local range_start_number = get_month_number (range_start);&lt;br /&gt;
	&lt;br /&gt;
	if 0 == range_start_number then								-- is this a month range?&lt;br /&gt;
		local range_start_number = get_season_number (range_start);		-- not a month; is it a season? get start season number&lt;br /&gt;
		local range_end_number = get_season_number (range_end);			-- get end season number&lt;br /&gt;
&lt;br /&gt;
		if 0 ~= range_start_number then							-- is start of range a season?&lt;br /&gt;
			if range_start_number &amp;lt; range_end_number then		-- range_start is a season&lt;br /&gt;
				return true;									-- return true when range_end is also a season and follows start season; else false&lt;br /&gt;
			end&lt;br /&gt;
			if 4 == range_start_number and 1 == range_end_number then	-- special case when range is Fall-Winter or Autumn-Winter&lt;br /&gt;
				return true;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		return false;		-- range_start is not a month or a season; or range_start is a season and range_end is not; or improper season sequence&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local range_end_number = get_month_number (range_end);		-- get end month number&lt;br /&gt;
	if range_start_number &amp;lt; range_end_number then				-- range_start is a month; does range_start precede range_end?&lt;br /&gt;
		return true;											-- if yes, return true&lt;br /&gt;
	end&lt;br /&gt;
	return false;												-- range_start month number is greater than or equal to range end number; or range end isn't a month&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Check date format to see that it is one of the formats approved by WP:DATESNO or WP:DATERANGE. Exception: only allowed range separator is endash.&lt;br /&gt;
Additionally, check the date to see that it is a real date: no 31 in 30-day months; no 29 February when not a leap year.  Months, both long-form and three&lt;br /&gt;
character abbreviations, and seasons must be spelled correctly. Future years beyond next year are not allowed.&lt;br /&gt;
&lt;br /&gt;
If the date fails the format tests, this function returns false and does not return values for anchor_year and COinS_date.  When this happens, the date parameter is&lt;br /&gt;
used in the COinS metadata and the CITEREF identifier gets its year from the year parameter if present otherwise CITEREF does not get a date value.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
	date_string - date string from date-holding parameters (date, year, accessdate, embargo, archivedate, etc.)&lt;br /&gt;
&lt;br /&gt;
Returns:&lt;br /&gt;
	false if date string is not a real date; else&lt;br /&gt;
	true, anchor_year, COinS_date&lt;br /&gt;
		anchor_year can be used in CITEREF anchors&lt;br /&gt;
		COinS_date is date_string without anchor_year disambiguator if any&lt;br /&gt;
]]&lt;br /&gt;
function check_date (date_string)&lt;br /&gt;
	local year;			-- assume that year2, months, and days are not used;&lt;br /&gt;
	local year2=0;		-- second year in a year range&lt;br /&gt;
	local month=0;&lt;br /&gt;
	local month2=0;		-- second month in a month range&lt;br /&gt;
	local day=0;&lt;br /&gt;
	local day2=0;		-- second day in a day range&lt;br /&gt;
	local anchor_year;&lt;br /&gt;
	local coins_date;&lt;br /&gt;
&lt;br /&gt;
	if date_string:match(&amp;quot;^%d%d%d%d%-%d%d%-%d%d$&amp;quot;) then										-- year-initial numerical year month day format&lt;br /&gt;
		year, month, day=string.match(date_string, &amp;quot;(%d%d%d%d)%-(%d%d)%-(%d%d)&amp;quot;);&lt;br /&gt;
		month=tonumber(month);&lt;br /&gt;
		if 12 &amp;lt; month or 1 &amp;gt; month or 1583 &amp;gt; tonumber(year) then return false; end			-- month number not valid or not Gregorian calendar&lt;br /&gt;
		anchor_year = year;&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^%a+ +[1-9]%d?, +[1-9]%d%d%d%a?$&amp;quot;) then						-- month-initial: month day, year&lt;br /&gt;
		month, day, anchor_year, year=string.match(date_string, &amp;quot;(%a+)%s*(%d%d?),%s*((%d%d%d%d)%a?)&amp;quot;);&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		if 0 == month then return false; end												-- return false if month text isn't one of the twelve months&lt;br /&gt;
				&lt;br /&gt;
	elseif date_string:match(&amp;quot;^%a+ +[1-9]%d?–[1-9]%d?, +[1-9]%d%d%d%a?$&amp;quot;) then				-- month-initial day range: month day–day, year; days are separated by endash&lt;br /&gt;
		month, day, day2, anchor_year, year=string.match(date_string, &amp;quot;(%a+) +(%d%d?)–(%d%d?), +((%d%d%d%d)%a?)&amp;quot;);&lt;br /&gt;
		if tonumber(day) &amp;gt;= tonumber(day2) then return false; end							-- date range order is left to right: earlier to later; dates may not be the same;&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		if 0 == month then return false; end												-- return false if month text isn't one of the twelve months&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^[1-9]%d? +%a+ +[1-9]%d%d%d%a?$&amp;quot;) then						-- day-initial: day month year&lt;br /&gt;
		day, month, anchor_year, year=string.match(date_string, &amp;quot;(%d%d*)%s*(%a+)%s*((%d%d%d%d)%a?)&amp;quot;);&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		if 0 == month then return false; end												-- return false if month text isn't one of the twelve months&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^[1-9]%d?–[1-9]%d? +%a+ +[1-9]%d%d%d%a?$&amp;quot;) then				-- day-range-initial: day–day month year; days are separated by endash&lt;br /&gt;
		day, day2, month, anchor_year, year=string.match(date_string, &amp;quot;(%d%d?)–(%d%d?) +(%a+) +((%d%d%d%d)%a?)&amp;quot;);&lt;br /&gt;
		if tonumber(day) &amp;gt;= tonumber(day2) then return false; end							-- date range order is left to right: earlier to later; dates may not be the same;&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		if 0 == month then return false; end												-- return false if month text isn't one of the twelve months&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^[1-9]%d? +%a+ – [1-9]%d? +%a+ +[1-9]%d%d%d%a?$&amp;quot;) then		-- day initial month-day-range: day month - day month year; uses spaced endash&lt;br /&gt;
		day, month, day2, month2, anchor_year, year=date_string:match(&amp;quot;(%d%d?) +(%a+) – (%d%d?) +(%a+) +((%d%d%d%d)%a?)&amp;quot;);&lt;br /&gt;
		if (not is_valid_month_season_range(month, month2)) or not is_valid_year(year) then return false; end	-- date range order is left to right: earlier to later;&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		month2 = get_month_number (month2);&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^%a+ +[1-9]%d? – %a+ +[1-9]%d?, +[1-9]%d%d%d?%a?$&amp;quot;) then		-- month initial month-day-range: month day – month day, year;  uses spaced endash&lt;br /&gt;
		month, day, month2, day2, anchor_year, year=date_string:match(&amp;quot;(%a+) +(%d%d?) – (%a+) +(%d%d?), +((%d%d%d%d)%a?)&amp;quot;);&lt;br /&gt;
		if (not is_valid_month_season_range(month, month2)) or not is_valid_year(year) then return false; end&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		month2 = get_month_number (month2);&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^[1-9]%d? +%a+ +[1-9]%d%d%d – [1-9]%d? +%a+ +[1-9]%d%d%d%a?$&amp;quot;) then		-- day initial month-day-year-range: day month year - day month year; uses spaced endash&lt;br /&gt;
		day, month, year, day2, month2, anchor_year, year2=date_string:match(&amp;quot;(%d%d?) +(%a+) +(%d%d%d%d?) – (%d%d?) +(%a+) +((%d%d%d%d?)%a?)&amp;quot;);&lt;br /&gt;
		if tonumber(year2) &amp;lt;= tonumber(year) then return false; end							-- must be sequential years, left to right, earlier to later&lt;br /&gt;
		if not is_valid_year(year2) then return false; end									-- year2 no more than one year in the future&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		month2 = get_month_number (month2);&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^%a+ +[1-9]%d?, +[1-9]%d%d%d – %a+ +[1-9]%d?, +[1-9]%d%d%d%a?$&amp;quot;) then		-- month initial month-day-year-range: month day, year – month day, year;  uses spaced endash&lt;br /&gt;
		month, day, year, month2, day2, anchor_year, year2=date_string:match(&amp;quot;(%a+) +(%d%d?), +(%d%d%d%d) – (%a+) +(%d%d?), +((%d%d%d%d)%a?)&amp;quot;);&lt;br /&gt;
		if tonumber(year2) &amp;lt;= tonumber(year) then return false; end							-- must be sequential years, left to right, earlier to later&lt;br /&gt;
		if not is_valid_year(year2) then return false; end									-- year2 no more than one year in the future&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		month2 = get_month_number (month2);&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^%a+ +[1-9]%d%d%d–%d%d%a?$&amp;quot;) then								-- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash&lt;br /&gt;
		if nil == date_string:match(&amp;quot;^Winter&amp;quot;) and nil == date_string:match(&amp;quot;^Summer&amp;quot;) then return false end;	-- 'month' can only be Winter or Summer&lt;br /&gt;
		local century;&lt;br /&gt;
		year, century, anchor_year, year2=date_string:match(&amp;quot;%a+ +((%d%d)%d%d)–((%d%d)%a?)&amp;quot;);&lt;br /&gt;
		anchor_year=year..'–'..anchor_year;													-- assemble anchor_year from both years&lt;br /&gt;
		year2 = century..year2;																-- add the century to year2 for comparisons&lt;br /&gt;
		if 1 ~= tonumber(year2) - tonumber(year) then return false; end						-- must be sequential years, left to right, earlier to later&lt;br /&gt;
		if not is_valid_year(year2) then return false; end									-- no year farther in the future than next year&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^%a+ +[1-9]%d%d%d–[1-9]%d%d%d%a?$&amp;quot;) then						-- special case Winter/Summer year-year; year separated with unspaced endash&lt;br /&gt;
		if nil == date_string:match(&amp;quot;^Winter&amp;quot;) and nil == date_string:match(&amp;quot;^Summer&amp;quot;) then return false end;	-- 'month' can only be Winter or Summer&lt;br /&gt;
		year, anchor_year, year2=date_string:match(&amp;quot;%a+ +(%d%d%d%d)–((%d%d%d%d)%a?)&amp;quot;);&lt;br /&gt;
		anchor_year=year..'–'..anchor_year;													-- assemble anchor_year from both years&lt;br /&gt;
		if 1 ~= tonumber(year2) - tonumber(year) then return false; end						-- must be sequential years, left to right, earlier to later&lt;br /&gt;
		if not is_valid_year(year2) then return false; end									-- no year farther in the future than next year&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^%a+ +[1-9]%d%d%d% – %a+ +[1-9]%d%d%d%a?$&amp;quot;) then				-- month/season year - month/season year; separated by spaced endash&lt;br /&gt;
		month, year, month2, anchor_year, year2=date_string:match(&amp;quot;(%a+) +(%d%d%d%d) – (%a+) +((%d%d%d%d)%a?)&amp;quot;);&lt;br /&gt;
		anchor_year=year..'–'..anchor_year;													-- assemble anchor_year from both years&lt;br /&gt;
		if tonumber(year) &amp;gt;= tonumber(year2) then return false; end							-- left to right, earlier to later, not the same&lt;br /&gt;
		if not is_valid_year(year2) then return false; end									-- no year farther in the future than next year&lt;br /&gt;
		if not((0 ~= get_month_number(month) and 0 ~= get_month_number(month2)) or 			-- both must be month year or season year, not mixed&lt;br /&gt;
			(0 ~= get_season_number(month) and 0 ~= get_season_number(month2))) then return false; end&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match (&amp;quot;^%a+–%a+ +[1-9]%d%d%d%a?$&amp;quot;) then								-- month/season range year; months separated by endash &lt;br /&gt;
		month, month2, anchor_year, year=date_string:match (&amp;quot;(%a+)–(%a+)%s*((%d%d%d%d)%a?)&amp;quot;);&lt;br /&gt;
		if (not is_valid_month_season_range(month, month2)) or (not is_valid_year(year)) then&lt;br /&gt;
			return false;&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
	elseif date_string:match(&amp;quot;^%a+ +%d%d%d%d%a?$&amp;quot;) then							-- month/season year&lt;br /&gt;
		month, anchor_year, year=date_string:match(&amp;quot;(%a+)%s*((%d%d%d%d)%a?)&amp;quot;);&lt;br /&gt;
		if not is_valid_year(year) then return false; end&lt;br /&gt;
		if not is_valid_month_or_season (month) then return false; end&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^[1-9]%d%d%d?–[1-9]%d%d%d?%a?$&amp;quot;) then				-- Year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999&lt;br /&gt;
		year, anchor_year, year2=date_string:match(&amp;quot;(%d%d%d%d?)–((%d%d%d%d?)%a?)&amp;quot;);&lt;br /&gt;
		anchor_year=year..'–'..anchor_year;										-- assemble anchor year from both years&lt;br /&gt;
		if tonumber(year) &amp;gt;= tonumber(year2) then return false; end				-- left to right, earlier to later, not the same&lt;br /&gt;
		if not is_valid_year(year2) then return false; end						-- no year farther in the future than next year&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^[1-9]%d%d%d–%d%d%a?$&amp;quot;) then						-- Year range: YYYY–YY; separated by unspaced endash&lt;br /&gt;
		local century;&lt;br /&gt;
		year, century, anchor_year, year2=date_string:match(&amp;quot;((%d%d)%d%d)–((%d%d)%a?)&amp;quot;);&lt;br /&gt;
		anchor_year=year..'–'..anchor_year;										-- assemble anchor year from both years&lt;br /&gt;
		if 13 &amp;gt; tonumber(year2) then return false; end							-- don't allow 2003-05 which might be May 2003&lt;br /&gt;
		year2 = century..year2;													-- add the century to year2 for comparisons&lt;br /&gt;
		if tonumber(year) &amp;gt;= tonumber(year2) then return false; end				-- left to right, earlier to later, not the same&lt;br /&gt;
		if not is_valid_year(year2) then return false; end						-- no year farther in the future than next year&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match(&amp;quot;^[1-9]%d%d%d?%a?$&amp;quot;) then							-- year; here accept either YYY or YYYY&lt;br /&gt;
		anchor_year, year=date_string:match(&amp;quot;((%d%d%d%d?)%a?)&amp;quot;);&lt;br /&gt;
		if false == is_valid_year(year) then&lt;br /&gt;
			return false;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
	else&lt;br /&gt;
		return false;											-- date format not one of the MOS:DATE approved formats&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local result=true;											-- check whole dates for validity; assume true because not all dates will go through this test&lt;br /&gt;
	if 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 == day2 then		-- YMD (simple whole date)&lt;br /&gt;
		result=is_valid_date(year,month,day);&lt;br /&gt;
&lt;br /&gt;
	elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 ~= day2 then	-- YMD-d (day range)&lt;br /&gt;
		result=is_valid_date(year,month,day);&lt;br /&gt;
		result=result and is_valid_date(year,month,day2);&lt;br /&gt;
&lt;br /&gt;
	elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 ~= month2 and 0 ~= day2 then	-- YMD-md (day month range)&lt;br /&gt;
		result=is_valid_date(year,month,day);&lt;br /&gt;
		result=result and is_valid_date(year,month2,day2);&lt;br /&gt;
&lt;br /&gt;
	elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 ~= year2 and 0 ~= month2 and 0 ~= day2 then	-- YMD-ymd (day month year range)&lt;br /&gt;
		result=is_valid_date(year,month,day);&lt;br /&gt;
		result=result and is_valid_date(year2,month2,day2);&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if false == result then return false; end&lt;br /&gt;
																-- if here, then date_string is valid; get coins_date from date_string (leave CITEREF disambiguator) ...&lt;br /&gt;
	coins_date=date_string:match(&amp;quot;^(.+%d)%a?$&amp;quot;);				-- last character of valid disambiguatable date is always a digit&lt;br /&gt;
	coins_date= mw.ustring.gsub(coins_date, &amp;quot;–&amp;quot;, &amp;quot;-&amp;quot; );			-- ... and replace any ndash with a hyphen&lt;br /&gt;
	&lt;br /&gt;
	return true, anchor_year, coins_date;						-- format is good and date string represents a real date&lt;br /&gt;
end	&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; D A T E S &amp;gt;--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Cycle the date-holding parameters in passed table date_parameters_list through check_date() to check compliance with MOS:DATE. For all valid dates, check_date() returns&lt;br /&gt;
true. The |date= parameter test is unique, it is the only date holding parameter from which values for anchor_year (used in CITEREF identifiers) and COinS_date (used in&lt;br /&gt;
the COinS metadata) are derived.  The |date= parameter is the only date-holding parameter that is allowed to contain the no-date keywords &amp;quot;n.d.&amp;quot; or &amp;quot;nd&amp;quot; (without quotes).&lt;br /&gt;
&lt;br /&gt;
Unlike most error messages created in this module, only one error message is created by this function. Because all of the date holding parameters are processed serially,&lt;br /&gt;
a single error message is created as the dates are tested.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
function p.dates(date_parameters_list)&lt;br /&gt;
	local anchor_year;		-- will return as nil if the date being tested is not |date=&lt;br /&gt;
	local COinS_date;		-- will return as nil if the date being tested is not |date=&lt;br /&gt;
	local error_message =&amp;quot;&amp;quot;;&lt;br /&gt;
	local good_date=false;&lt;br /&gt;
	&lt;br /&gt;
	for k, v in pairs(date_parameters_list) do										-- for each date-holding parameter in the list&lt;br /&gt;
		if is_set(v) then															-- if the parameter has a value&lt;br /&gt;
			if v:match(&amp;quot;^c%. [1-9]%d%d%d?%a?$&amp;quot;) then								-- special case for c. year or with or without CITEREF disambiguator - only |date= and |year=&lt;br /&gt;
				local year = v:match(&amp;quot;c%. ([1-9]%d%d%d?)%a?&amp;quot;);						-- get the year portion so it can be tested&lt;br /&gt;
				if 'date'==k then&lt;br /&gt;
					anchor_year, COinS_date = v:match(&amp;quot;((c%. [1-9]%d%d%d?)%a?)&amp;quot;);	-- anchor year and COinS_date only from |date= parameter&lt;br /&gt;
					good_date = is_valid_year(year);&lt;br /&gt;
				elseif 'year'==k then&lt;br /&gt;
					good_date = is_valid_year(year);&lt;br /&gt;
				end&lt;br /&gt;
			elseif 'date'==k then													-- if the parameter is |date=&lt;br /&gt;
				if v:match(&amp;quot;^n%.d%.%a?&amp;quot;) then										-- if |date=n.d. with or without a CITEREF disambiguator&lt;br /&gt;
					good_date, anchor_year, COinS_date = true, v:match(&amp;quot;((n%.d%.)%a?)&amp;quot;);	--&amp;quot;n.d.&amp;quot;; no error when date parameter is set to no date&lt;br /&gt;
				elseif v:match(&amp;quot;^nd%a?$&amp;quot;) then										-- if |date=nd with or without a CITEREF disambiguator&lt;br /&gt;
					good_date, anchor_year, COinS_date = true, v:match(&amp;quot;((nd)%a?)&amp;quot;);	--&amp;quot;nd&amp;quot;;	no error when date parameter is set to no date&lt;br /&gt;
				else&lt;br /&gt;
					good_date, anchor_year, COinS_date = check_date (v);			-- go test the date&lt;br /&gt;
				end&lt;br /&gt;
			else																	-- any other date-holding parameter&lt;br /&gt;
				good_date = check_date (v);											-- go test the date&lt;br /&gt;
			end&lt;br /&gt;
			if false==good_date then												-- assemble one error message so we don't add the tracking category multiple times&lt;br /&gt;
				if is_set(error_message) then										-- once we've added the first portion of the error message ...&lt;br /&gt;
					error_message=error_message .. &amp;quot;, &amp;quot;;							-- ... add a comma space separator&lt;br /&gt;
				end&lt;br /&gt;
				error_message=error_message .. &amp;quot;&amp;amp;#124;&amp;quot; .. k .. &amp;quot;=&amp;quot;;				-- add the failed parameter&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return anchor_year, COinS_date, error_message;		-- and done&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p;&lt;/div&gt;</summary>
		<author><name>Cilraaz</name></author>
	</entry>
</feed>