13,398
edits
WikiDwarfBOT (talk | contribs) m (→top: robot:, replaced: WP: → BP: (2)) |
(Update) |
||
Line 1: | Line 1: | ||
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- | --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- | ||
]] | ]] | ||
local add_prop_cat, is_set, in_array, wrap_style; | local add_prop_cat, is_set, in_array, set_message, substitute, wrap_style; -- imported functions from selected Module:Citation/CS1/Utilities | ||
local cfg; -- table of tables imported from selected Module:Citation/CS1/Configuration | local cfg; -- table of tables imported from selected Module:Citation/CS1/Configuration | ||
Line 44: | Line 43: | ||
if good1 and good2 then -- lang.formatDate() returns a timestamp in the local script which which tonumber() may not understand | if good1 and good2 then -- lang.formatDate() returns a timestamp in the local script which which tonumber() may not understand | ||
access_ts = tonumber (access_ts) or lang_object:parseFormattedNumber (access_ts); | access_ts = tonumber (access_ts) or lang_object:parseFormattedNumber (access_ts); -- convert to numbers for the comparison; | ||
tomorrow_ts = tonumber (tomorrow_ts) or lang_object:parseFormattedNumber (tomorrow_ts); | tomorrow_ts = tonumber (tomorrow_ts) or lang_object:parseFormattedNumber (tomorrow_ts); | ||
else | else | ||
Line 55: | Line 54: | ||
return false; -- accessdate out of range | return false; -- accessdate out of range | ||
end | end | ||
end | end | ||
Line 85: | Line 65: | ||
local function get_month_number (month) | local function get_month_number (month) | ||
return cfg.date_names['local'].long[month] or cfg.date_names['local'].short[month] or | return cfg.date_names['local'].long[month] or cfg.date_names['local'].short[month] or -- look for local names first | ||
cfg.date_names['en'].long[month] or cfg.date_names['en'].short[month] or | cfg.date_names['en'].long[month] or cfg.date_names['en'].short[month] or -- failing that, look for English names | ||
0; | 0; -- not a recognized month name | ||
end | end | ||
Line 102: | Line 82: | ||
which became part of ISO 8601 in 2019. See '§Sub-year groupings'. The standard defines various divisions using | which became part of ISO 8601 in 2019. See '§Sub-year groupings'. The standard defines various divisions using | ||
numbers 21-41. cs1|2 only supports generic seasons. EDTF does support the distinction between north and south | numbers 21-41. cs1|2 only supports generic seasons. EDTF does support the distinction between north and south | ||
hemisphere seasons but cs1|2 has no way to make that distinction. | |||
These additional divisions not currently supported: | These additional divisions not currently supported: | ||
Line 195: | Line 175: | ||
Function gets current year from the server and compares it to year from a citation parameter. Years more than one | 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. | year in the future are not acceptable. | ||
Special case for |pmc-embargo-date=: years more than two years in the future are not acceptable | |||
]] | ]] | ||
local function is_valid_year (year) | local function is_valid_year (year, param) | ||
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 | ||
end | end | ||
year = tonumber (year) or lang_object:parseFormattedNumber (year); -- convert to | year = tonumber (year) or lang_object:parseFormattedNumber (year); -- convert to number for the comparison | ||
if year and (100 > year) then -- years less than 100 not supported | |||
return false; | |||
end | |||
if 'pmc-embargo-date' == param then -- special case for |pmc-embargo-date= | |||
return year and (year <= tonumber(os.date("%Y"))+2) or false; -- years more than two years in the future are not accepted | |||
end | |||
return year and (year <= year_limit) or false; | return year and (year <= year_limit) or false; | ||
end | end | ||
Line 219: | Line 208: | ||
]] | ]] | ||
local function is_valid_date (year, month, day) | local function is_valid_date (year, month, day, param) | ||
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 | if not is_valid_year (year, param) then -- no farther into the future than next year except |pmc-embargo-date= no more than two years in the future | ||
return false; | return false; | ||
end | end | ||
month = tonumber(month); -- required for YYYY-MM-DD dates | month = tonumber (month); -- required for YYYY-MM-DD dates | ||
if (2 == month) then | if (2 == month) then -- if February | ||
month_length = 28; -- then 28 days unless | month_length = 28; -- then 28 days unless | ||
if 1582 > tonumber(year) then -- Julian calendar | if 1582 > tonumber(year) then -- Julian calendar | ||
if 0 == (year%4) then | if 0 == (year%4) then -- is a leap year? | ||
month_length = 29; -- if leap year then 29 days in February | month_length = 29; -- if leap year then 29 days in February | ||
end | end | ||
else -- Gregorian calendar | else -- Gregorian calendar | ||
if (0 == (year%4) and (0 ~= (year%100) or 0 == (year%400))) then | if (0 == (year%4) and (0 ~= (year%100) or 0 == (year%400))) then -- is a leap year? | ||
month_length = 29; -- if leap year then 29 days in February | month_length = 29; -- if leap year then 29 days in February | ||
end | end | ||
Line 253: | Line 242: | ||
Months in a range are expected to have the same style: Jan–Mar or October–December but not February–Mar or Jul–August. | Months in a range are expected to have the same style: Jan–Mar or October–December but not February–Mar or Jul–August. | ||
This function looks in cfg.date_names{} to see if both month names are listed in the long subtable or both are | |||
listed in the short subtable. When both have the same style (both are listed in the same table), returns true; false else | |||
]] | ]] | ||
local function is_valid_month_range_style (month1, month2) | local function is_valid_month_range_style (month1, month2) | ||
if (cfg.date_names.en.long[month1] and cfg.date_names.en.long[month2]) or -- are both English names listed in the long subtable? | |||
(cfg.date_names.en.short[month1] and cfg.date_names.en.short[month2]) or -- are both English names listed in the short subtable? | |||
(cfg.date_names['local'].long[month1] and cfg.date_names['local'].long[month2]) or -- are both local names listed in the long subtable? | |||
(cfg.date_names['local'].short[month1] and cfg.date_names['local'].short[month2]) then -- are both local names listed in the short subtable? | |||
return true; | |||
end | end | ||
return false; -- names are mixed | |||
end | end | ||
Line 299: | Line 283: | ||
-- here when range_start is a month | -- here when range_start is a month | ||
range_end_number = get_month_number (range_end); -- get end month number | range_end_number = get_month_number (range_end); -- get end month number | ||
if range_start_number < range_end_number | if range_start_number < range_end_number and -- range_start is a month; does range_start precede range_end? | ||
is_valid_month_range_style (range_start, range_end) then -- do months have the same style? | |||
return true; -- proper order and same style | return true; -- proper order and same style | ||
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 341: | Line 324: | ||
local date; -- one date or first date in a range | local date; -- one date or first date in a range | ||
local date2 = ''; -- end of range date | local date2 = ''; -- end of range date | ||
input.year = tonumber (input.year) or lang_object:parseFormattedNumber (input.year); -- language-aware tonumber() | |||
input.year2 = tonumber (input.year2) or lang_object:parseFormattedNumber (input.year2); -- COinS dates are pseudo-ISO 8601 so convert to Arabic numerals | |||
-- start temporary Julian / Gregorian calendar uncertainty detection | -- start temporary Julian / Gregorian calendar uncertainty detection | ||
local year = | local year = input.year; -- this temporary code to determine the extent of sources dated to the Julian/Gregorian | ||
local month = tonumber(input.month); -- interstice 1 October 1582 – 1 January 1926 | local month = tonumber(input.month); -- interstice 1 October 1582 – 1 January 1926 | ||
local day = tonumber (input.day); | local day = tonumber (input.day); | ||
Line 352: | Line 338: | ||
end | end | ||
-- end temporary Julian / Gregorian calendar uncertainty detection | -- end temporary Julian / Gregorian calendar uncertainty detection | ||
if ((1582 == year) and (10 > month)) or (1582 > year) then -- if a Julian calendar date | |||
if 1582 > | tCOinS_date.rftdate = tostring (input.year); -- &rft.date gets year only | ||
date = input.year; | return; -- done | ||
end | |||
-- here for all forms of Gregorian dates | |||
if 20 < tonumber (input.month) then -- if season, quarter, or proper-name date | |||
date = input.year; -- &rft.date gets year only | |||
if 0 ~= input.year2 and input.year ~= input.year2 then -- if a range, only the second year portion when not the same as range start year | if 0 ~= input.year2 and input.year ~= input.year2 then -- if a range, only the second year portion when not the same as range start year | ||
date = string.format ('%.4d/%.4d', | date = string.format ('%.4d/%.4d', input.year, input.year2) -- assemble the date range | ||
end | end | ||
local season = {[24] = 'winter', [21] = 'spring', [22] = 'summer', [23] = 'fall', [33] = '1', [34] = '2', [35] = '3', [36] = '4', [98] = 'Easter', [99] = 'Christmas'}; -- seasons lowercase, no autumn; proper-names use title case | |||
if 0 == input.month2 then -- single season, quarter, or proper-name date | |||
if 40 < tonumber(input.month) then | |||
tCOinS_date.rftchron = season[input.month]; -- proper-name date; used in journal metadata only | |||
elseif 30 < tonumber(input.month) then | |||
tCOinS_date.rftquarter = season[input.month]; -- quarter date; used in journal metadata only | |||
else | |||
tCOinS_date.rftssn = season[input.month]; -- season date; used in journal metadata only | |||
end | |||
else -- season ranges are lumped into &rft.chron; &rft.ssn and &rft.quarter are left blank | |||
if input.year ~= input.year2 then -- season year – season year range or season year–year | |||
if 0 ~= input.month2 then | |||
tCOinS_date.rftchron = string.format ('%s %s – %s %s', season[input.month], input.year, season[input.month2], input.year2); -- used in journal metadata only | |||
end | end | ||
else -- season–season year range | |||
tCOinS_date.rftchron = season[input.month] .. '–' .. season[input.month2]; -- season–season year range; used in journal metadata only | |||
end | end | ||
end | end | ||
tCOinS_date.rftdate = date; | |||
tCOinS_date.rftdate = tostring (date); | |||
return; -- done | return; -- done | ||
end | end | ||
-- here for gregorian calendar dates | |||
if 0 ~= input.day then | if 0 ~= input.day then | ||
date = string.format ('%s-%.2d-%.2d', input.year, tonumber(input.month), tonumber(input.day)); -- whole date | date = string.format ('%s-%.2d-%.2d', input.year, tonumber(input.month), tonumber(input.day)); -- whole date | ||
Line 429: | Line 417: | ||
['dMy'] = {'^([1-9]%d?) +(%D-) +((%d%d%d%d?)%a?)$', 'd', 'm', 'a', 'y'}, | ['dMy'] = {'^([1-9]%d?) +(%D-) +((%d%d%d%d?)%a?)$', 'd', 'm', 'a', 'y'}, | ||
-- year-initial: year month day; day: 1 or 2 two digits, leading zero allowed; not supported at en.wiki | -- year-initial: year month day; day: 1 or 2 two digits, leading zero allowed; not supported at en.wiki | ||
-- ['yMd'] = {'^((%d%d%d%d?)%a?) +(%D-) +(%d%d?)$', 'a', 'y', 'm', 'd'}, | -- ['yMd'] = {'^((%d%d%d%d?)%a?) +(%D-) +(%d%d?)$', 'a', 'y', 'm', 'd'}, | ||
-- day-range-initial: day–day month year; days are separated by endash | -- day-range-initial: day–day month year; days are separated by endash | ||
['d-dMy'] = {'^([1-9]%d?)[%-–]([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'd2', 'm', 'a', 'y'}, | ['d-dMy'] = {'^([1-9]%d?)[%-–]([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'd2', 'm', 'a', 'y'}, | ||
Line 456: | Line 444: | ||
['y'] = {'^((%d%d%d%d?)%a?)$'}, -- year; here accept either YYY or YYYY | ['y'] = {'^((%d%d%d%d?)%a?)$'}, -- year; here accept either YYY or YYYY | ||
} | } | ||
--[[--------------------------< I S _ V A L I D _ E M B A R G O _ D A T E >------------------------------------ | |||
returns true and date value if that value has proper dmy, mdy, ymd format. | |||
returns false and 9999 (embargoed forever) when date value is not proper format; assumes that when |pmc-embargo-date= is | |||
set, the editor intended to embargo a PMC but |pmc-embargo-date= does not hold a single date. | |||
]] | |||
local function is_valid_embargo_date (v) | |||
if v:match (patterns['ymd'][1]) or -- ymd | |||
v:match (patterns['Mdy'][1]) or -- dmy | |||
v:match (patterns['dMy'][1]) then -- mdy | |||
return true, v; | |||
end | |||
return false, '9999'; -- if here not good date so return false and set embargo date to long time in future | |||
end | |||
--[[--------------------------< 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 | 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. Additionally, check the date to see that it is a real date: no 31 in 30-day | allowed range separator is endash. 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 character abbreviations, and seasons | months; no 29 February when not a leap year. Months, both long-form and three character abbreviations, and seasons | ||
Line 482: | Line 489: | ||
local function check_date (date_string, param, tCOinS_date) | local function check_date (date_string, param, tCOinS_date) | ||
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; | local year2 = 0; -- second year in a year range | ||
local month = 0; | local month = 0; | ||
local month2 = 0; | local month2 = 0; -- second month in a month range | ||
local day = 0; | local day = 0; | ||
local day2 = 0; -- second day in a day range | local day2 = 0; -- second day in a day range | ||
Line 562: | Line 569: | ||
month, year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns['Sy4-y2'][1]); | month, year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns['Sy4-y2'][1]); | ||
if 'Winter' ~= month and 'Summer' ~= month then return false end; -- 'month' can only be Winter or Summer | if 'Winter' ~= month and 'Summer' ~= month then return false end; -- 'month' can only be Winter or Summer | ||
anchor_year = year .. '–' .. anchor_year; | anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years | ||
year2 = century..year2; -- add the century to year2 for comparisons | year2 = century..year2; -- add the century to year2 for comparisons | ||
if 1 ~= tonumber(year2) - tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later | if 1 ~= tonumber(year2) - tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later | ||
Line 570: | Line 577: | ||
elseif mw.ustring.match(date_string, patterns['Sy-y'][1]) then -- special case Winter/Summer year-year; year separated with unspaced endash | elseif mw.ustring.match(date_string, patterns['Sy-y'][1]) then -- special case Winter/Summer year-year; year separated with unspaced endash | ||
month, year, anchor_year, year2 = mw.ustring.match(date_string, patterns['Sy-y'][1]); | month, year, anchor_year, year2 = mw.ustring.match(date_string, patterns['Sy-y'][1]); | ||
if 'Winter' ~= | month = get_season_number (month, param); -- <month> can only be winter or summer; also for metadata | ||
anchor_year = year .. '–' .. anchor_year; | if (month ~= cfg.date_names['en'].season['Winter']) and (month ~= cfg.date_names['en'].season['Summer']) then | ||
return false; -- not Summer or Winter; abandon | |||
end | |||
anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years | |||
if 1 ~= tonumber(year2) - tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later | if 1 ~= 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 -- 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 | ||
elseif mw.ustring.match(date_string, patterns['My-My'][1]) then -- month/season year - month/season year; separated by spaced endash | elseif mw.ustring.match(date_string, patterns['My-My'][1]) then -- month/season year - month/season year; separated by spaced endash | ||
month, year, month2, anchor_year, year2 = mw.ustring.match(date_string, patterns['My-My'][1]); | month, year, month2, anchor_year, year2 = mw.ustring.match(date_string, patterns['My-My'][1]); | ||
anchor_year = year .. '–' .. anchor_year; | anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years | ||
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 | ||
Line 612: | Line 621: | ||
elseif mw.ustring.match(date_string, patterns['y-y'][1]) then -- Year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999 | elseif mw.ustring.match(date_string, patterns['y-y'][1]) then -- Year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999 | ||
year, anchor_year, year2 = mw.ustring.match(date_string, patterns['y-y'][1]); | year, anchor_year, year2 = mw.ustring.match(date_string, patterns['y-y'][1]); | ||
anchor_year = year .. '–' .. anchor_year; | anchor_year = year .. '–' .. anchor_year; -- assemble anchor year from both years | ||
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 | ||
Line 619: | Line 628: | ||
local century; | local century; | ||
year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns['y4-y2'][1]); | year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns['y4-y2'][1]); | ||
anchor_year = year .. '–' .. anchor_year; | anchor_year = year .. '–' .. anchor_year; -- assemble anchor year from both years | ||
if 13 > tonumber(year2) then return false; end -- don't allow 2003-05 which might be May 2003 | if 13 > tonumber(year2) then return false; end -- don't allow 2003-05 which might be May 2003 | ||
year2 = century .. year2; | year2 = century .. year2; -- add the century to year2 for comparisons | ||
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 | ||
elseif mw.ustring.match(date_string, patterns['y'][1]) then | if in_array (param, {'date', 'publication-date', 'year'}) then -- here when 'valid' abbreviated year range; if one of these parameters | ||
add_prop_cat ('year-range-abbreviated'); -- add properties cat | |||
end | |||
elseif mw.ustring.match(date_string, patterns['y'][1]) then -- year; here accept either YYY or YYYY | |||
anchor_year, year = mw.ustring.match(date_string, patterns['y'][1]); | anchor_year, year = mw.ustring.match(date_string, patterns['y'][1]); | ||
if false == is_valid_year(year) then | if false == is_valid_year(year) then | ||
Line 640: | Line 649: | ||
end | end | ||
if 'access-date' == param then -- test | if param ~= 'date' then -- CITEREF disambiguation only allowed in |date=; |year= & |publication-date= promote to date | ||
if anchor_year:match ('%l$') then | |||
return false; | |||
end | |||
end | |||
if 'access-date' == param then -- test access-date here because we have numerical date parts | |||
if 0 ~= year and 0 ~= month and 0 ~= day and -- all parts of a single date required | if 0 ~= year and 0 ~= month and 0 ~= day and -- all parts of a single date required | ||
0 == year2 and 0 == month2 and 0 == day2 then -- none of these; | 0 == year2 and 0 == month2 and 0 == day2 then -- none of these; access-date must not be a range | ||
if not is_valid_accessdate(year .. '-' .. month .. '-' .. day) then | if not is_valid_accessdate(year .. '-' .. month .. '-' .. day) then | ||
return false; -- return false when | return false; -- return false when access-date out of bounds | ||
end | end | ||
else | else | ||
return false; -- return false when | return false; -- return false when access-date is a range of two dates | ||
end | |||
end | |||
if 'archive-date' == param then -- test archive-date here because we have numerical date parts | |||
if not (0 ~= year and 0 ~= month and 0 ~= day and -- all parts of a single date required | |||
0 == year2 and 0 == month2 and 0 == day2) then -- none of these; archive-date must not be a range | |||
return false; -- return false when archive-date is a range of two dates | |||
end | end | ||
end | end | ||
Line 653: | Line 675: | ||
local result=true; -- check whole dates for validity; assume true because not all dates will go through this test | 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, param); -- <param> for |pmc-embargo-date= | ||
elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 ~= day2 then -- YMD-d (day range) | elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 ~= day2 then -- YMD-d (day range) | ||
result = is_valid_date(year, month, day); | result = is_valid_date (year, month, day); | ||
result = result and is_valid_date(year, month, day2); | result = result and is_valid_date (year, month, day2); | ||
elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 ~= month2 and 0 ~= day2 then -- YMD-md (day month range) | elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 ~= month2 and 0 ~= day2 then -- YMD-md (day month range) | ||
result = is_valid_date(year, month, day); | result = is_valid_date (year, month, day); | ||
result = result and is_valid_date(year, month2, day2); | result = result and is_valid_date (year, month2, day2); | ||
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) | 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) | ||
Line 718: | Line 740: | ||
good_date, anchor_year, COinS_date = true, v.val:match("((%d+)%a?)"); | good_date, anchor_year, COinS_date = true, v.val:match("((%d+)%a?)"); | ||
end | end | ||
elseif 'pmc-embargo-date' == k then | elseif 'pmc-embargo-date' == k then -- if the parameter is |pmc-embargo-date= | ||
good_date = check_date (v.val, k); -- go test the date | good_date = check_date (v.val, k); -- go test the date | ||
if true == good_date then -- if the date is a valid date | if true == good_date then -- if the date is a valid date | ||
good_date, embargo_date = is_valid_embargo_date (v.val); -- is |pmc-embargo-date= date a single dmy, mdy, or ymd formatted date? yes: returns embargo; no: returns 9999 | good_date, embargo_date = is_valid_embargo_date (v.val); -- is |pmc-embargo-date= date a single dmy, mdy, or ymd formatted date? yes: returns embargo date; no: returns 9999 | ||
end | end | ||
else -- any other date-holding parameter | else -- any other date-holding parameter | ||
Line 737: | Line 759: | ||
--[[--------------------------< 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 >------------------------------------------------ | ||
Compare the value provided in |year= with the year value(s) provided in |date=. This function | Compare the value provided in |year= with the year value(s) provided in |date=. This function sets a local numeric value: | ||
0 - year value does not match the year value in date | 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 | 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) | 2 - year value matches the year value in date when date is in the form YYYY-MM-DD and year is disambiguated (|year=YYYYx) | ||
the numeric value in <result> determines the 'output' if any from this function: | |||
0 – adds error message to error_list sequence table | |||
1 – adds maint cat | |||
2 – does nothing | |||
]] | ]] | ||
local function year_date_check (year_string, date_string) | local function year_date_check (year_string, year_origin, date_string, date_origin, error_list) | ||
local year; | local year; | ||
local date1; | local date1; | ||
local date2; | local date2; | ||
local result = 1; -- result of the test; assume that the test passes | local result = 1; -- result of the test; assume that the test passes | ||
year = year_string:match ('(%d%d%d%d?)'); | year = year_string:match ('(%d%d%d%d?)'); | ||
Line 780: | Line 807: | ||
result = 0; | result = 0; | ||
end | end | ||
else | else -- should never get here; this function called only when no other date errors | ||
result = 0; -- no recognizable year in date | result = 0; -- no recognizable year in date | ||
end | end | ||
if 0 == result then -- year / date mismatch | |||
table.insert (error_list, substitute (cfg.messages['mismatch'], {year_origin, date_origin})); -- add error message to error_list sequence table | |||
elseif 1 == result then -- redundant year / date | |||
set_message ('maint_date_year'); -- add a maint cat | |||
end | |||
end | end | ||
Line 819: | Line 851: | ||
['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy | ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy | ||
['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy | ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy | ||
-- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, | -- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki | ||
}, | }, | ||
['Mdy'] = { -- date format is Mdy; reformat to: | ['Mdy'] = { -- date format is Mdy; reformat to: | ||
Line 825: | Line 857: | ||
['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy | ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy | ||
['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd | ['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd | ||
-- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, | -- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki | ||
}, | }, | ||
['dMy'] = { -- date format is dMy; reformat to: | ['dMy'] = { -- date format is dMy; reformat to: | ||
Line 831: | Line 863: | ||
['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy | ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy | ||
['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd | ['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd | ||
-- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, | -- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki | ||
}, | }, | ||
['Md-dy'] = { -- date format is Md-dy; reformat to: | ['Md-dy'] = { -- date format is Md-dy; reformat to: | ||
Line 866: | Line 898: | ||
['any'] = {'%s %s', 'm', 'y'}, -- dmy/mdy agnostic | ['any'] = {'%s %s', 'm', 'y'}, -- dmy/mdy agnostic | ||
}, | }, | ||
-- ['yMd'] = { | -- ['yMd'] = { -- not supported at en.wiki | ||
-- ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, | -- ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy | ||
-- ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, | -- ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy | ||
-- ['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, | -- ['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd | ||
-- }, | -- }, | ||
} | } | ||
Line 887: | Line 919: | ||
end | end | ||
-- yMd is not supported at en.wiki; when yMd is supported at your wiki, uncomment the next line | |||
-- if 'yMd' == format_param and in_array (pattern_idx, {'yMd', 'Md-dy', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy'}) then -- these formats not convertable; yMd not supported at en.wiki | |||
-- if 'yMd' == format_param and in_array (pattern_idx, {'yMd', 'Md-dy', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy'}) then -- these formats not convertable; yMd not supported at en.wiki | if 'yMd' == format_param then -- yMd not supported at en.wiki; when yMd is supported at your wiki, remove or comment-out this line | ||
if 'yMd' == format_param then -- yMd not supported at en.wiki | |||
return; -- not a reformattable date | return; -- not a reformattable date | ||
end | end | ||
Line 908: | Line 938: | ||
}; | }; | ||
if t.a then -- if this date has an anchor year capture | if t.a then -- if this date has an anchor year capture (all convertable date formats except ymd) | ||
t.y = t.a; | if t.y2 then -- for year range date formats | ||
t.y2 = t.a; -- use the anchor year capture when reassembling the date | |||
else -- here for single date formats (except ymd) | |||
t.y = t.a; -- use the anchor year capture when reassembling the date | |||
end | |||
end | end | ||
if tonumber(t.m) then -- if raw month is a number (converting from ymd) | if tonumber(t.m) then -- if raw month is a number (converting from ymd) | ||
if 's' == mon_len then -- if we are to use abbreviated month names | if 's' == mon_len then -- if we are to use abbreviated month names | ||
t.m = cfg.date_names[' | t.m = cfg.date_names['inv_local_short'][tonumber(t.m)]; -- convert it to a month name | ||
else | else | ||
t.m = cfg.date_names[' | t.m = cfg.date_names['inv_local_long'][tonumber(t.m)]; -- convert it to a month name | ||
end | end | ||
t.d = t.d:gsub ('0(%d)', '%1'); -- strip leading '0' from day if present | t.d = t.d:gsub ('0(%d)', '%1'); -- strip leading '0' from day if present | ||
elseif 'ymd' == format_param then -- when converting to ymd | elseif 'ymd' == format_param then -- when converting to ymd | ||
if 1582 > tonumber(t.y) then -- ymd format dates not allowed before 1582 | t.y = t.y:gsub ('%a', ''); -- strip CITREF disambiguator if present; anchor year already known so process can proceed; TODO: maint message? | ||
if 1582 > tonumber (t.y) then -- ymd format dates not allowed before 1582 | |||
return; | return; | ||
end | end | ||
Line 930: | Line 965: | ||
t[mon] = get_month_number (t[mon]); -- get the month number for this month (is length agnostic) | t[mon] = get_month_number (t[mon]); -- get the month number for this month (is length agnostic) | ||
if 0 == t[mon] then return; end -- seasons and named dates can't be converted | if 0 == t[mon] then return; end -- seasons and named dates can't be converted | ||
t[mon] = (('s' == mon_len) and cfg.date_names[' | t[mon] = (('s' == mon_len) and cfg.date_names['inv_local_short'][t[mon]]) or cfg.date_names['inv_local_long'][t[mon]]; -- fetch month name according to length | ||
end | end | ||
end | end | ||
Line 1,011: | Line 1,046: | ||
date_parameters_list[param_name].val = new_date; -- update date in date list | date_parameters_list[param_name].val = new_date; -- update date in date list | ||
result = true; -- and announce that changes have been made | result = true; -- and announce that changes have been made | ||
break; | |||
end | end | ||
end -- if | end -- if | ||
Line 1,017: | Line 1,053: | ||
end -- if | end -- if | ||
end -- for | end -- for | ||
return result; | return result; -- declare boolean result and done | ||
end | end | ||
Line 1,034: | Line 1,070: | ||
local n; | local n; | ||
for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list | for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list | ||
if is_set (param_val.val) | if is_set (param_val.val) and | ||
not mw.ustring.match (param_val.val, patterns.ymd[1]) then -- for those that are not ymd dates (ustring because here digits may not be Western) | |||
param_val.val, n = param_val.val:gsub ('%-', '–'); -- replace any hyphen with ndash | param_val.val, n = param_val.val:gsub ('%-', '–'); -- replace any hyphen with ndash | ||
if 0 ~= n then | if 0 ~= n then | ||
Line 1,041: | Line 1,077: | ||
result = true; | result = true; | ||
end | end | ||
end | end | ||
end | end | ||
Line 1,050: | Line 1,085: | ||
--[[-------------------------< D A T E _ N A M E _ X L A T E >------------------------------------------------ | --[[-------------------------< D A T E _ N A M E _ X L A T E >------------------------------------------------ | ||
Attempts to translate English | Attempts to translate English date names to local-language date names using names supplied by MediaWiki's | ||
date parser function. This is simple name-for-name replacement and may not work for all languages. | date parser function. This is simple name-for-name replacement and may not work for all languages. | ||
Line 1,064: | Line 1,099: | ||
local date; | local date; | ||
local sources_t = { | |||
{cfg.date_names.en.long, cfg.date_names.inv_local_long}, -- for translating long English month names to long local month names | |||
{cfg.date_names.en.short, cfg.date_names.inv_local_short}, -- short month names | |||
{cfg.date_names.en.quarter, cfg.date_names.inv_local_quarter}, -- quarter date names | |||
{cfg.date_names.en.season, cfg.date_names.inv_local_season}, -- season date nam | |||
{cfg.date_names.en.named, cfg.date_names.inv_local_named}, -- named dates | |||
} | |||
local function is_xlateable (month) -- local function to get local date name that replaces existing English-language date name | |||
for _, date_names_t in ipairs (sources_t) do -- for each sequence table in date_names_t | |||
if date_names_t[1][month] then -- if date name is English month (long or short), quarter, season or named and | |||
if date_names_t[2][date_names_t[1][month]] then -- if there is a matching local date name | |||
return date_names_t[2][date_names_t[1][month]]; -- return the local date name | |||
end | |||
end | |||
end | |||
end | |||
for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list | for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list | ||
if is_set(param_val.val) then -- if the parameter has a value | if is_set(param_val.val) then -- if the parameter has a value | ||
date = param_val.val; | date = param_val.val; | ||
for month in mw.ustring.gmatch (date, '%a+') do | for month in mw.ustring.gmatch (date, '[%a ]+') do -- iterate through all date names in the date (single date or date range) | ||
month = mw.text.trim (month); -- this because quarterly dates contain whitespace | |||
xlate = is_xlateable (month); -- get translate <month>; returns translation or nil | |||
if xlate then | |||
if | |||
date = mw.ustring.gsub (date, month, xlate); -- replace the English with the translation | date = mw.ustring.gsub (date, month, xlate); -- replace the English with the translation | ||
date_parameters_list[param_name].val = date; -- save the translated date | date_parameters_list[param_name].val = date; -- save the translated date | ||
Line 1,093: | Line 1,140: | ||
return modified; | return modified; | ||
end | |||
Line 1,106: | Line 1,153: | ||
is_set = utilities_page_ptr.is_set; | is_set = utilities_page_ptr.is_set; | ||
in_array = utilities_page_ptr.in_array; | in_array = utilities_page_ptr.in_array; | ||
set_message = utilities_page_ptr.set_message; | |||
substitute = utilities_page_ptr.substitute; | |||
wrap_style = utilities_page_ptr.wrap_style; | wrap_style = utilities_page_ptr.wrap_style; | ||
cfg = cfg_table_ptr; -- import tables from selected Module:Citation/CS1/Configuration | cfg = cfg_table_ptr; -- import tables from selected Module:Citation/CS1/Configuration | ||
end | |||
--[[--------------------------< A R C H I V E _ D A T E _ C H E C K >------------------------------------------ | |||
Compare value in |archive-date= with the timestamp in Wayback machine urls. Emits an error message when |archive-date= | |||
does not match the timestamp. | |||
]] | |||
local function archive_date_check (archive_date, archive_url_timestamp) | |||
local good, archive_date_ts = pcall (lang_object.formatDate, lang_object, 'Ymd', archive_date); -- |archive-date= value to YYYYMMDD format | |||
-- local archive_date_ts = lang_object:formatDate ('Ymd', archive_date); -- |archive-date= value to YYYYMMDD format | |||
if good then | |||
if not archive_url_timestamp:find (archive_date_ts, 1, true) then -- plain text find; begin search at position 1 | |||
set_message ('err_archive_date_url_ts_mismatch'); -- emit an error message | |||
end | |||
end | |||
end | end | ||
Line 1,117: | Line 1,184: | ||
return { -- return exported functions | return { -- return exported functions | ||
archive_date_check = archive_date_check, | |||
date_hyphen_to_dash = date_hyphen_to_dash, | |||
date_name_xlate = date_name_xlate, | |||
dates = dates, | dates = dates, | ||
reformat_dates = reformat_dates, | |||
set_selected_modules = set_selected_modules, | |||
year_date_check = year_date_check, | year_date_check = year_date_check, | ||
} | } |
edits