Module:Citation/CS1/Date validation: Difference between revisions

    m>Trappist the monk
    (Synch from sandbox;)
    m>Trappist the monk
    (Synch from sandbox;)
    Line 97: Line 97:
    return true;
    return true;
    end
    end


    --[[--------------------------< I S _ V A L I D _ Y E A R >----------------------------------------------------
    --[[--------------------------< I S _ V A L I D _ Y E A R >----------------------------------------------------
    Line 112: Line 111:
    end
    end


    --[[
    --[[--------------------------< I S _ V A L I D _ D A T E >----------------------------------------------------
    Returns true if day is less than or equal to the number of days in month and year is no farther into the future than next year; else returns false.
    Returns true if day is less than or equal to the number of days in month and year is no farther into the future
    than next year; else returns false.
     
    Assumes Julian calendar prior to year 1582 and Gregorian calendar thereafter. Accounts for Julian calendar leap
    years before 1582 and Gregorian leap years after 1582. Where the two calendars overlap (1582 to approximately
    1923) dates are assumed to be Gregorian.


    Assumes Julian calendar prior to year 1582 and Gregorian calendar thereafter. Accounts for Julian calendar leap years before 1582 and Gregorian leap years after 1582.
    Where the two calendars overlap (1582 to approximately 1923) dates are assumed to be Gregorian.
    ]]
    ]]
    local function is_valid_date (year, month, day)
    local function is_valid_date (year, month, day)
    local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    local month_length;
    local month_length;
    if not is_valid_year(year) then -- no farther into the future than next year
    if not is_valid_year(year) then -- no farther into the future than next year
    return false;
    return false;
    end
    end
    if (2==month) then -- if February
    month = tonumber(month); -- required for YYYY-MM-DD dates
    month_length = 28; -- then 28 days unless
    if 1582 > tonumber(year) then -- Julian calendar
    if (2==month) then -- if February
    month_length = 28; -- then 28 days unless
    if 1582 > tonumber(year) then -- Julian calendar
    if 0==(year%4) then
    if 0==(year%4) then
    month_length = 29;
    month_length = 29;
    end
    end
    else -- 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[tonumber(month)];
    month_length=days_in_month[month];
    end
    end


    Line 213: Line 218:
    This function receives a table of date parts for one or two dates and an empty table reference declared in
    This function receives a table of date parts for one or two dates and an empty table reference declared in
    Module:Citation/CS1.  The function is called only for |date= parameters and only if the |date=<value> is  
    Module:Citation/CS1.  The function is called only for |date= parameters and only if the |date=<value> is  
    determined to be a valid date format.  The question of what to do with invlaid date formats is not answered here.
    determined to be a valid date format.  The question of what to do with invalid date formats is not answered here.


    The date parts in the input table are converted to an ISO 8601 conforming date string:
    The date parts in the input table are converted to an ISO 8601 conforming date string:
    Line 296: Line 301:
    --[[--------------------------< C H E C K _ D A T E >----------------------------------------------------------
    --[[--------------------------< C H E C K _ D A T E >----------------------------------------------------------


    Check date format to see that it is one of the formats approved by WP:DATESNO or WP:DATERANGE. Exception: only allowed range separator is endash.
    Check date format to see that it is one of the formats approved by WP:DATESNO or WP:DATERANGE. Exception: only
    Additionally, check the date to see that it is a real date: no 31 in 30-day months; no 29 February when not a leap year.  Months, both long-form and three
    allowed range separator is endash. Additionally, check the date to see that it is a real date: no 31 in 30-day
    character abbreviations, and seasons must be spelled correctly. Future years beyond next year are not allowed.
    months; no 29 February when not a leap year.  Months, both long-form and three character abbreviations, and seasons
    must be spelled correctly. Future years beyond next year are not allowed.


    If the date fails the format tests, this function returns false and does not return values for anchor_year and COinS_date.  When this happens, the date parameter is
    If the date fails the format tests, this function returns false and does not return values for anchor_year and
    used in the COinS metadata and the CITEREF identifier gets its year from the year parameter if present otherwise CITEREF does not get a date value.
    COinS_date.  When this happens, the date parameter is used in the COinS metadata and the CITEREF identifier gets
    its year from the year parameter if present otherwise CITEREF does not get a date value.


    Inputs:
    Inputs:
    Line 310: Line 317:
    true, anchor_year, COinS_date
    true, anchor_year, COinS_date
    anchor_year can be used in CITEREF anchors
    anchor_year can be used in CITEREF anchors
    COinS_date is date_string without anchor_year disambiguator if any -- this is being obsoleted.  In future:
    COinS_date is ISO 8601 format date; see make_COInS_date()
    COinS_date is ISO 8601 format date; see make_COInS_date()
    ]]
    ]]
    local function check_date (date_string, tCOinS_date)
    local function check_date (date_string, tCOinS_date)
    local year; -- assume that year2, months, and days are not used;
    local year; -- assume that year2, months, and days are not used;
    Line 415: Line 423:
    end
    end


    elseif date_string:match ("^%a+–%a+ +[1-9]%d%d%d%a?$") then -- month/season range year; months separated by endash  
    elseif date_string:match ("^%a+–%a+ +[1-9]%d%d%d%a?$") then -- month/season range year; months separated by endash  
    month, month2, anchor_year, year=date_string:match ("(%a+)–(%a+)%s*((%d%d%d%d)%a?)");
    month, month2, anchor_year, year=date_string:match ("(%a+)–(%a+)%s*((%d%d%d%d)%a?)");
    if (not is_valid_month_season_range(month, month2)) or (not is_valid_year(year)) then return false; end
    if (not is_valid_month_season_range(month, month2)) or (not is_valid_year(year)) then return false; end
    Line 461: Line 469:


    else
    else
    return false; -- date format not one of the MOS:DATE approved formats
    return false; -- date format not one of the MOS:DATE approved formats
    end
    end


    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);
    Line 482: Line 490:
    if false == result then return false; end
    if false == result then return false; end
    -- if here, then date_string is valid; get coins_date from date_string (leave CITEREF disambiguator) ...
    -- coins_date=date_string:match("^(.+%d)%a?$"); -- last character of valid disambiguatable date is always a digit
    -- coins_date= mw.ustring.gsub(coins_date, "–", "-" ); -- ... and replace any ndash with a hyphen


    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
    Line 490: Line 495:
    end
    end
    return true, anchor_year; -- format is good and date string represents a real date
    return true, anchor_year; -- format is good and date string represents a real date
    -- return true, anchor_year, coins_date; -- format is good and date string represents a real date
    end
    end


    --[[--------------------------< D A T E S >--------------------------------------------------------------------
    --[[--------------------------< D A T E S >--------------------------------------------------------------------
    Line 509: Line 514:
    local COinS_date; -- will return as nil if the date being tested is not |date=
    local COinS_date; -- will return as nil if the date being tested is not |date=
    local error_message = "";
    local error_message = "";
    -- local mismatch = 0;
    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) then -- if the parameter has a value
    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:match("^c%. [1-9]%d%d%d?%a?$") then -- special case for c. year or with or without CITEREF disambiguator - only |date= and |year=
    local year = v:match("c%. ([1-9]%d%d%d?)%a?"); -- get the year portion so it can be tested
    local year = v:match("c%. ([1-9]%d%d%d?)%a?"); -- get the year portion so it can be tested
    if 'date'==k then
    if 'date'==k then
    anchor_year, COinS_date = v:match("((c%. [1-9]%d%d%d?)%a?)"); -- anchor year and COinS_date only from |date= parameter
    anchor_year, COinS_date = v:match("((c%. [1-9]%d%d%d?)%a?)"); -- anchor year and COinS_date only from |date= parameter
    Line 522: Line 526:
    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:match("^n%.d%.%a?") then -- if |date=n.d. with or without a CITEREF disambiguator
    if v:match("^n%.d%.%a?") then -- if |date=n.d. with or without a CITEREF disambiguator
    good_date, anchor_year, COinS_date = true, v:match("((n%.d%.)%a?)"); --"n.d."; no error when date parameter is set to no date
    good_date, anchor_year, COinS_date = true, v:match("((n%.d%.)%a?)"); --"n.d."; no error when date parameter is set to no date
    elseif v:match("^nd%a?$") then -- if |date=nd with or without a CITEREF disambiguator
    elseif v:match("^nd%a?$") then -- if |date=nd with or without a CITEREF disambiguator
    good_date, anchor_year, COinS_date = true, v:match("((nd)%a?)"); --"nd"; no error when date parameter is set to no date
    good_date, anchor_year, COinS_date = true, v:match("((nd)%a?)"); --"nd"; no error when date parameter is set to no date
    else
    else
    good_date, anchor_year, COinS_date = check_date (v, tCOinS_date); -- go test the date
    good_date, anchor_year, COinS_date = check_date (v, tCOinS_date); -- go test the date
    end
    end
    elseif 'access-date'==k then -- if the parameter is |date=
    elseif 'access-date'==k then -- if the parameter is |date=
    good_date = check_date (v); -- go test the date
    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 = is_valid_accessdate (v); -- is Wikipedia start date < accessdate < tomorrow's date?
    good_date = is_valid_accessdate (v); -- is Wikipedia start date < accessdate < tomorrow's date?
    end
    end
    else -- any other date-holding parameter
    else -- any other date-holding parameter
    good_date = check_date (v); -- go test the date
    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 ...
    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
    error_message=error_message .. ", "; -- ... add a comma space separator
    end
    end
    error_message=error_message .. "&#124;" .. k .. "="; -- add the failed parameter
    error_message=error_message .. "&#124;" .. k .. "="; -- add the failed parameter
    end
    end
    end
    end
    end
    end
    -- return anchor_year, COinS_date, error_message, mismatch; -- and done
    return anchor_year, error_message; -- and done
    return anchor_year, error_message; -- and done
    end
    end


    --[[--------------------------< Y E A R _ D A T E _ C H E C K >------------------------------------------------
    --[[--------------------------< Y E A R _ D A T E _ C H E C K >------------------------------------------------