模块:Citation/CS1/Date validation:修订间差异
imported>Liangent test |
imported>Antigng |
||
| (未显示2个用户的8个中间版本) | |||
| 第1行: | 第1行: | ||
local p = {} | local p = {} | ||
--[[--------------------------< I S _ V A L I D _ D A T E _ F R O M _ A _ P O I N T >------------------------- | |||
将日期验证的逻辑拆分出来,供其它模块使用 | |||
]] | |||
local function is_valid_date_from_a_point (date, point_ts) | |||
local lang = mw.getContentLanguage(); | |||
local good1, good2; | |||
local access_ts, tomorrow_ts; -- to hold unix time stamps representing the dates | |||
good1, access_ts = pcall( lang.formatDate, lang, 'U', date ); -- convert date value to unix timesatmp | |||
good2, tomorrow_ts = pcall( lang.formatDate, lang, 'U', 'today + 2 days' ); -- today midnight + 2 days is one second more than all day tomorrow | |||
if good1 and good2 then | |||
access_ts = tonumber (access_ts); -- convert to numbers for the comparison | |||
tomorrow_ts = tonumber (tomorrow_ts); | |||
else | |||
return false; -- one or both failed to convert to unix time stamp | |||
end | |||
if point_ts <= access_ts and access_ts < tomorrow_ts then -- the point <= date < tomorrow's date | |||
return true; | |||
else | |||
return false; -- date out of range | |||
end | |||
end | |||
--[[--------------------------< I S _ V A L I D _ A C C E S S D A T E >---------------------------------------- | --[[--------------------------< I S _ V A L I D _ A C C E S S D A T E >---------------------------------------- | ||
| 第17行: | 第39行: | ||
local function is_valid_accessdate (accessdate) | local function is_valid_accessdate (accessdate) | ||
accessdate = accessdate:gsub("年", "-"); | |||
accessdate = accessdate:gsub("月", "-"); | |||
accessdate = accessdate:gsub("日", "-"); | |||
accessdate = accessdate:gsub("-$", ""); | |||
return is_valid_date_from_a_point (accessdate, 979516800); | |||
end | end | ||
| 第100行: | 第108行: | ||
return true; | return true; | ||
end | end | ||
--[[--------------------------< I S _ V A L I D _ Y E A R >---------------------------------------------------- | --[[--------------------------< I S _ V A L I D _ Y E A R >---------------------------------------------------- | ||
| 第115行: | 第122行: | ||
end | end | ||
--[[ | --[[--------------------------< I S _ V A L I D _ D A T E >---------------------------------------------------- | ||
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. | 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. | |||
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. Where the two calendars overlap (1582 to approximately | |||
1923) dates are assumed to be Gregorian. | |||
]] | ]] | ||
local 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; | ||
if not is_valid_year(year) then -- no farther into the future than next year | if not is_valid_year(year) then -- no farther into the future than next year | ||
return false; | return false; | ||
end | end | ||
if (2==month) then | month = tonumber(month); -- required for YYYY-MM-DD dates | ||
month_length = 28; | |||
if 1582 > tonumber(year) then | if (2==month) then -- if February | ||
month_length = 28; -- then 28 days unless | |||
if 1582 > tonumber(year) then -- Julian calendar | |||
if 0==(year%4) then | if 0==(year%4) then | ||
month_length = 29; | month_length = 29; | ||
end | end | ||
else | else -- Gregorian calendar | ||
if (0==(year%4) and (0~=(year%100) or 0==(year%400))) then -- is a leap year? | if (0==(year%4) and (0~=(year%100) or 0==(year%400))) then -- is a leap year? | ||
month_length = 29; | month_length = 29; -- if leap year then 29 days in February | ||
end | end | ||
end | end | ||
else | else | ||
month_length=days_in_month[ | month_length=days_in_month[month]; | ||
end | end | ||
| 第216行: | 第229行: | ||
This function receives a table of date parts for one or two dates and an empty table reference declared in | This function receives a table of date parts for one or two dates and an empty table reference declared in | ||
Module:Citation/CS1. The function is called only for |date= parameters and only if the |date=<value> is | Module:Citation/CS1. The function is called only for |date= parameters and only if the |date=<value> is | ||
determined to be a valid date format. The question of what to do with | determined to be a valid date format. The question of what to do with invalid date formats is not answered here. | ||
The date parts in the input table are converted to an ISO 8601 conforming date string: | The date parts in the input table are converted to an ISO 8601 conforming date string: | ||
| 第299行: | 第312行: | ||
--[[--------------------------< C H E C K _ D A T E >---------------------------------------------------------- | --[[--------------------------< C H E C K _ D A T E >---------------------------------------------------------- | ||
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. | Check date format to see that it is one of the formats approved by WP:DATESNO or WP:DATERANGE. Exception: only | ||
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 | allowed range separator is endash. Additionally, check the date to see that it is a real date: no 31 in 30-day | ||
character abbreviations, and seasons must be spelled correctly. Future years beyond next year are not allowed. | months; no 29 February when not a leap year. Months, both long-form and three character abbreviations, and seasons | ||
must be spelled correctly. Future years beyond next year are not allowed. | |||
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 | If the date fails the format tests, this function returns false and does not return values for anchor_year and | ||
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. | COinS_date. When this happens, the date parameter is 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. | |||
Inputs: | Inputs: | ||
| 第313行: | 第328行: | ||
true, anchor_year, COinS_date | true, anchor_year, COinS_date | ||
anchor_year can be used in CITEREF anchors | anchor_year can be used in CITEREF anchors | ||
COinS_date is ISO 8601 format date; see make_COInS_date() | COinS_date is ISO 8601 format date; see make_COInS_date() | ||
]] | ]] | ||
local function check_date (date_string, tCOinS_date) | local function check_date (date_string, tCOinS_date) | ||
local year; -- assume that year2, months, and days are not used; | local year; -- assume that year2, months, and days are not used; | ||
| 第418行: | 第434行: | ||
end | end | ||
elseif date_string:match ("^%a+–%a+ +[1-9]%d%d%d%a?$") then | elseif date_string:match ("^%a+–%a+ +[1-9]%d%d%d%a?$") then -- month/season range year; months separated by endash | ||
month, month2, anchor_year, year=date_string:match ("(%a+)–(%a+)%s*((%d%d%d%d)%a?)"); | month, month2, anchor_year, year=date_string:match ("(%a+)–(%a+)%s*((%d%d%d%d)%a?)"); | ||
if (not is_valid_month_season_range(month, month2)) or (not is_valid_year(year)) then return false; end | if (not is_valid_month_season_range(month, month2)) or (not is_valid_year(year)) then return false; end | ||
| 第491行: | 第507行: | ||
else | else | ||
return false; | return false; -- date format not one of the MOS:DATE approved formats | ||
end | end | ||
local result=true; | local result=true; -- check whole dates for validity; assume true because not all dates will go through this test | ||
if 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 == day2 then -- YMD (simple whole date) | if 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 == day2 then -- YMD (simple whole date) | ||
result=is_valid_date(year,month,day); | result=is_valid_date(year,month,day); | ||
| 第512行: | 第528行: | ||
if false == result then return false; end | if false == result then return false; end | ||
if nil ~= tCOinS_date then -- this table only passed into this function when testing |date= parameter values | if nil ~= tCOinS_date then -- this table only passed into this function when testing |date= parameter values | ||
| 第520行: | 第533行: | ||
end | end | ||
return true, anchor_year; | return true, anchor_year; -- format is good and date string represents a real date | ||
end | end | ||
--[[--------------------------< D A T E S >-------------------------------------------------------------------- | --[[--------------------------< D A T E S >-------------------------------------------------------------------- | ||
| 第539行: | 第552行: | ||
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 good_date = false; | ||
for k, v in pairs(date_parameters_list) do | for k, v in pairs(date_parameters_list) do -- for each date-holding parameter in the list | ||
if is_set(v) then | if is_set(v) then -- if the parameter has a value | ||
if v:match("^c%. [1-9]%d%d%d?%a?$") then | if v:match("^c%. [1-9]%d%d%d?%a?$") then -- special case for c. year or with or without CITEREF disambiguator - only |date= and |year= | ||
local year = v:match("c%. ([1-9]%d%d%d?)%a?"); | local year = v:match("c%. ([1-9]%d%d%d?)%a?"); -- get the year portion so it can be tested | ||
if 'date'==k then | if 'date'==k then | ||
anchor_year, COinS_date = v:match("((c%. [1-9]%d%d%d?)%a?)"); -- anchor year and COinS_date only from |date= parameter | anchor_year, COinS_date = v:match("((c%. [1-9]%d%d%d?)%a?)"); -- anchor year and COinS_date only from |date= parameter | ||
| 第552行: | 第564行: | ||
good_date = is_valid_year(year); | good_date = is_valid_year(year); | ||
end | end | ||
elseif 'date'==k then | elseif 'date'==k then -- if the parameter is |date= | ||
if v:match("^n%.d%.%a?") then | if v:match("^n%.d%.%a?") then -- if |date=n.d. with or without a CITEREF disambiguator | ||
good_date, anchor_year, COinS_date = true, v:match("((n%.d%.)%a?)"); --"n.d."; no error when date parameter is set to no date | good_date, anchor_year, COinS_date = true, v:match("((n%.d%.)%a?)"); --"n.d."; no error when date parameter is set to no date | ||
elseif v:match("^nd%a?$") then | elseif v:match("^nd%a?$") then -- if |date=nd with or without a CITEREF disambiguator | ||
good_date, anchor_year, COinS_date = true, v:match("((nd)%a?)"); --"nd"; no error when date parameter is set to no date | good_date, anchor_year, COinS_date = true, v:match("((nd)%a?)"); --"nd"; no error when date parameter is set to no date | ||
else | else | ||
good_date, anchor_year, COinS_date = check_date (v, tCOinS_date); | good_date, anchor_year, COinS_date = check_date (v, tCOinS_date); -- go test the date | ||
end | end | ||
elseif 'access-date'==k then | elseif 'access-date'==k then -- if the parameter is |date= | ||
good_date = check_date (v); | good_date = check_date (v); -- go test the date | ||
if true == good_date then | if true == good_date then -- if the date is a valid date | ||
good_date = is_valid_accessdate (v); | good_date = is_valid_accessdate (v); -- is Wikipedia start date < accessdate < tomorrow's date? | ||
end | end | ||
else | else -- any other date-holding parameter | ||
good_date = check_date (v); | good_date = check_date (v); -- go test the date | ||
end | end | ||
if false==good_date then | if false==good_date then -- assemble one error message so we don't add the tracking category multiple times | ||
if is_set(error_message) then | if is_set(error_message) then -- once we've added the first portion of the error message ... | ||
error_message=error_message .. ", "; | error_message=error_message .. ", "; -- ... add a comma space separator | ||
end | end | ||
error_message=error_message .. "|" .. k .. "="; | error_message=error_message .. "|" .. k .. "="; -- add the failed parameter | ||
end | end | ||
end | end | ||
end | end | ||
return anchor_year, error_message; -- and done | return anchor_year, error_message; -- and done | ||
end | end | ||
--[[--------------------------< Y E A R _ D A T E _ C H E C K >------------------------------------------------ | --[[--------------------------< Y E A R _ D A T E _ C H E C K >------------------------------------------------ | ||
| 第629行: | 第641行: | ||
end | end | ||
return {dates = dates, year_date_check = year_date_check} -- return exported functions | return {dates = dates, year_date_check = year_date_check, is_valid_date_from_a_point = is_valid_date_from_a_point} | ||
-- return exported functions | |||