變更
無編輯摘要
local p = {}
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------]] local is_set, in_array; -- imported functions from selected Module:Citation/CS1/Utilitieslocal cfg; -- table of tables imported from slected Module:Citation/CS1/Configuration --[=[-------------------------< I S _ V A L I D _ A C C E S S D A T E >----------------------------------------
returns true if:
adding 24 hours gives 2015-01-03T00:00:00 – one second more than tomorrow
This function does not work if it is fed month names for languages other than English. Wikimedia #time: parserapparently doesn't understand non-Engish date month names. This function will always return false when the datecontains a non-English month name because good1 is false after the call to lang.formatDate(). To get around thatcall this function with YYYY-MM-DD format dates. ]=]
local function is_valid_accessdate (accessdate)
local access_ts, tomorrow_ts; -- to hold unix time stamps representing the dates
good1, access_ts = pcall( lang.formatDate, lang, 'U', accessdate ); -- convert accessdate value to unix timesatmp
good2, tomorrow_ts = pcall( lang.formatDate, lang, 'U', 'today + 2 days' ); -- today midnight + 2 days is one second more than all day tomorrow
if good1 and good2 then -- lang.formatDate() returns a timestamp in the local script which which tonumber() may not understand access_ts = tonumber (access_ts) or lang:parseFormattedNumber (access_ts); -- convert to numbers for the comparison; tomorrow_ts = tonumber (tomorrow_ts) or lang:parseFormattedNumber (tomorrow_ts);
else
return false; -- one or both failed to convert to unix time stamp
end
if 979516800 <= access_ts and access_ts < tomorrow_ts then -- Wikipedia start date <= accessdate < tomorrow's date
return true;
end
end
--[[--------------------------< G E T _ M O N T H _ N U M B E R >----------------------------------------------
local function get_month_number (month)
end
--[[--------------------------< 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 |embargo= is
set, the editor intended to embargo a pmc but |embargo= does not hold a single date.
]]
local function is_valid_embargo_date (v)
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
return false, '9999'; -- if here not good date so return false and set embargo date to long time in future
end
--[[--------------------------< G E T _ S E A S O N _ N U M B E R >--------------------------------------------
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
Uses ISO DIS 8601 2016 part 2 §4.7 Divisions of a year for hemishpere-independent seasons:
21-24 = Spring, Summer, Autumn, Winter, independent of “Hemisphere”
These additional divisions not currently supported:
25-28 = Spring - Northern Hemisphere, Summer- Northern Hemisphere, Autumn - Northern Hemisphere, Winter - Northern Hemisphere
29-32 = Spring – Southern Hemisphere, Summer– Southern Hemisphere, Autumn – Southern Hemisphere, Winter - Southern Hemisphere
33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each)
37-39 = Quadrimester 1, Quadrimester 2, Quadrimester 3 (4 months each)
40-41 = Semestral 1, Semestral-2 (6 months each)
]]
local function get_season_number (season)
end
local function is_proper_name (name)
end
]]
local year_limit;
local function is_valid_year(year)
if not is_set(year_limit) then
Month pairs are expected to be left to right, earliest to latest in time.
]]
local function is_valid_month_season_range(range_start, range_end)
local range_start_number = get_month_number (range_start);
local range_end_number;
if 0 == range_start_number then -- is this a month range?
if (0 ~= range_start_number ) and (0 ~= range_end_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 any season range pairing is Fall-Winter or Autumn-Winter return true; endaccepted
end
return false; -- range_start is not a month or a season; and/or range_start is a season and range_end is not; or improper a season sequence
end
-- here when range_start is a month local range_end_number = get_month_number (range_end); -- get end month number
if range_start_number < range_end_number then -- range_start is a month; does range_start precede range_end?
if is_valid_month_range_style (range_start, range_end) then -- do months have the same style?
The input table has:
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; 31– proper name dates99 Christmas
day, day2 – 0 if not provided, 1-31 for days
local date; -- one date or first date in a range
local date2 = ''; -- end of range date
-- start temporary Julian / Gregorian calendar uncertainty detection
local year = tonumber(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 day = tonumber (input.day);
if (0 ~= day) and -- day must have a value for this to be a whole date
(((1582 == year) and (10 <= month) and (12 >= month)) or -- any whole 1582 date from 1 october to 31 December or
((1926 == year) and (1 == month) and (1 == input.day)) or -- 1 January 1926 or
((1582 < year) and (1925 >= year))) then -- any date 1 January 1583 – 31 December 1925
tCOinS_date.inter_cal_cat = true; -- set category flag true
end
-- end temporary Julian / Gergorian calendar uncertainty detection
if 1582 > tonumber(input.year) or 20 < tonumber(input.month) then -- Julian calendar or season so &rft.date gets year only
end
if 20 < tonumber(input.month) then -- if season or propername date
local season = {[2124]='winter', [2221]='spring', [2322]='summer', [2423]='fall', [3199]='Christmas'}; -- seasons lowercase, no autumn; proper names use title case
if 0 == input.month2 then -- single season date
if 30 <tonumber(input.month) then
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
tCOinS_date.rftchron = string.format ('%s %s – %s %s', season[input.month], input.year, season[input.month2], input.year2);
end
]]
local function check_date (date_string, tCOinS_date, test_accessdate)
local year; -- assume that year2, months, and days are not used;
local year2=0; -- second year in a year range
if date_string:match("^%d%d%d%d%-%d%d%-%d%d$") then -- year-initial numerical year month day format
year, month, day=string.match(date_string, "(%d%d%d%d)%-(%d%d)%-(%d%d)");
if 12 < tonumber(month) or 1 > tonumber(month) or 1583 1582 > tonumber(year) or 0 == tonumber(day) then return false; end -- month or day number not valid or not Gregorian calendar
anchor_year = year;
elseif date_string:mw.ustring.match(date_string, "^%a+ D- +[1-9]%d?, +[1-9]%d%d%d%a?$") then -- month-initial: month day, year month, day, anchor_year, year=stringmw.ustring.match(date_string, "(%aD-) +)%s*(%d%d?),%s*((%d%d%d%d?)%a?)");
month = get_month_number (month);
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
elseif date_string:mw.ustring.match(date_string, "^%a+ D- +[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=stringmw.ustring.match(date_string, "(%a+D-) +(%d%d?) [%- –](%d%d?), +((%d%d%d%d)%a?)");
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);
year2=year;
elseif date_string:mw.ustring.match(date_string, "^[1-9]%d? +%a+ D- +[1-9]%d%d%d%a?$") then -- day-initial: day month year day, month, anchor_year, year=stringmw.ustring.match(date_string, "(%d%d*)%s*(%aD-) +)%s*((%d%d%d%d?)%a?)");
month = get_month_number (month);
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
elseif date_string:mw.ustring.match(date_string, "^[1-9]%d? [%- –][1-9]%d? +%a+ D- +[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=stringmw.ustring.match(date_string, "(%d%d?) [%- –](%d%d?) +(%a+D-) +((%d%d%d%d)%a?)");
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);
year2=year;
elseif date_string:mw.ustring.match(date_string, "^[1-9]%d? +%aD- + [%- – ] +[1-9]%d? +%a+ D- +[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=date_string:mw.ustring.match(date_string, "(%d%d?) +(%aD-) +) [%- – ] +(%d%d?) +(%a+D-) +((%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;
month = get_month_number (month); -- for metadata
year2=year;
elseif date_string:mw.ustring.match(date_string, "^%a+ D- +[1-9]%d? +[%- – ] +%a+ D- +[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=date_string:mw.ustring.match(date_string, "(%a+D-) +(%d%d?) +[%- – ] +(%a+D-) +(%d%d?), +((%d%d%d%d)%a?)");
if (not is_valid_month_season_range(month, month2)) or not is_valid_year(year) then return false; end
month = get_month_number (month); -- for metadata
year2=year;
elseif date_string:mw.ustring.match(date_string, "^[1-9]%d? +%a+ D- +[1-9]%d%d%d +[%- – ] +[1-9]%d? +%a+ D- +[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=date_string:mw.ustring.match(date_string, "(%d%d?) +(%a+D-) +(%d%d%d%d?) +[%- – ] +(%d%d?) +(%a+D-) +((%d%d%d%d?)%a?)");
if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later
if 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
month2 = get_month_number (month2);
elseif date_string:mw.ustring.match(date_string, "^%a+ D- +[1-9]%d?, +[1-9]%d%d%d +[%- – ] +%a+ D- +[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=date_string:mw.ustring.match(date_string, "(%a+D-) +(%d%d?), +(%d%d%d%d) +[%- – ] +(%a+D-) +(%d%d?), +((%d%d%d%d)%a?)");
if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later
if 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
month2 = get_month_number (month2);
elseif date_string:mw.ustring.match(date_string, "^%a+ D- +[1-9]%d%d%d [%- –]%d%d%a?$") then -- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash
local century;
month, year, century, anchor_year, year2=date_string:mw.ustring.match(date_string, "(%a+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
anchor_year=year..'–'..anchor_year; -- assemble anchor_year from both years
month = get_season_number (month);
elseif date_string:mw.ustring.match(date_string, "^%a+ D- +[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=date_string:mw.ustring.match(date_string, "(%a+D-) +(%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
anchor_year=year..'–'..anchor_year; -- assemble anchor_year from both years
month = get_season_number (month); -- for metadata
elseif date_string:mw.ustring.match(date_string, "^%a+ D- +[1-9]%d%d%d+[% - – ] +%a+ D- +[1-9]%d%d%d%a?$") then -- month/season year - month/season year; separated by spaced endash month, year, month2, anchor_year, year2=date_string:mw.ustring.match(date_string, "(%a+D-) +(%d%d%d%d) +[%- – ] +(%a+D-) +((%d%d%d%d)%a?)");
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
end
elseif date_string:mw.ustring.match (date_string, "^%a+ D-[%- –]%a+ D- +[1-9]%d%d%d%a?$") then -- month/season range year; months separated by endash month, month2, anchor_year, year=date_string:mw.ustring.match (date_string, "(%a+D-) [%- –](%a+D-)%s*((%d%d%d%d)%a?)");
if (not is_valid_month_season_range(month, month2)) or (not is_valid_year(year)) then return false; end
if 0 ~= get_month_number(month) then -- determined to be a valid range so just check this one to know if month or season
year2=year;
elseif date_string:mw.ustring.match(date_string, "^%a+ D- +%d%d%d%d%a?$") then -- month/season year or proper-name year month, anchor_year, year=date_string:mw.ustring.match(date_string, "(%a+D-)%s*((%d%d%d%d)%a?)");
if not is_valid_year(year) then return false; end
if not is_valid_month_or_season (month) and 0 == is_proper_name (month) then return false; end
end
elseif date_string: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=date_string:mw.ustring.match(date_string, "(%d%d%d%d?) [%- –]((%d%d%d%d?)%a?)");
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 not is_valid_year(year2) then return false; end -- no year farther in the future than next year
elseif date_string:mw.ustring.match(date_string, "^[1-9]%d%d%d [%- –]%d%d%a?$") then -- Year range: YYYY–YY; separated by unspaced endash
local century;
year, century, anchor_year, year2=date_string:mw.ustring.match(date_string, "((%d%d)%d%d) [%- –]((%d%d)%a?)");
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
end
else return false; -- LOCAL: do date format not use mw.ustringone of the MOS: it allows full-width characters for %d.DATE approved formats elseif date_string:match("^[1-9]%d%d%d年[1-9]%d?月[1-9]%d?日$") then -- zh: year month day year, month, day=date_string:match("(%d%d%d%d)年(%d%d*月)(%d%d*)日"); month = get_month_number (month); if 0 == month then return false; end anchor_year = year;
end
end
local anchor_year; -- will return as nil if the date being tested is not |date=
local COinS_date; -- will return as nil if the date being tested is not |date=
local 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;
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=
v = mw.ustring.gsub (v, '%d', cfg.date_names.local_digits);
local year = v:match("c%. ([1-9]%d%d%d?)%a?"); -- get the year portion so it can be tested
if 'date'==k then
end
elseif 'date'==k then -- if the parameter is |date=
if v:match("^n%.d%.%a?$") then -- if |date=n.d. with or without a CITEREF disambiguator
good_date, anchor_year, COinS_date = true, v:match("((n%.d%.)%a?)"); --"n.d."; no error when date parameter is set to no date
elseif v:match("^nd%a?$") then -- if |date=nd with or without a CITEREF disambiguator
else
good_date, anchor_year, COinS_date = check_date (v, tCOinS_date); -- go test the date
end
elseif 'year'==k then -- if the parameter is |year= it should hold only a year value
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:match("((%d+)%a?)");
end
elseif 'access-date'==k then -- if the parameter is |date=
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
good_date , embargo_date = is_valid_accessdate is_valid_embargo_date (v); -- is Wikipedia start |embargo= date < accessdate < tomorrow's a single dmy, mdy, or ymd formatted date?yes:returns embargo; no: returns 9999
end
else -- any other date-holding parameter
end
end
return anchor_year, embargo_date, error_message; -- and done
end
year = year_string:match ('(%d%d%d%d?)');
if date_string:match ('%d%d%d%d%-%d%d%-%d%d') and year_string:match ('%d%d%d%d%a') then --special case where both date and year are required YYYY-MM-DD and YYYYx
date1 = date_string:match ('(%d%d%d%d)');
year = year_string:match ('(%d%d%d%d)');
end
elseif date_string:match ("%d%d%d%d?.-%d%d%d%d?") then -- any of the standard range formats of date with two three- or four-digit years
date1, date2 = date_string:match ("(%d%d%d%d?).-(%d%d%d%d?)");
if year ~= date1 and year ~= date2 then
end
elseif date_string:mw.ustring.match (date_string, "%d%d%d%d[%s%-–]+%d%d") then -- YYYY-YY date ranges
local century;
date1, century, date2 = date_string:mw.ustring.match (date_string, "((%d%d)%d%d)[%s%-–]+(%d%d)");
date2 = century..date2; -- convert YY to YYYY
if year ~= date1 and year ~= date2 then
result = 0;
end
else
result = 0; -- no recognizable year in date
end
return result;
end