Module:Citation/CS1/Date validation: Difference between revisions
Synch from sandbox;
m>Trappist the monk m (Synch from sandbox;) |
m>Trappist the monk (Synch from sandbox;) |
||
Line 3: | Line 3: | ||
-- 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 | -- 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 | ||
function get_month_number (month) | local function get_month_number (month) | ||
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}; | 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}; | ||
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}; | 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}; | ||
Line 15: | Line 15: | ||
-- 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 | -- 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 | ||
function get_season_number (season) | local function get_season_number (season) | ||
local season_list = {['Winter']=1, ['Spring']=2, ['Summer']=3, ['Fall']=4, ['Autumn']=4} | local season_list = {['Winter']=1, ['Spring']=2, ['Summer']=3, ['Fall']=4, ['Autumn']=4} | ||
local temp; | local temp; | ||
Line 24: | Line 24: | ||
--returns true if month or season is valid (properly spelled, capitalized, abbreviated) | --returns true if month or season is valid (properly spelled, capitalized, abbreviated) | ||
function is_valid_month_or_season (month_season) | local function is_valid_month_or_season (month_season) | ||
if 0 == get_month_number (month_season) then -- if month text isn't one of the twelve months, might be a season | if 0 == get_month_number (month_season) then -- if month text isn't one of the twelve months, might be a season | ||
if 0 == get_season_number (month_season) then -- not a month, is it a season? | if 0 == get_season_number (month_season) then -- not a month, is it a season? | ||
Line 40: | Line 40: | ||
]] | ]] | ||
function is_valid_year(year) | local function is_valid_year(year) | ||
if not is_set(year_limit) then | if not is_set(year_limit) then | ||
year_limit = tonumber(os.date("%Y"))+1; -- global variable so we only have to fetch it once | year_limit = tonumber(os.date("%Y"))+1; -- global variable so we only have to fetch it once | ||
Line 53: | Line 53: | ||
Where the two calendars overlap (1582 to approximately 1923) dates are assumed to be Gregorian. | Where the two calendars overlap (1582 to approximately 1923) dates are assumed to be Gregorian. | ||
]] | ]] | ||
function is_valid_date (year, month, day) | local function is_valid_date (year, month, day) | ||
local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; | local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; | ||
local month_length; | local month_length; | ||
Line 81: | Line 81: | ||
end | end | ||
--[[ | --[[--------------------------< I S _ V A L I D _ M O N T H _ R A N G E _ S T Y L E >-------------------------- | ||
Months in a range are expected to have the same style: Jan–Mar or October–December but not February–Mar or Jul–August. | |||
There is a special test for May because it can be either short or long form. | |||
Returns true when style for both months is the same | |||
]] | |||
local function is_valid_month_range_style (month1, month2) | |||
local len1 = month1:len(); | |||
local len2 = month2:len(); | |||
if len1 == len2 then | |||
return true; -- both months are short form so return true | |||
elseif 'May' == month1 or 'May'== month2 then | |||
return true; -- both months are long form so return true | |||
elseif 3 == len1 or 3 == len2 then | |||
return false; -- months are mixed form so return false | |||
else | |||
return true; -- both months are long form so return true | |||
end | |||
end | |||
--[[--------------------------< I S _ V A L I D _ M O N T H _ S E A S O N _ R A N G E >------------------------ | |||
Check a pair of months or seasons to see if both are valid members of a month or season pair. | Check a pair of months or seasons to see if both are valid members of a month or season pair. | ||
Month pairs are expected to be left to right, earliest to latest in time. | Month pairs are expected to be left to right, earliest to latest in time. | ||
an oddity with seasons | |||
is made to see if |date=Fall-Winter yyyy (4-1) is the date. | Similarly, seasons are also left to right, earliest to latest in time. There is 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 is made to see if |date=Fall-Winter yyyy (4-1) is the date. | |||
]] | ]] | ||
function is_valid_month_season_range(range_start, range_end) | local function is_valid_month_season_range(range_start, range_end) | ||
local range_start_number = get_month_number (range_start); | local range_start_number = get_month_number (range_start); | ||
Line 109: | Line 136: | ||
local range_end_number = get_month_number (range_end); -- get end month number | local range_end_number = get_month_number (range_end); -- get end month number | ||
if range_start_number < range_end_number then -- range_start is a month; does range_start precede range_end? | if range_start_number < range_end_number then -- range_start is a month; does range_start precede range_end? | ||
return true; | if is_valid_month_range_style (range_start, range_end) then -- do months have the same style? | ||
return true; -- proper order and same style | |||
end | |||
end | end | ||
return false; -- range_start month number is greater than or equal to range end number; or range end isn't a month | return false; -- range_start month number is greater than or equal to range end number; or range end isn't a month | ||
Line 131: | Line 160: | ||
COinS_date is date_string without anchor_year disambiguator if any | COinS_date is date_string without anchor_year disambiguator if any | ||
]] | ]] | ||
function check_date (date_string) | local function check_date (date_string) | ||
local year; -- assume that year2, months, and days are not used; | local year; -- assume that year2, months, and days are not used; | ||
local year2=0; -- second year in a year range | local year2=0; -- second year in a year range | ||
Line 184: | Line 213: | ||
day, month, year, day2, month2, anchor_year, year2=date_string:match("(%d%d?) +(%a+) +(%d%d%d%d?) – (%d%d?) +(%a+) +((%d%d%d%d?)%a?)"); | day, month, year, day2, month2, anchor_year, year2=date_string:match("(%d%d?) +(%a+) +(%d%d%d%d?) – (%d%d?) +(%a+) +((%d%d%d%d?)%a?)"); | ||
if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later | if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later | ||
if not is_valid_year(year2) then return false; end | if not is_valid_year(year2) or not is_valid_month_range_style(month, month2) then return false; end -- year2 no more than one year in the future; months same style | ||
month = get_month_number (month); | month = get_month_number (month); | ||
month2 = get_month_number (month2); | month2 = get_month_number (month2); | ||
Line 191: | Line 220: | ||
month, day, year, month2, day2, anchor_year, year2=date_string:match("(%a+) +(%d%d?), +(%d%d%d%d) – (%a+) +(%d%d?), +((%d%d%d%d)%a?)"); | month, day, year, month2, day2, anchor_year, year2=date_string:match("(%a+) +(%d%d?), +(%d%d%d%d) – (%a+) +(%d%d?), +((%d%d%d%d)%a?)"); | ||
if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later | if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later | ||
if not is_valid_year(year2) then return false; end | if not is_valid_year(year2) or not is_valid_month_range_style(month, month2) then return false; end -- year2 no more than one year in the future; months same style | ||
month = get_month_number (month); | month = get_month_number (month); | ||
month2 = get_month_number (month2); | month2 = get_month_number (month2); | ||
Line 216: | Line 245: | ||
if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same | if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same | ||
if not is_valid_year(year2) then return false; end -- no year farther in the future than next year | if not is_valid_year(year2) then return false; end -- no year farther in the future than next year | ||
if not((0 ~= get_month_number(month) and 0 ~= get_month_number(month2)) or | if not((0 ~= get_month_number(month) and 0 ~= get_month_number(month2) and is_valid_month_range_style(month, month2)) or -- both must be month year, same month style | ||
(0 ~= get_season_number(month) and 0 ~= get_season_number(month2))) then return false; end | (0 ~= get_season_number(month) and 0 ~= get_season_number(month2))) then return false; end -- or season year, not mixed | ||
elseif date_string:match ("^%a+–%a+ +[1-9]%d%d%d%a?$") then -- month/season range year; months separated by endash | elseif date_string:match ("^%a+–%a+ +[1-9]%d%d%d%a?$") then -- month/season range year; months separated by endash | ||
Line 291: | Line 320: | ||
]] | ]] | ||
function p.dates(date_parameters_list) | --function p.dates(date_parameters_list) | ||
local function dates(date_parameters_list) | |||
local anchor_year; -- will return as nil if the date being tested is not |date= | local anchor_year; -- will return as nil if the date being tested is not |date= | ||
local COinS_date; -- will return as nil if the date being tested is not |date= | local COinS_date; -- will return as nil if the date being tested is not |date= | ||
local error_message =""; | local error_message = ""; | ||
local good_date=false; | local mismatch = 0; | ||
local good_date = false; | |||
for k, v in pairs(date_parameters_list) do -- for each date-holding parameter in the list | for k, v in pairs(date_parameters_list) do -- for each date-holding parameter in the list | ||
Line 326: | Line 357: | ||
end | end | ||
end | end | ||
return anchor_year, COinS_date, error_message, mismatch; -- and done | |||
end | |||
--[[--------------------------< Y E A R _ D A T E _ C H E C K >------------------------------------------------ | |||
Compare the value provided in |year= with the year value(s) provided in |date=. This function returns a numeric value: | |||
0 - year value does not match the year value in date | |||
1 - (default) year value matches the year value in date or one of the year values when date contains two years | |||
2 - year value matches the year value in date when date is in the form YYYY-MM-DD and year is disambiguated (|year=YYYYx) | |||
]] | |||
local function year_date_check (year_string, date_string) | |||
local year; | |||
local date1; | |||
local date2; | |||
local result = 1; -- result of the test; assume that the test passes | |||
year = year_string:match ('(%d%d%d%d?)'); | |||
if date_string:match ('%d%d%d%d%-%d%d%-%d%d') and year_string:match ('%d%d%d%d%a') then --special case where date and year required YYYY-MM-DD and YYYYx | |||
date1 = date_string:match ('(%d%d%d%d)'); | |||
year = year_string:match ('(%d%d%d%d)'); | |||
if year ~= date1 then | |||
result = 0; -- years don't match | |||
else | |||
result = 2; -- years match; but because disambiguated, don't add to maint cat | |||
end | |||
elseif date_string:match ("%d%d%d%d?.-%d%d%d%d?") then -- any of the standard formats of date with two three- or four-digit years | |||
date1, date2 = date_string:match ("(%d%d%d%d?).-(%d%d%d%d?)"); | |||
if year ~= date1 and year ~= date2 then | |||
result = 0; | |||
end | |||
elseif date_string:match ("%d%d%d%d[%s%-–]+%d%d") then -- YYYY-YY date ranges | |||
local century; | |||
date1, century, date2 = date_string:match ("((%d%d)%d%d)[%s%-–]+(%d%d)"); | |||
date2 = century..date2; -- convert YY to YYYY | |||
if year ~= date1 and year ~= date2 then | |||
result = 0; | |||
end | |||
elseif date_string:match ("%d%d%d%d?") then -- any of the standard formats of date with one year | |||
date1 = date_string:match ("(%d%d%d%d?)"); | |||
if year ~= date1 then | |||
result = 0; | |||
end | |||
end | |||
return result; | |||
end | end | ||
return | return {dates = dates, year_date_check = year_date_check} -- return exported functions |