Difference between pages "Module:Citation/CS1/Date validation" and "Module:Citation/CS1/Date validation/sandbox"
(Difference between pages)
Josh Renaud (talk | contribs) |
Josh Renaud (talk | contribs) m (1 revision imported) |
||
Line 1: | Line 1: | ||
− | --[[-- | + | --[[ |
+ | History of changes since last sync: 2016-07-30 | ||
+ | |||
]] | ]] | ||
− | local | + | local p = {} |
− | |||
− | --[[--------------------------< F | + | --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- |
− | |||
− | |||
− | |||
]] | ]] | ||
− | local | + | local is_set, in_array; -- imported function from selected Module:Citation/CS1/Utilities |
− | |||
Line 22: | Line 19: | ||
Wikipedia start date is 2001-01-15T00:00:00 UTC which is 979516800 seconds after 1970-01-01T00:00:00 UTC (the start of Unix time) | Wikipedia start date is 2001-01-15T00:00:00 UTC which is 979516800 seconds after 1970-01-01T00:00:00 UTC (the start of Unix time) | ||
− | accessdate is the date provided in | | + | accessdate is the date provided in |accessdate= at time 00:00:00 UTC |
today is the current date at time 00:00:00 UTC plus 48 hours | today is the current date at time 00:00:00 UTC plus 48 hours | ||
if today is 2015-01-01T00:00:00 then | if today is 2015-01-01T00:00:00 then | ||
Line 29: | Line 26: | ||
This function does not work if it is fed month names for languages other than English. Wikimedia #time: parser | This function does not work if it is fed month names for languages other than English. Wikimedia #time: parser | ||
− | apparently doesn't understand non- | + | apparently doesn't understand non-Engish date month names. This function will always return false when the date |
contains a non-English month name because good1 is false after the call to lang.formatDate(). To get around that | contains a non-English month name because good1 is false after the call to lang.formatDate(). To get around that | ||
call this function with YYYY-MM-DD format dates. | call this function with YYYY-MM-DD format dates. | ||
Line 36: | Line 33: | ||
local function is_valid_accessdate (accessdate) | local function is_valid_accessdate (accessdate) | ||
+ | local lang = mw.getContentLanguage(); | ||
local good1, good2; | local good1, good2; | ||
− | local access_ts, tomorrow_ts; -- to hold | + | local access_ts, tomorrow_ts; -- to hold unix time stamps representing the dates |
− | + | ||
− | good1, access_ts = pcall ( | + | good1, access_ts = pcall( lang.formatDate, lang, 'U', accessdate ); -- convert accessdate value to unix timesatmp |
− | good2, tomorrow_ts = pcall ( | + | 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 | + | if good1 and good2 then |
− | access_ts = tonumber | + | access_ts = tonumber (access_ts); -- convert to numbers for the comparison |
− | tomorrow_ts = tonumber | + | tomorrow_ts = tonumber (tomorrow_ts); |
else | else | ||
− | return false; -- one or both failed to convert to | + | return false; -- one or both failed to convert to unix time stamp |
end | end | ||
Line 59: | Line 57: | ||
--[[--------------------------< G E T _ M O N T H _ N U M B E R >---------------------------------------------- | --[[--------------------------< G E T _ M O N T H _ N U M B E R >---------------------------------------------- | ||
− | returns a number according to the month in a date: 1 for January, etc. Capitalization and spelling must be correct. | + | 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 |
− | If not a valid month, returns 0 | ||
]] | ]] | ||
local 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 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}; | |
− | + | return long_months[month] or -- if month is the long-form name | |
+ | short_months[month] or -- if month is the short-form name | ||
+ | 0; -- misspelled, improper case, or not a month name | ||
end | end | ||
− | --[[--------------------------< | + | --[[--------------------------< I S _ V A L I D _ E M B A R G O _ D A T E >------------------------------------ |
− | returns | + | returns true and date value if that value has proper dmy, mdy, ymd format. |
− | |||
− | |||
− | returns | + | returns false and 9999 (embargoed forever) when date value is not proper format; assumes that when |embargo= is |
− | + | set, the editor intended to embargo a pmc but |embargo= does not hold a single date. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
]] | ]] | ||
− | local function | + | local function is_valid_embargo_date (v) |
− | if ' | + | if v:match ('^%d%d%d%d%-%d%d%-%d%d$') or -- ymd |
− | + | v:match ('^%d%d?%s+%a+%s+%d%d%d%d$') or -- dmy | |
+ | v:match ('^%a+%s+%d%d?%s*,%s*%d%d%d%d$') then -- mdy | ||
+ | return true, v; | ||
end | end | ||
− | return | + | return false, '9999'; -- if here not good date so return false and set embargo date to long time in future |
− | |||
− | |||
end | end | ||
− | --[[--------------------------< G E T _ | + | --[[--------------------------< G E T _ S E A S O N _ N U M B E R >-------------------------------------------- |
− | |||
− | |||
− | |||
− | |||
− | returns | + | 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 |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
]] | ]] | ||
− | local function | + | local function get_season_number (season) |
− | + | local season_list = {['Winter']=21, ['Spring']=22, ['Summer']=23, ['Fall']=24, ['Autumn']=24}; -- make sure these numbers do not overlap month numbers | |
− | + | local temp; | |
− | + | temp=season_list[season]; | |
− | + | if temp then return temp; end -- if season is a valid name return its number | |
− | + | return 0; -- misspelled, improper case, or not a season name | |
− | |||
− | |||
end | end | ||
+ | --[[--------------------------< I S _ P R O P E R _ N A M E >-------------------------------------------------- | ||
− | + | returns a non-zero number if date contains a recognized proper name. Capitalization and spelling must be correct. | |
− | |||
− | returns a non-zero number if date contains a recognized proper | ||
− | |||
− | |||
]] | ]] | ||
− | local function | + | local function is_proper_name (name) |
− | + | local name_list = {['Christmas']=31} | |
− | + | local temp; | |
− | + | temp=name_list[name]; | |
− | + | if temp then return temp; end -- if name is a valid name return its number | |
− | + | return 0; -- misspelled, improper case, or not a proper name | |
− | |||
end | end | ||
+ | --[[--------------------------< I S _ V A L I D _ M O N T H _ O R _ S E A S O N >------------------------------ | ||
− | -- | + | --returns true if month or season is valid (properly spelled, capitalized, abbreviated) |
− | |||
− | returns true if month or season | ||
]] | ]] | ||
− | local function | + | 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_season_number (month_season) then -- not a month, is it a season? | |
− | + | return false; -- return false not a month or one of the five seasons | |
− | |||
− | |||
− | |||
− | if 0 | ||
− | return | ||
end | end | ||
end | end | ||
− | return | + | 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 >---------------------------------------------------- | ||
− | 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. | ||
− | |||
− | |||
]] | ]] | ||
− | + | local year_limit; | |
− | local 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; | + | year_limit = tonumber(os.date("%Y"))+1; -- global variable so we only have to fetch it once |
end | end | ||
− | + | return tonumber(year) <= year_limit; -- false if year is in the future more than one year | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | |||
--[[--------------------------< I S _ V A L I D _ D A T E >---------------------------------------------------- | --[[--------------------------< 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 | 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. | than next year; else returns false. | ||
Line 208: | Line 155: | ||
]] | ]] | ||
− | 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 | + | if not is_valid_year(year) then -- no farther into the future than next year |
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 |
− | month_length = 29; | + | month_length = 29; |
end | end | ||
else -- Gregorian calendar | 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; -- if leap year then 29 days in February | month_length = 29; -- if leap year then 29 days in February | ||
end | end | ||
end | end | ||
else | else | ||
− | month_length = days_in_month[month]; | + | month_length=days_in_month[month]; |
end | end | ||
Line 237: | Line 184: | ||
return true; | return true; | ||
end | end | ||
− | |||
--[[--------------------------< I S _ V A L I D _ M O N T H _ R A N G E _ S T Y L E >-------------------------- | --[[--------------------------< 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. | 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 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 | ||
− | |||
end | end | ||
Line 264: | Line 215: | ||
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. | ||
− | + | 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. | ||
]] | ]] | ||
− | local 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); | ||
local range_end_number; | local range_end_number; | ||
− | + | ||
if 0 == range_start_number then -- is this a month range? | if 0 == range_start_number then -- is this a month range? | ||
− | range_start_number = get_season_number (range_start | + | local range_start_number = get_season_number (range_start); -- not a month; is it a season? get start season number |
− | range_end_number = get_season_number (range_end | + | range_end_number = get_season_number (range_end); -- get end season number |
− | if | + | if 0 ~= range_start_number then -- is start of range a season? |
− | + | if range_start_number < range_end_number then -- range_start is a season | |
+ | return true; -- return true when range_end is also a season and follows start season; else false | ||
+ | end | ||
+ | if 24 == range_start_number and 21 == range_end_number then -- special case when season range is Fall-Winter or Autumn-Winter | ||
+ | return true; | ||
+ | end | ||
end | end | ||
− | return false; | + | 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 |
end | end | ||
− | + | ||
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 then -- 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? | + | if 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 | 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 310: | Line 268: | ||
The input table has: | The input table has: | ||
year, year2 – always present; if before 1582, ignore months and days if present | year, year2 – always present; if before 1582, ignore months and days if present | ||
− | month, month2 – 0 if not provided, 1-12 for months, 21-24 for seasons; | + | month, month2 – 0 if not provided, 1-12 for months, 21-24 for seasons; 31– proper name dates |
day, day2 – 0 if not provided, 1-31 for days | day, day2 – 0 if not provided, 1-31 for days | ||
the output table receives: | the output table receives: | ||
− | rftdate: an | + | rftdate: an IS8601 formatted date |
− | rftchron: a free-form version of the date, usually without year which is in rftdate (season ranges and | + | rftchron: a free-form version of the date, usually without year which is in rftdate (season ranges and propername dates) |
rftssn: one of four season keywords: winter, spring, summer, fall (lowercase) | rftssn: one of four season keywords: winter, spring, summer, fall (lowercase) | ||
− | |||
]] | ]] | ||
Line 324: | Line 281: | ||
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 | ||
− | + | ||
− | + | if 1582 > tonumber(input.year) or 20 < tonumber(input.month) then -- Julian calendar or season so &rft.date gets year only | |
− | + | date = input.year; | |
− | if | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | date = input.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 | 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', input.year, input.year2) | + | date = string.format ('%.4d/%.4d', tonumber(input.year), tonumber(input.year2)) -- assemble the date range |
end | end | ||
− | + | if 20 < tonumber(input.month) then -- if season or propername date | |
− | + | local season = {[21]='winter', [22]='spring', [23]='summer', [24]='fall', [31]='Christmas'}; -- seasons lowercase, no autumn; proper names use title case | |
− | + | if 0 == input.month2 then -- single season date | |
− | + | if 30 <tonumber(input.month) then | |
− | + | tCOinS_date.rftchron = season[input.month]; -- proper name dates | |
− | + | else | |
− | + | tCOinS_date.rftssn = season[input.month]; -- seasons | |
− | + | end | |
− | + | else -- season range with a second season specified | |
− | + | if input.year ~= input.year2 then -- season year – season year range or season year–year | |
− | + | tCOinS_date.rftssn = season[input.month]; -- start of range season; keep this? | |
− | + | if 0~= input.month2 then | |
− | if 0 ~= input.month2 then | + | tCOinS_date.rftchron = string.format ('%s %s – %s %s', season[input.month], input.year, season[input.month2], input.year2); |
− | + | end | |
+ | else -- season–season year range | ||
+ | tCOinS_date.rftssn = season[input.month]; -- start of range season; keep this? | ||
+ | tCOinS_date.rftchron = season[input.month] .. '–' .. season[input.month2]; -- season–season year range | ||
end | end | ||
− | |||
− | |||
end | end | ||
end | end | ||
− | + | tCOinS_date.rftdate = date; | |
− | tCOinS_date.rftdate = | ||
return; -- done | return; -- done | ||
end | end | ||
− | + | ||
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 381: | Line 331: | ||
tCOinS_date.rftdate = date .. date2; -- date2 has the '/' separator | tCOinS_date.rftdate = date .. date2; -- date2 has the '/' separator | ||
return; | return; | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
Line 462: | Line 342: | ||
If the date fails the format tests, this function returns false and does not return values for anchor_year and | 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 | + | 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. | its year from the year parameter if present otherwise CITEREF does not get a date value. | ||
Inputs: | Inputs: | ||
− | date_string - date string from date-holding parameters (date, year, | + | date_string - date string from date-holding parameters (date, year, accessdate, embargo, archivedate, etc.) |
Returns: | Returns: | ||
Line 476: | Line 356: | ||
]] | ]] | ||
− | local function check_date (date_string, | + | local function check_date (date_string, tCOinS_date, test_accessdate) |
− | local year; | + | 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; | + | local day2=0; -- second day in a day range |
local anchor_year; | local anchor_year; | ||
local coins_date; | local coins_date; | ||
− | if date_string:match ( | + | if date_string:match("^%d%d%d%d%-%d%d%-%d%d$") then -- year-initial numerical year month day format |
− | year, month, day = date_string | + | year, month, day=string.match(date_string, "(%d%d%d%d)%-(%d%d)%-(%d%d)"); |
if 12 < tonumber(month) or 1 > tonumber(month) or 1582 > tonumber(year) or 0 == tonumber(day) then return false; end -- month or day number not valid or not Gregorian calendar | if 12 < tonumber(month) or 1 > tonumber(month) or 1582 > tonumber(year) or 0 == tonumber(day) then return false; end -- month or day number not valid or not Gregorian calendar | ||
anchor_year = year; | anchor_year = year; | ||
− | + | ||
− | elseif | + | elseif date_string:match("^%a+ +[1-9]%d?, +[1-9]%d%d%d%a?$") then -- month-initial: month day, year |
− | month, day, anchor_year, year = | + | month, day, anchor_year, year=string.match(date_string, "(%a+)%s*(%d%d?),%s*((%d%d%d%d)%a?)"); |
month = get_month_number (month); | month = get_month_number (month); | ||
− | if 0 == month then return false; end | + | if 0 == month then return false; end -- return false if month text isn't one of the twelve months |
− | elseif mw.ustring.match(date_string, | + | elseif mw.ustring.match(date_string, "^%a+ +[1-9]%d?[%-–][1-9]%d?, +[1-9]%d%d%d%a?$") then -- month-initial day range: month day–day, year; days are separated by endash |
− | month, day, day2, anchor_year, year = mw.ustring.match(date_string, | + | month, day, day2, anchor_year, year=mw.ustring.match(date_string, "(%a+) +(%d%d?)[%-–](%d%d?), +((%d%d%d%d)%a?)"); |
− | if tonumber(day) >= tonumber(day2) then return false; end | + | if tonumber(day) >= tonumber(day2) then return false; end -- date range order is left to right: earlier to later; dates may not be the same; |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
month = get_month_number (month); | month = get_month_number (month); | ||
− | if 0 == month then return false; end | + | if 0 == month then return false; end -- return false if month text isn't one of the twelve months |
+ | month2=month; -- for metadata | ||
+ | year2=year; | ||
− | + | elseif date_string:match("^[1-9]%d? +%a+ +[1-9]%d%d%d%a?$") then -- day-initial: day month year | |
− | elseif | + | day, month, anchor_year, year=string.match(date_string, "(%d%d*)%s*(%a+)%s*((%d%d%d%d)%a?)"); |
− | |||
month = get_month_number (month); | month = get_month_number (month); | ||
− | if 0 == month then return false; end | + | if 0 == month then return false; end -- return false if month text isn't one of the twelve months |
− | |||
− | elseif mw.ustring.match(date_string, | + | elseif mw.ustring.match(date_string, "^[1-9]%d?[%-–][1-9]%d? +%a+ +[1-9]%d%d%d%a?$") then -- day-range-initial: day–day month year; days are separated by endash |
− | day, day2, month, anchor_year, year = mw.ustring.match(date_string, | + | day, day2, month, anchor_year, year=mw.ustring.match(date_string, "(%d%d?)[%-–](%d%d?) +(%a+) +((%d%d%d%d)%a?)"); |
− | if tonumber(day) >= tonumber(day2) then return false; end | + | if tonumber(day) >= tonumber(day2) then return false; end -- date range order is left to right: earlier to later; dates may not be the same; |
month = get_month_number (month); | month = get_month_number (month); | ||
− | if 0 == month then return false; end | + | if 0 == month then return false; end -- return false if month text isn't one of the twelve months |
− | month2 = month; | + | month2=month; -- for metadata |
− | year2 = year; | + | year2=year; |
− | elseif mw.ustring.match(date_string, | + | elseif mw.ustring.match(date_string, "^[1-9]%d? +%a+ [%-–] [1-9]%d? +%a+ +[1-9]%d%d%d%a?$") then -- day initial month-day-range: day month - day month year; uses spaced endash |
− | day, month, day2, month2, anchor_year, year = mw.ustring.match(date_string, | + | day, month, day2, month2, anchor_year, year=mw.ustring.match(date_string, "(%d%d?) +(%a+) [%-–] (%d%d?) +(%a+) +((%d%d%d%d)%a?)"); |
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; | 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; | ||
− | month = get_month_number (month); | + | month = get_month_number (month); -- for metadata |
month2 = get_month_number (month2); | month2 = get_month_number (month2); | ||
− | year2 = year; | + | year2=year; |
− | elseif mw.ustring.match(date_string, | + | elseif mw.ustring.match(date_string, "^%a+ +[1-9]%d? [%-–] %a+ +[1-9]%d?, +[1-9]%d%d%d?%a?$") then -- month initial month-day-range: month day – month day, year; uses spaced endash |
− | month, day, month2, day2, anchor_year, year = mw.ustring.match(date_string, | + | month, day, month2, day2, anchor_year, year=mw.ustring.match(date_string, "(%a+) +(%d%d?) [%-–] (%a+) +(%d%d?), +((%d%d%d%d)%a?)"); |
− | if (not is_valid_month_season_range(month, month2 | + | if (not is_valid_month_season_range(month, month2)) or not is_valid_year(year) then return false; end |
− | month = get_month_number (month); | + | month = get_month_number (month); -- for metadata |
month2 = get_month_number (month2); | month2 = get_month_number (month2); | ||
− | year2 = year; | + | year2=year; |
− | elseif mw.ustring.match(date_string, | + | elseif mw.ustring.match(date_string, "^[1-9]%d? +%a+ +[1-9]%d%d%d [%-–] [1-9]%d? +%a+ +[1-9]%d%d%d%a?$") then -- day initial month-day-year-range: day month year - day month year; uses spaced endash |
− | day, month, year, day2, month2, anchor_year, year2 = mw.ustring.match(date_string, | + | day, month, year, day2, month2, anchor_year, year2=mw.ustring.match(date_string, "(%d%d?) +(%a+) +(%d%d%d%d?) [%-–] (%d%d?) +(%a+) +((%d%d%d%d?)%a?)"); |
− | if tonumber(year2) <= tonumber(year) then return false; end | + | 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) 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 | 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); -- for metadata |
month2 = get_month_number (month2); | month2 = get_month_number (month2); | ||
− | |||
− | elseif mw.ustring.match(date_string, | + | elseif mw.ustring.match(date_string, "^%a+ +[1-9]%d?, +[1-9]%d%d%d [%-–] %a+ +[1-9]%d?, +[1-9]%d%d%d%a?$") then -- month initial month-day-year-range: month day, year – month day, year; uses spaced endash |
− | month, day, year, month2, day2, anchor_year, year2 = mw.ustring.match(date_string, | + | month, day, year, month2, day2, anchor_year, year2=mw.ustring.match(date_string, "(%a+) +(%d%d?), +(%d%d%d%d) [%-–] (%a+) +(%d%d?), +((%d%d%d%d)%a?)"); |
− | if tonumber(year2) <= tonumber(year) then return false; end | + | 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) 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 | 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); -- for metadata |
− | month2 = get_month_number(month2); | + | month2 = get_month_number (month2); |
− | |||
− | elseif mw.ustring.match(date_string, | + | elseif mw.ustring.match(date_string, "^%a+ +[1-9]%d%d%d[%-–]%d%d%a?$") then -- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash |
local century; | local century; | ||
− | month, year, century, anchor_year, year2 = mw.ustring.match(date_string, | + | month, year, century, anchor_year, year2=mw.ustring.match(date_string, "(%a+) +((%d%d)%d%d)[%-–]((%d%d)%a?)"); |
− | if 'Winter' ~= month and 'Summer' ~= month then return false end; | + | 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; | + | year2 = century..year2; -- add the century to year2 for comparisons |
− | if 1 ~= tonumber(year2) - tonumber(year) then return false; end | + | 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 | + | if not is_valid_year(year2) then return false; end -- no year farther in the future than next year |
− | month = get_season_number(month | + | month = get_season_number (month); |
− | elseif mw.ustring.match(date_string, | + | elseif mw.ustring.match(date_string, "^%a+ +[1-9]%d%d%d[%-–][1-9]%d%d%d%a?$") then -- special case Winter/Summer year-year; year separated with unspaced endash |
− | month, year, anchor_year, year2 = mw.ustring.match(date_string, | + | month, year, anchor_year, year2=mw.ustring.match(date_string, "(%a+) +(%d%d%d%d)[%-–]((%d%d%d%d)%a?)"); |
− | + | if 'Winter' ~= month and 'Summer' ~= month then return false end; -- 'month' can only be Winter or Summer | |
− | if | + | 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 not is_valid_year(year2) then return false; end -- no year farther in the future than next year | |
− | anchor_year = year .. '–' .. anchor_year; | + | month = get_season_number (month); -- for metadata |
− | if 1 ~= tonumber(year2) - tonumber(year) then return false; end | ||
− | if not is_valid_year(year2) then return false; end | ||
− | elseif mw.ustring.match(date_string, | + | elseif mw.ustring.match(date_string, "^%a+ +[1-9]%d%d%d +[%-–] +%a+ +[1-9]%d%d%d%a?$") then -- month/season year - month/season year; separated by spaced endash |
− | month, year, month2, anchor_year, year2 = mw.ustring.match(date_string, | + | month, year, month2, anchor_year, year2=mw.ustring.match(date_string, "(%a+) +(%d%d%d%d) +[%-–] +(%a+) +((%d%d%d%d)%a?)"); |
− | 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 | + | 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 | + | if not is_valid_year(year2) then return false; end -- no year farther in the future than next year |
if 0 ~= get_month_number(month) and 0 ~= get_month_number(month2) and is_valid_month_range_style(month, month2) then -- both must be month year, same month style | if 0 ~= get_month_number(month) and 0 ~= get_month_number(month2) and is_valid_month_range_style(month, month2) then -- both must be month year, same month style | ||
month = get_month_number(month); | month = get_month_number(month); | ||
month2 = get_month_number(month2); | month2 = get_month_number(month2); | ||
− | elseif 0 ~= get_season_number(month | + | elseif 0 ~= get_season_number(month) and 0 ~= get_season_number(month2) then -- both must be or season year, not mixed |
− | month = get_season_number(month | + | month = get_season_number(month); |
− | month2 = get_season_number(month2 | + | month2 = get_season_number(month2); |
else | else | ||
return false; | return false; | ||
end | end | ||
− | elseif mw.ustring.match(date_string, | + | elseif mw.ustring.match(date_string, "^%a+[%-–]%a+ +[1-9]%d%d%d%a?$") then -- month/season range year; months separated by endash |
− | month, month2, anchor_year, year = mw.ustring.match(date_string, | + | month, month2, anchor_year, year=mw.ustring.match(date_string, "(%a+)[%-–](%a+)%s*((%d%d%d%d)%a?)"); |
− | if (not is_valid_month_season_range(month, month2 | + | if (not is_valid_month_season_range(month, month2)) or (not is_valid_year(year)) then return false; end |
if 0 ~= get_month_number(month) then -- determined to be a valid range so just check this one to know if month or season | if 0 ~= get_month_number(month) then -- determined to be a valid range so just check this one to know if month or season | ||
month = get_month_number(month); | month = get_month_number(month); | ||
month2 = get_month_number(month2); | month2 = get_month_number(month2); | ||
− | |||
else | else | ||
− | month = get_season_number(month | + | month = get_season_number(month); |
− | month2 = get_season_number(month2 | + | month2 = get_season_number(month2); |
end | end | ||
− | year2 = year; | + | year2=year; |
− | elseif | + | elseif date_string:match("^%a+ +%d%d%d%d%a?$") then -- month/season year or proper-name year |
− | month, anchor_year, year = | + | month, anchor_year, year=date_string:match("(%a+)%s*((%d%d%d%d)%a?)"); |
if not is_valid_year(year) then return false; end | if not is_valid_year(year) then return false; end | ||
− | month = | + | if not is_valid_month_or_season (month) and 0 == is_proper_name (month) then return false; end |
− | + | if 0 ~= get_month_number(month) then -- determined to be a valid range so just check this one to know if month or season | |
+ | month = get_month_number(month); | ||
+ | elseif 0 ~= get_season_number(month) then | ||
+ | month = get_season_number(month); | ||
+ | else | ||
+ | month = is_proper_name (month); -- must be proper name; not supported in COinS | ||
+ | end | ||
− | elseif mw.ustring.match(date_string, | + | elseif mw.ustring.match(date_string, "^[1-9]%d%d%d?[%-–][1-9]%d%d%d?%a?$") 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, | + | year, anchor_year, year2=mw.ustring.match(date_string, "(%d%d%d%d?)[%-–]((%d%d%d%d?)%a?)"); |
− | 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 | ||
− | elseif mw.ustring.match(date_string, | + | elseif mw.ustring.match(date_string, "^[1-9]%d%d%d[%-–]%d%d%a?$") then -- Year range: YYYY–YY; separated by unspaced endash |
local century; | local century; | ||
− | year, century, anchor_year, year2 = mw.ustring.match(date_string, | + | year, century, anchor_year, year2=mw.ustring.match(date_string, "((%d%d)%d%d)[%-–]((%d%d)%a?)"); |
− | 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 date_string:match("^[1-9]%d%d%d?%a?$") then -- year; here accept either YYY or YYYY | |
− | + | anchor_year, year=date_string:match("((%d%d%d%d?)%a?)"); | |
− | |||
− | |||
− | elseif | ||
− | anchor_year, year = | ||
if false == is_valid_year(year) then | if false == is_valid_year(year) then | ||
return false; | return false; | ||
Line 638: | Line 507: | ||
end | end | ||
− | if | + | if test_accessdate then -- test accessdate 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; accessdate 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 accessdate out of bounds |
end | end | ||
else | else | ||
− | return false; -- return false when | + | return false; -- return false when accessdate is a range of two dates |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
end | end | ||
Line 664: | Line 520: | ||
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); |
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) | ||
− | result = is_valid_date(year, month, day); | + | result=is_valid_date(year,month,day); |
− | result = result and is_valid_date(year2, month2, day2); | + | result=result and is_valid_date(year2,month2,day2); |
end | end | ||
Line 682: | Line 538: | ||
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 | ||
− | make_COinS_date ({year = year, month = month, day = day, year2 = year2, month2 = month2, day2 = day2}, tCOinS_date); -- make an ISO 8601 date string for COinS | + | make_COinS_date ({year=year, month=month, day=day, year2=year2, month2=month2, day2=day2}, tCOinS_date); -- make an ISO 8601 date string for COinS |
end | end | ||
Line 696: | Line 552: | ||
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, | 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, | ||
− | + | a single error message is created as the dates are tested. | |
]] | ]] | ||
− | local function dates(date_parameters_list, tCOinS_date | + | local function dates(date_parameters_list, tCOinS_date) |
− | local anchor_year; | + | local anchor_year; -- will return as nil if the date being tested is not |date= |
− | local COinS_date; | + | local COinS_date; -- will return as nil if the date being tested is not |date= |
local embargo_date; -- if embargo date is a good dmy, mdy, ymd date then holds original value else reset to 9999 | local embargo_date; -- if embargo date is a good dmy, mdy, ymd date then holds original value else reset to 9999 | ||
+ | local error_message = ""; | ||
local good_date = false; | 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 | ||
− | if is_set(v | + | if is_set(v) then -- if the parameter has a value |
− | + | 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= | |
− | if v | + | local year = v:match("c%. ([1-9]%d%d%d?)%a?"); -- get the year portion so it can be tested |
− | local year = v | + | 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 | ||
good_date = is_valid_year(year); | good_date = is_valid_year(year); | ||
− | elseif 'year' == k then | + | elseif 'year'==k then |
good_date = is_valid_year(year); | good_date = is_valid_year(year); | ||
end | end | ||
− | elseif 'date' == k then -- if the parameter is |date= | + | elseif 'date'==k then -- if the parameter is |date= |
− | if v | + | 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 | + | 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 | + | elseif v:match("^nd%a?$") then -- if |date=nd with or without a CITEREF disambiguator |
− | good_date, anchor_year, COinS_date = true, v | + | 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 | + | good_date, anchor_year, COinS_date = check_date (v, tCOinS_date); -- go test the date |
end | end | ||
− | elseif 'year' == k then -- if the parameter is |year= it should hold only a year value | + | elseif 'year'==k then -- if the parameter is |year= it should hold only a year value |
− | if v | + | if v:match("^[1-9]%d%d%d?%a?$") then -- if |year= 3 or 4 digits only with or without a CITEREF disambiguator |
− | good_date, anchor_year, COinS_date = true, v | + | good_date, anchor_year, COinS_date = true, v:match("((%d+)%a?)"); |
end | end | ||
− | elseif ' | + | elseif 'access-date'==k then -- if the parameter is |date= |
− | good_date = check_date (v | + | good_date = check_date (v, nil, true); -- go test the date; nil is a placeholder; true is the test_accessdate flag |
+ | elseif 'embargo'==k then -- if the parameter is |embargo= | ||
+ | good_date = check_date (v); -- 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 | + | good_date, embargo_date = is_valid_embargo_date (v); -- is |embargo= date a single dmy, mdy, or ymd formatted date? yes:returns embargo; no: returns 9999 |
end | end | ||
else -- any other date-holding parameter | 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 -- assemble one error message so we don't add the tracking category multiple times | + | 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 -- once we've added the first portion of the error message ... | |
+ | error_message=error_message .. ", "; -- ... add a comma space separator | ||
+ | end | ||
+ | error_message=error_message .. "|" .. k .. "="; -- add the failed parameter | ||
end | end | ||
end | end | ||
end | end | ||
− | return anchor_year, embargo_date; -- and done | + | return anchor_year, embargo_date, error_message; -- and done |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
Line 773: | Line 609: | ||
--[[--------------------------< 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 returns a 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) | ||
− | |||
− | |||
− | |||
− | |||
− | |||
]] | ]] | ||
− | local function year_date_check (year_string | + | local function year_date_check (year_string, date_string) |
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 808: | Line 639: | ||
end | end | ||
− | elseif mw.ustring.match(date_string, "%d%d%d%d[%-–]%d%d") then | + | elseif mw.ustring.match(date_string, "%d%d%d%d[%-–]%d%d") then -- YYYY-YY date ranges |
local century; | local century; | ||
date1, century, date2 = mw.ustring.match(date_string, "((%d%d)%d%d)[%-–]+(%d%d)"); | date1, century, date2 = mw.ustring.match(date_string, "((%d%d)%d%d)[%-–]+(%d%d)"); | ||
Line 821: | Line 652: | ||
result = 0; | result = 0; | ||
end | end | ||
− | else | + | else |
result = 0; -- no recognizable year in date | result = 0; -- no recognizable year in date | ||
end | end | ||
+ | return result; | ||
+ | end | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | --[[-------------------------< R E F O R M A T T A B L E S >------------------------------------------------ | ||
+ | |||
+ | These table are used exclusively for reformatting dates | ||
− | + | ]] | |
− | + | local source_patterns = { -- this table holds patterns that match allowed date formats used to extract date components | |
− | + | ['dmy'] = '(%d%d?)%s+(%a+)%s+(%d%d%d%d)', | |
+ | ['mdy'] = '(%a+)%s+(%d%d?),%s+(%d%d%d%d)', | ||
+ | ['ymd'] = '(%d%d%d%d)%-(%d%d)-(%d%d)', | ||
+ | } | ||
− | + | local short_formats = { -- this table holds format strings used by os.date() for short month names | |
− | + | ['dmy'] = '%e %b %Y', | |
− | + | ['mdy'] = '%b %e, %Y', | |
− | + | ['ymd'] = '%F', | |
+ | } | ||
− | + | local long_formats = { -- this table holds format strings used by os.date() for long month names | |
− | [' | + | ['dmy'] = '%e %B %Y', |
− | + | ['mdy'] = '%B %e, %Y', | |
− | + | ['ymd'] = '%F', | |
− | + | } | |
− | |||
− | |||
− | |||
− | |||
− | + | --[[-------------------------< G E T _ D M Y _ D A T E _ P A R T S >------------------------------------------ | |
− | + | ||
− | + | extracts year, month and day from DMY formatted date, places them in the source_date table, and returns. | |
− | |||
]] | ]] | ||
− | local | + | local function get_dmy_date_parts (date, source_date) |
− | + | source_date.day, source_date.month, source_date.year = date:match (source_patterns['dmy']); -- get date components as strings | |
− | + | source_date.month = get_month_number (source_date.month); -- get month number | |
− | + | end | |
− | + | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | --[[-------------------------< G E T _ M D Y _ D A T E _ P A R T S >------------------------------------------ | ||
− | + | extracts year, month and day from MDY formatted date, places them in the source_date table, and returns. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | ]] | |
− | |||
− | |||
− | + | local function get_mdy_date_parts (date, source_date) | |
− | + | source_date.month, source_date.day, source_date.year = date:match (source_patterns['mdy']); -- get date components as strings | |
− | + | source_date.month = get_month_number (source_date.month); -- get month number | |
− | + | end | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | --[[-------------------------< G E T _ Y M D _ D A T E _ P A R T S >------------------------------------------ | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | extracts year, month and day from YMD formatted date, places them in the source_date table, and returns. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | ]] | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | local function get_ymd_date_parts (date, source_date) | |
+ | source_date.year, source_date.month, source_date.day = date:match (source_patterns['ymd']); -- get date components as strings | ||
end | end | ||
Line 1,000: | Line 721: | ||
--[[-------------------------< R E F O R M A T _ D A T E S >-------------------------------------------------- | --[[-------------------------< R E F O R M A T _ D A T E S >-------------------------------------------------- | ||
− | Reformats existing dates into the format specified by format | + | Reformats existing dates into the format specified by format and short. |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | format is one of several keywords: dmy, dmy-all, mdy, mdy-all, ymd, ymd-all. The all version includes access- and | |
− | + | archive-dates; otherwise these dates are not reformatted | |
− | |||
− | dates | + | Date ranges, season dates, proper name dates are not currently supported. |
− | |||
− | |||
]] | ]] | ||
− | local function reformat_dates (date_parameters_list, format) | + | local function reformat_dates (date_parameters_list, format, short) |
local all = false; -- set to false to skip access- and archive-dates | local all = false; -- set to false to skip access- and archive-dates | ||
− | local | + | local format_str; |
− | + | local source_date = {}; | |
− | local | ||
− | |||
− | if format:match('%a+%-all') then | + | if format:match('%a+%-all') then |
format = format:match('(%a+)%-all'); -- extract the format | format = format:match('(%a+)%-all'); -- extract the format | ||
− | all = true; -- | + | all = true; -- set to true to format access- and archive-dates |
− | + | end | |
− | + | ||
− | + | for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list | |
− | + | if is_set(param_val) then -- if the parameter has a value | |
− | + | if not all and in_array (param_name, {'access-date', 'archive-date'}) then -- if access- or archive-date and format not xxx-all | |
− | + | param_val = ''; -- set to empty string so we don't process this date | |
− | + | end | |
+ | for source, pattern in pairs(source_patterns) do | ||
+ | if param_val:match(pattern) then | ||
+ | if 'ymd' == source then | ||
+ | get_ymd_date_parts (param_val, source_date); -- get the date parts into the source_date table | ||
+ | elseif 'dmy' == source then | ||
+ | get_dmy_date_parts (param_val, source_date); -- get the date parts into the source_date table | ||
+ | elseif 'mdy' == source then | ||
+ | get_mdy_date_parts (param_val, source_date); -- get the date parts into the source_date table | ||
+ | end | ||
+ | |||
+ | if 'ymd' == format and 1582 > tonumber(source_date.year) then -- ymd format dates not allowed before 1582 | ||
+ | return false; -- abandon reformatting | ||
+ | end | ||
+ | |||
+ | if short then | ||
+ | format_str = short_formats[format]; | ||
+ | else | ||
+ | format_str = long_formats[format]; | ||
+ | end | ||
+ | -- convert date and save; | ||
+ | date_parameters_list[param_name] = mw.text.trim (os.date (format_str, os.time(source_date))); -- strip leading space when single digit day and %e is first format | ||
+ | end | ||
+ | end | ||
end | end | ||
− | end | + | end |
− | + | return true; -- declare success and done | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
Line 1,084: | Line 787: | ||
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 | + | if not param_val:match ('%d%d%d%d%-%d%d%-%d%d') then -- for those that are not ymd dates |
− | + | param_val, n = param_val:gsub ('%-', '–'); -- replace any hyphen with ndash | |
− | + | if 0 ~= n then | |
− | + | date_parameters_list[param_name] = param_val; -- update the list | |
− | + | result = true; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
end | end | ||
end | end | ||
− | + | return result; -- so we know if | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
Line 1,163: | Line 805: | ||
]] | ]] | ||
− | local function set_selected_modules ( | + | local function set_selected_modules (utilities_page_ptr) |
− | + | is_set = utilities_page_ptr.is_set; -- import functions from select Module:Citation/CS1/Utilities module | |
− | + | in_array = utilities_page_ptr.in_array; -- import functions from select Module:Citation/CS1/Utilities module | |
− | in_array = utilities_page_ptr.in_array; | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | |||
− | |||
return { -- return exported functions | return { -- return exported functions | ||
− | |||
− | |||
− | |||
dates = dates, | dates = dates, | ||
+ | year_date_check = year_date_check, | ||
reformat_dates = reformat_dates, | reformat_dates = reformat_dates, | ||
− | + | date_hyphen_to_dash = date_hyphen_to_dash, | |
− | + | set_selected_modules = set_selected_modules | |
− | |||
} | } |