Модуль:Calendar: различия между версиями

Материал из FetbukWiki
https://ru.wikipedia.org/wiki/>Ping08
м (Защитил страницу Модуль:Calendar: критический шаблон или модуль: более 220000 включений ([Редактирование=только администраторы] (бессрочно) [Переименование=только администраторы] (бессрочно)))
м (141 версия импортирована)
 
(не показано 29 промежуточных версий 4 участников)
Строка 3: Строка 3:
local getArgs = require('Module:Arguments').getArgs
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local yesno = require('Module:Yesno')
local lang = mw.getContentLanguage()
local mwlang = mw.getContentLanguage()
local err = "―" -- NthDay nil result
local err = "―" -- NthDay nil result
local tCon = table.concat


-- 00) Блок многократно используемых списков
-- 00) Блок многократно используемых списков
Строка 14: Строка 15:
local params = { {"г", "g"}, {"ю", "j"}}
local params = { {"г", "g"}, {"ю", "j"}}
local comment = { '<span style="border-bottom: 1px dotted; cursor: help" title="по юлианскому календарю">','</span>'}
local comment = { '<span style="border-bottom: 1px dotted; cursor: help" title="по юлианскому календарю">','</span>'}
local category = {["params"]="<!--[[Категория:Модуль:calendar:Страницы с некорректными параметрами]]-->"}


-- duplicates:
-- AST, BST, CST, ECT, IST, MST, PST, SST,
local known_tzs = {
local known_tzs = {
   ACDT='+10:30', ACST='+09:30', ACT ='+08:00', ADT  ='-03:00', AEDT ='+11:00',
   ACDT='+10:30', ACST='+09:30', ACT ='+08:00', ADT  ='-03:00', AEDT ='+11:00',
Строка 41: Строка 43:
   SST ='+08:00', TAHT='-10:00', THA ='+07:00', UTC  ='+00:00', UYST ='-02:00',
   SST ='+08:00', TAHT='-10:00', THA ='+07:00', UTC  ='+00:00', UYST ='-02:00',
   UYT ='-03:00', VET ='-04:30', VLAT='+10:00', WAT  ='+01:00', WEDT ='+01:00',
   UYT ='-03:00', VET ='-04:30', VLAT='+10:00', WAT  ='+01:00', WEDT ='+01:00',
   WEST='+01:00', WET ='+00:00', YAKT='+09:00', YEKT ='+05:00', MSK  ='+03:00',
   WEST='+01:00', WET ='+00:00', YAKT='+09:00', YEKT ='+05:00',
   -- US Millitary (for RFC-822)
   -- US Millitary (for RFC-822)
   Z='+00:00', A='-01:00', M='-12:00', N='+01:00', Y='+12:00',
   Z='+00:00', A='-01:00', M='-12:00', N='+01:00', Y='+12:00',
}
}
local category = {
["no_parameters"]=
"<!--[[Категория:Модуль:Calendar:Страницы без параметров]]-->",
["incomplete_parameters"]=
"<!--[[Категория:Модуль:Calendar:Страницы с неполными или некорректными параметрами]]-->",
["without_verification"]=
"<!--[[Категория:Модуль:Calendar:Страницы без проверки параметров]]-->",
["erroneous_parameters"]=
"<!--[[Категория:Модуль:Calendar:Страницы с ошибочными параметрами]]-->"
}
-- несколько параметров передаются вместе с кодом ошибки в таблице, один может быть передан простым значением
local e = {
["start"]="<span class=error>Ошибка: ",
["ending"]=".</span>",
["no_pattern_match"]="строка «%s» не совпадает с заданными паттернами",
["no_valid_date"]="дата «%s» не является корректной",
["wrong_jd"]="юлианская дата %s вне диапазона",
["no_data"]="нет входящих данных",
["too_many_arguments"]="ожидается менее %i аргументов",
["too_little_arguments"]="ожидается более %i аргументов",
["wrong_calculation"]="даты %s и %s не прошли проверку, %s дней разница",
["unknown_param"]="параметр %s неизвестен",
["unknown_error"]="неизвестная ошибка",
["tech_error"]="ошибка в функции %s",
["box_date"]="строка «%s» не является верной датой, пожалуйста, укажите дату в формате ГГГГ-ММ-ДД"
-- [""]="",
}


local tzs_names = {"ACDT","ACST","ACT","ADT","AEDT","AEST","AFT","AKDT","AKST",
local tzs_names = {"ACDT","ACST","ACT","ADT","AEDT","AEST","AFT","AKDT","AKST",
Строка 57: Строка 88:
"SAST","SBT","SCT","SLT","SST","SST","TAHT","THA","UTC","UYST","UYT","VET",
"SAST","SBT","SCT","SLT","SST","SST","TAHT","THA","UTC","UYST","UYT","VET",
"VLAT","WAT","WEDT","WEST","WET","YAKT","YEKT","Z","A","M","N","Y","MSK"}
"VLAT","WAT","WEDT","WEST","WET","YAKT","YEKT","Z","A","M","N","Y","MSK"}
local pattern = { -- для распознавания дат, переданных одним строчным параметром
{"(-?%d%d%d%d?)[-%.%s/\\](%d%d)[-%.%s/\\](%d%d)",  ["order"] = {3,2,1} },  -- yyyy mm dd
{"(%d+)[-%.%s/\\](%d+)[-%.%s/\\](%d%d%d%d?)", ["order"] = {1,2,3} }, -- dd mm yyyy
{"(%d%d)[-%.%s/\\](%d%d%d%d?)", ["order"] = {2,3} }, -- mm yyyy
{"(%d%d%d%d?)[-%.%s/\\](%d%d)", ["order"] = {3,2} }, -- yyyy mm
{"(%d+)%s(%l+)%s(%d%d%d%d?)", ["order"] = {1,2,3} }, -- d mmm y
{"(%l+)%s(%d+),?%s(%d%d%d%d?)", ["order"] = {2,1,3} }, -- mmm d, y
{"(%l+)%s(%d%d%d%d?)", ["order"] = {2,3} }, -- mmm y
}
local time_units = {"year","month","day"} --не используется
--[[ local time_units = {"second", "minute", "hour",
    "day_of_month", "day_of_week", "day_of_year",
    "week", "month", "year", "year_of_century", "century"} ]]--
-- напоминание чтобы сделать более точные пересчёты - с часами / расчёт длительностей периодов
local mnlang = {"ru_G", "ru_N", "en", "de", "fr"}
local month_lang = {
["ru_G"] = {"января","февраля","марта","апреля","мая","июня",
"июля","августа","сентября","октября","ноября","декабря"},
["ru_N"] = {"январь","февраль","март","апрель","май","июнь",
"июль","август","сентябрь","октябрь","ноябрь","декабрь"},
["en"] = {"january", "february", "march", "april", "may", "june",
"july", "august", "september", "october", "november", "december"},
["de"] = {"januar", "februar", "märz", "april", "mai", "juni",
"juli", "august", "september", "oktober", "november", "dezember"},
["fr"] = {"janvier", "février", "mars", "avril", "mai", "juin",
"juillet", "août", "septembre", "octobre", "novembre", "décembre"}
}
-- заполняется автоматически
local reverse_month_lang = {}
-- вспомогательная функция для обращения таблиц (смена ключей со значениями)
local reverse_table = function (strait_table)
local reversed_table = {}
for k,v in pairs(strait_table) do
reversed_table[v] = k
end
return reversed_table
end
-- запуск цикла по заполнению обратных таблиц, необходимых для распознавания дат
local filling_months = function (mnlang, month_lang)
for i=1, #mnlang do
reverse_month_lang[mnlang[i]] = reverse_table(month_lang[mnlang[i]])
end
end


-- 10) Блок общих функций
-- 10) Блок общих функций
Строка 83: Строка 163:


local function init(num)
local function init(num)
output = {}
local output = {}
for i=1,num do
for i=1,num do
table.insert(output, {["year"]="", ["month"]="", ["day"]=""})
table.insert(output, {["year"]="", ["month"]="", ["day"]=""})
Строка 99: Строка 179:


local function inbord(val, down, up)
local function inbord(val, down, up)
if type(up) ~= "number" or type(down) ~= "number" or type(val) ~= "number" or up < down or val < down or val > up then
return not (type(up) ~= "number" or type(down) ~= "number" or type(val) ~= "number" or up < down or val < down or val > up)
return false
    else
        return true
    end
end
end


function shallowcopy(orig)
local function shallowcopy(orig)
     local orig_type = type(orig)
     local orig_type = type(orig)
     local copy
     local copy
Строка 130: Строка 206:


-- 20) Блок общих проверочных функций, связанных с датами
-- 20) Блок общих проверочных функций, связанных с датами
local function unwarp(tbl)
if not tbl then return ""
elseif type(tbl) ~= "table" then return tbl
elseif (tbl.day or tbl.month or tbl.year) then
return (tbl.year or "?").."-"..(tbl.month or "?").."-"..(tbl.day or "?")
else return (tbl[3] or "?").."-"..(tbl[2] or "?").."-"..(tbl[1] or "?")
end
end


local function leap_year(y,jul)
local function leap_year(y,jul)
Строка 142: Строка 226:
end
end


function isdate ( chain , jul ) -- можно использовать для проверки таблиц с полями day, month, year
-- функция для вычисления последнего дня месяца для юлианского и григорианского календарей
local function month_end_day (month,year,is_julian)
local month_end_day = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -- если не задан год, дата 29 февраля считается допустимой
if not month or type(month) ~= "number" or month < 1 or month > 12 then return nil
elseif month ~= 2 or not year then return month_end_day[month]
elseif month == 2 and (year % 4) == 0 and not ((not is_julian) and (year % 100 == 0 and year % 400 ~= 0)) then return 29
elseif month == 2 then return 28
else return nil -- в случае не целого значения входящих параметров или при иных непредусмотренных событиях
end
end
 
local function isdate ( chain , jul ) -- можно использовать для проверки таблиц с полями day, month, year
if not chain then return false
if not chain then return false
elseif (not type(chain) == "table")
elseif (not type(chain) == "table")
Строка 217: Строка 312:
end
end


-- функция для нормализации значений дат и перевода месяцев в числа
local function numerize(str)
    if type(str) == "number" then
        return math.floor(str)
elseif str == "" or str == nil or type(str) ~= "string" then
return nil
    elseif type(tonumber(str)) == "number" then
        return math.floor(tonumber(str))
    else
    for i=1, #mnlang do
    if inlist(mw.ustring.lower(str),month_lang[mnlang[i]]) then
return reverse_month_lang[mnlang[i]][mw.ustring.lower(str)]
end
    end
    end
end
-- функция распознавания даты, переданной одной строкой
local function parse_date(date_string)
if type(date_string) ~= "string" or date_string == "" then return nil end
local out_date_str = {"","",""}
local error_data = {}
for i=1, #pattern do
local result_1, result_2, result_3 = mw.ustring.match(mw.ustring.lower(date_string),pattern[i][1])
if (result_1 or "") > "" then
out_date_str[pattern[i].order[1]] = result_1
    out_date_str[pattern[i].order[2]] = result_2
    if (pattern[i].order[3]) then out_date_str[pattern[i].order[3]] = result_3 end
    -- mw.log("Паттерн " .. i .. ", строка: " .. date_string)
    break
end
end
local date = {
["day"]  =numerize(out_date_str[1]),
["month"]=numerize(out_date_str[2]),
["year"] =numerize(out_date_str[3])}
return date --, error_data
end
----[[ УСТАРЕЛО ]]----
----[[ УСТАРЕЛО ]]----
local numstr2date = function(numstr)
local numstr2date = function(numstr)
local lang = mw.getContentLanguage()
local format = "Y-m-d"
local format = "Y-m-d"
local iso_date = lang:formatDate(format,numstr)
local iso_date = mwlang:formatDate(format,numstr)
     local y,m,d = string.match(iso_date, "(%d+)-(%d+)-(%d+)")
     local y,m,d = string.match(iso_date, "(%d+)-(%d+)-(%d+)")
local dateout = {["year"]=purif(y), ["month"]=purif(m), ["day"]=purif(d)}
local dateout = {["year"]=purif(y), ["month"]=purif(m), ["day"]=purif(d)}
Строка 241: Строка 373:
--        dateout = {["year"]=nums[3], ["month"]=nums[2], ["day"]=nums[1]}
--        dateout = {["year"]=nums[3], ["month"]=nums[2], ["day"]=nums[1]}
--    else
--    else
-- local lang = mw.getContentLanguage()
-- local mwlang = mw.getContentLanguage()
-- implement lang:formatDate(format,datein,true) here
-- implement mwlang:formatDate(format,datein,true) here
--        return error("Не распознано " .. numstr .. " как дата")
--        return error("Не распознано " .. numstr .. " как дата")
--    end
--    end
Строка 256: Строка 388:
else numyear = 1 - numyear end
else numyear = 1 - numyear end
if wiki then  
if wiki then  
-- output = table.concat({'[[', numyear,' год',bcmark,'|', numyear,']]', " ", yearmark, " ", bcmark})
-- output = tCon({'[[', numyear,' год',bcmark,'|', numyear,']]', " ", yearmark, " ", bcmark})
output = table.concat({'[[', numyear,' год',bcmark,'|', trim(numyear .. " " .. yearmark .. " " .. bcmark), ']]'})
output = tCon({'[[', numyear,' год',bcmark,'|', trim(numyear .. " " .. yearmark .. " " .. bcmark), ']]'})
else
else
output = table.concat({numyear, " ", yearmark, bcmark})
output = tCon({numyear, " ", yearmark, bcmark})
end
end
return trim(output)
return trim(output)
Строка 267: Строка 399:
-- if not isdate(wikidate) then wiki = false end
-- if not isdate(wikidate) then wiki = false end
if not ispartdate(datein) then return "" end
if not ispartdate(datein) then return "" end
local dm_separ, output = ""
local dm_separ, output = "", nil
if (not (not datein.day)) and (not (not datein.month)) then dm_separ = " " end
if (not (not datein.day)) and (not (not datein.month)) then dm_separ = " " end
if (not datein.month) then datein.month = "" end
if (not datein.month) then datein.month = "" end
Строка 273: Строка 405:
local monlan = monthlang[datein.month] or ""
local monlan = monthlang[datein.month] or ""
if wiki and not inner_brt then
if wiki and not inner_brt then
output = table.concat({"[[", wikidate.day, " ", monthlang[wikidate.month] or "",
output = tCon({"[[", wikidate.day, " ", monthlang[wikidate.month] or "",
"|", (datein.day or ""), dm_separ, monlan, "]]"})
"|", (datein.day or ""), dm_separ, monlan, "]]"})
elseif wiki then
elseif wiki then
output = table.concat({"[[", wikidate.day, " ", monthlang[wikidate.month] or "",
output = tCon({"[[", wikidate.day, " ", monthlang[wikidate.month] or "",
"|", (datein.day or ""), dm_separ, monlan})
"|", (datein.day or ""), dm_separ, monlan})
else
else
output = table.concat({datein.day, dm_separ, monlan})
output = tCon({datein.day, dm_separ, monlan})
end
end
     return trim(output)
     return trim(output)
Строка 292: Строка 424:
local day = purif((d or "-"):match("(%d+)"))
local day = purif((d or "-"):match("(%d+)"))
if not month then  
if not month then  
msg = category["params"]
msg = category.incomplete_parameters
month = purif(month_to_num[string.lower(mw.ustring.match((d or ""),"(%a+)") or "-")])  
month = purif(month_to_num[string.lower(mw.ustring.match((d or ""),"(%a+)") or "-")])  
end
end
if (not day) and ((purif(string.match(m or "","(%d+)") or "") or 32) <= (monthd[month] or 31)) then  
if (not day) and ((purif(string.match(m or "","(%d+)") or "") or 32) <= (monthd[month] or 31)) then  
msg = category["params"]
msg = category.incomplete_parameters
day = purif(m:match("(%d+)") or "")  
day = purif(m:match("(%d+)") or "")  
end
end
if not year then  
if not year then  
msg = category["params"]
msg = category.incomplete_parameters
year = purif(string.match(m or "","(%d+)") or "")  
year = purif(string.match(m or "","(%d+)") or "")  
end
end
Строка 309: Строка 441:
local function glue(d1,m1,y1,d2,m2,y2)
local function glue(d1,m1,y1,d2,m2,y2)
if (not d1) and (not m1) and (not y1) and (not d2) and (not m2) and (not y2) then
if (not d1) and (not m1) and (not y1) and (not d2) and (not m2) and (not y2) then
return category["params"] end
return category.incomplete_parameters end
local gd,gm,gy,jd,jm,jy =  
local gd,gm,gy,jd,jm,jy =  
(d1 or ""),
(d1 or ""),
Строка 317: Строка 449:
(m2 or ""),
(m2 or ""),
(y2 or "")
(y2 or "")
--mw.log(table.concat({gd,gm,gy,jd,jm,jy}))
--mw.log(tCon({gd,gm,gy,jd,jm,jy}))
local gm_sep = {" [["," год|","]]"}
local gm_sep = {" [["," год|","]]"}
if (not gy) or (gy == "") then gm_sep = {"","",""} end
if (not gy) or (gy == "") then gm_sep = {"","",""} end
return table.concat({comment[1],trim(trim(jd .. " " .. jm) .. " " .. jy ),
return tCon({comment[1],trim(trim(jd .. " " .. jm) .. " " .. jy ),
comment[2]," ([[",trim(gd .. " " .. gm),"]]",gm_sep[1],(gy:match("(%d+)") or ""),
comment[2]," ([[",trim(gd .. " " .. gm),"]]",gm_sep[1],(gy:match("(%d+)") or ""),
gm_sep[2],gy,gm_sep[3],")",category["params"]})
gm_sep[2],gy,gm_sep[3],")",category.incomplete_parameters})
end
end


Строка 342: Строка 474:
if jd.year == gd.year then
if jd.year == gd.year then
cd.year = gd.year
cd.year = gd.year
gd.year, jd.year = nil
gd.year, jd.year = nil, nil
end
end
if jd.month == gd.month then
if jd.month == gd.month then
cd.month = gd.month
cd.month = gd.month
gd.month, jd.month = nil
gd.month, jd.month = nil, nil
end
end
if (not not cd.month) and wm then  
if (not not cd.month) and wm then  
return table.concat({comment[1] .. trim(day2lang(jd,jdate,false) .. " " .. year2lang(jd.year,yearmark,false)) .. comment[2],  
return tCon({comment[1] .. trim(day2lang(jd,jdate,false) .. " " .. year2lang(jd.year,yearmark,false)) .. comment[2],  
trim(left .. day2lang(gd,gdate,wd,wm) .. " " .. year2lang(gd.year,yearmark,wy)) .. right,  
trim(left .. day2lang(gd,gdate,wd,wm) .. " " .. year2lang(gd.year,yearmark,wy)) .. right,  
day2lang(cd,gdate,false) .. "]]", trim(year2lang(cd.year,yearmark,wy)..msg)}, " ")
day2lang(cd,gdate,false) .. "]]", trim(year2lang(cd.year,yearmark,wy)..msg)}, " ")
end  
end  
return table.concat({comment[1] .. trim(day2lang(jd,jdate,false) .. " " .. year2lang(jd.year,yearmark,false)) .. comment[2],  
return tCon({comment[1] .. trim(day2lang(jd,jdate,false) .. " " .. year2lang(jd.year,yearmark,false)) .. comment[2],  
trim(left .. day2lang(gd,gdate,wd) .. " " .. year2lang(gd.year,yearmark,wy)) .. right,  
trim(left .. day2lang(gd,gdate,wd) .. " " .. year2lang(gd.year,yearmark,wy)) .. right,  
trim(day2lang(cd,gdate,false)), trim(year2lang(cd.year,yearmark,wy)..msg)}, " ")
trim(day2lang(cd,gdate,false)), trim(year2lang(cd.year,yearmark,wy)..msg)}, " ")
Строка 360: Строка 492:
-- 40) Блок функций для перевода дат с использованием [[Юлианская дата]]
-- 40) Блок функций для перевода дат с использованием [[Юлианская дата]]


function gri2jd( datein )
local function gri2jd( datein )
if not isdate(datein) then return error((datein.day or "") .. "." .. (datein.month or "") .."." .. (datein.year or "") .. " неподходящая дата") end
if not isdate(datein) then return error((datein.day or "") .. "." .. (datein.month or "") .."." .. (datein.year or "") .. " неподходящая дата") end
     local year = datein.year
     local year = datein.year
Строка 379: Строка 511:
end
end


function jd2jul( jd )
local function jd2jul( jd )
if type(jd) ~= "number" then return error("Промежуточная переменная " .. (jd or "") .. " не является числом") end
if type(jd) ~= "number" then return error("Промежуточная переменная " .. (jd or "") .. " не является числом") end
     -- calendar date calculation
     -- calendar date calculation
Строка 394: Строка 526:
end
end


function jul2jd( datein )
local function jul2jd( datein )
if not isdate(datein,true) then return error((datein.day or "") .. "." .. (datein.month or "") ..".".. (datein.year or "") .. " неподходящая дата") end
if not isdate(datein,true) then return error((datein.day or "") .. "." .. (datein.month or "") ..".".. (datein.year or "") .. " неподходящая дата") end
     local year = datein.year
     local year = datein.year
Строка 413: Строка 545:
end
end


function jd2gri( jd )
local function jd2gri( jd )
if type(jd) ~= "number" then return error("Промежуточная переменная " .. (jd or "") .. " не является числом") end
if type(jd) ~= "number" then return error("Промежуточная переменная " .. (jd or "") .. " не является числом") end
     -- calendar date calculation
     -- calendar date calculation
Строка 430: Строка 562:
end
end


function astroyear(num, bc)
local function astroyear(num, bc)
if not num then return error()
if not num then return error()
elseif type(num) ~= "number" then return error()
elseif type(num) ~= "number" then return error()
Строка 440: Строка 572:
end
end


function recalc(datein,calend)
local function recalc(datein,calend)
if inlist(calend,params[1]) then  
if inlist(calend,params[1]) then  
return jd2jul(gri2jd(datein)), datein
return jd2jul(gri2jd(datein)), datein
   elseif inlist(calend,params[2]) then
   elseif inlist(calend,params[2]) then
return datein, jd2gri(jul2jd(datein))
return datein, jd2gri(jul2jd(datein))
   else error("Параметр " .. (calend or "") .. " не опознан, разрешённые: " .. table.concat(params[1]," ") .. " и " .. table.concat(params[2]," "))
   else error("Параметр " .. (calend or "") .. " не опознан, разрешённые: " .. tCon(params[1]," ") .. " и " .. tCon(params[2]," "))
   end
   end
end
end
Строка 458: Строка 590:
local cat = ""
local cat = ""
local nums = {}
local nums = {}
local hmarg, timedec = 0
local hmarg, timedec = 0, 0
local mmarg = "00"
local mmarg = "00"
local output = ""
local output = ""
Строка 552: Строка 684:
end
end


function p.ToIso( frame ) -- возможно неиспользуемая
-- =p.ToIso(mw.getCurrentFrame():newChild{title="smth",args={"12 декабря 2020"}})
-- =p.ToIso(mw.getCurrentFrame():newChild{title="smth",args={"1.2.1602"}})
-- =p.ToIso(mw.getCurrentFrame():newChild{title="smth",args={"12.12.2021"}})
-- =p.ToIso(mw.getCurrentFrame():newChild{title="smth",args={"2021.12.12"}})
function p.ToIso( frame )  
     local args = getArgs(frame, { frameOnly = true })
     local args = getArgs(frame, { frameOnly = true })
     local datein = args[1]
     local datein = args[1]
    -- инициализация, заполнение обратных таблиц, копирование параметров
filling_months(mnlang, month_lang)
     -- парсинг входящей даты по шаблону
     -- парсинг входящей даты по шаблону
     local pattern = "(%d+)%.(%d+)%.(%d+)"
     local date = parse_date(datein)
    local dayin, monthin, yearin = datein:match(pattern)
     if not (type(date.year) == 'number') then  
    local year = tonumber(yearin)
         return ("Wrong year: " .. unwarp(date))
    local month = tonumber(monthin)
    local day = tonumber(dayin)
    -- проверка параметров
     if not (type(year) == 'number') then  
         return error("Wrong year")
     end
     end
     if not (1 <= month and month <= 12) then  
     if not (1 <= date.month and date.month <= 12) then  
         return error("Wrong month")
         return ("Wrong month: " .. unwarp(date))
     end
     end
     if not (1 <= day and day <= 31) then  
     if not date.day or not (1 <= date.day and date.day <= month_end_day(date.month,date.year)) then  
         return error("Wrong day")
         return ("Wrong day: " .. unwarp(date))
     end
     end
     local timedate = os.time{year=year, month=month, day=day}
     local timedate = os.time{year=date.year, month=date.month, day=date.day}
     local date = os.date("%Y-%m-%d", timedate)
     local date = os.date("%Y-%m-%d", timedate)
     return date
     return date
end
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"12 декабря 2020"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"1.2.1602"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"декабрь 2020"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"12-2020"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"12.12.2021"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"2021.12.12"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"2021.11"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"11.2021"}})
function p.BoxDate( frame )
    local args = getArgs(frame, { frameOnly = true })
    local txtDateIn, strFormat  = args[1], args[2]
    local txtDateOut, date, status = p.bxDate(txtDateIn, strFormat, params)
if status.brk then
return error(status.errorText)
else
return txtDateOut
end
end
function p.bxDate( txtDateIn , strFormat, params ) -- к отладке
local txtDateOut, date, status = "", {}, {brk = false, errorCat = "", errorText = ""}
strFormat = strFormat or "j xg Y"
-- заглушка - таблица параметров на будущее
params = params or {}
if not txtDateIn then
status.errorText = e.no_data
status.errorCat = category.no_parameters
status.brk = true
else
-- заполнение служебных таблиц
filling_months(mnlang, month_lang)
end
if not status.brk then
-- парсинг входящей даты по шаблону
date = parse_date(txtDateIn)
    -- заменить сообщения об ошибках на списочные
    if not (date.year and type(date.year) == 'number') then
    status.errorText = string.format(e.box_date,txtDateIn)
    status.errorCat = category.incomplete_parameters
    status.brk = true
    end
    if not inbord(date.month,1,12) then
    status.errorText = string.format(e.box_date,txtDateIn)
    status.errorCat = category.incomplete_parameters
    status.brk = true
    end
    if not date.day and string.find(strFormat,"[dDjlNwzW]") then
    strFormat = trim(string.gsub(string.gsub(strFormat,"xg","F"),"[dDjlNwzW]",""))
    elseif not date.day then
    elseif not inbord(date.day,1,month_end_day(date.month,date.year)) then
    status.errorText = string.format(e.box_date,txtDateIn)
    status.errorCat = category.incomplete_parameters
    status.brk = true
    end
end
if not status.brk then
txtDateOut = mwlang:formatDate(strFormat,tCon({date.year,date.month,date.day},"-"),true)
end
    return txtDateOut, date, status
end
end


function p.ToDate( frame ) -- возможно неиспользуемая
function p.ToDate( frame ) -- возможно неиспользуемая
     local args = getArgs(frame, { frameOnly = true })
     local args = getArgs(frame, { frameOnly = true })
     local lang = mw.getContentLanguage()
     local mwlang = mw.getContentLanguage()
     local datein = args[1]
     local datein = args[1]
     local format = "j xg Y"
     local format = "j xg Y"
Строка 585: Строка 779:
     else format = args[2]
     else format = args[2]
     end
     end
     return lang:formatDate(format,datein,true)
     return mwlang:formatDate(format,datein,true)
end
end


Строка 628: Строка 822:
     local args = getArgs(frame, { frameOnly = true })
     local args = getArgs(frame, { frameOnly = true })
     if not args[1] then return err end
     if not args[1] then return err end
     local gdate, jdate = {}
     local gdate, jdate = {}, {}
     local strin = args[1]  
     local strin = args[1]  
     local cal = args[2]:lower() or "г"
     local cal = args[2]:lower() or "г"
Строка 669: Строка 863:
end
end
local cal = "г"
if (not args[2]) or (args[2] == "") then cal = "г"
else cal = args[2]:lower() end
local bc,wd,wm,wy,sq_brts =  
local bc,wd,wm,wy,sq_brts =  
is(args["bc"]),
is(args["bc"]),
Строка 675: Строка 873:
is(args["wy"]),
is(args["wy"]),
is(args["sq_brts"])
is(args["sq_brts"])
 
year = astroyear(purif(year),bc)
year = astroyear(purif(year),bc)
local datein = {["year"]=purif(year), ["month"]=purif(month), ["day"]=purif(day)}


local jdate = {["year"]=purif(year), ["month"]=purif(month), ["day"]=purif(day)}
local jdate, gdate = recalc(datein,cal)
local gdate = jd2gri(jul2jd(jdate))


     local yearmark = "года"
     local yearmark = "года"
Строка 690: Строка 888:
     else yearmark = trim(ym) or "года" end
     else yearmark = trim(ym) or "года" end
     end
     end
   
 
return double_couple(jdate, gdate, wd, wm, wy, sq_brts, yearmark)
return double_couple(jdate, gdate, wd, wm, wy, sq_brts, yearmark)
end
end
Строка 797: Строка 995:
mw.log("g1date " .. (undate(g1date ) or ""))
mw.log("g1date " .. (undate(g1date ) or ""))
mw.log("g2date " .. (undate(g2date ) or ""))
mw.log("g2date " .. (undate(g2date ) or ""))
return err .. category["params"]
return err .. category.incomplete_parameters
end
end
end
end


return p
return p

Текущая версия от 18:30, 19 марта 2022

Функции

Находится в бета-версии (42 273 байт). Об ошибках просьба сообщать на страницу обсуждения шаблона или самого модуля, или Carn. Если вы хотите поэкспериментировать, лучше делать это в альфа-версии (30 288 байт).

NthDay

{{ДатыСтрокой}}, {{Даты}}

  • Получает 4 числовых аргумента, считает дату и выдаёт её в формате пятого, необязательного аргумента. Примеры использования (значения аргументов в скобках):
    • первое (1) воскресенье (0) октября (10) (2020) года ={{#invoke:Calendar|NthDay|1|0|10|2020}}= 04.10.20
    • вторая (2) среда (3) мая (5) (2019) года ={{#invoke:Calendar|NthDay|2|3|5|2019}}= 08.05.19
    • последний (-1) понедельник (1) января (1) (2010) года ={{#invoke:Calendar|NthDay|-1|1|1|2010}}= 25.01.10
    • предпоследняя (-2) суббота (6) декабря (12) (2001) года ={{#invoke:Calendar|NthDay|-2|6|12|2001}}= 22.12.01
    • третье (3) воскресенье (0) марта (3) (2024) года в формате ISO 8601={{#invoke:Calendar|NthDay|3|0|3|2024|%Y-%m-%d}}= 2024-03-17

unitime

{{НП/Формат времени}}

OldDate

устарело

  • Два обязательных аргумента, первый из которых — дата в формате ДД.ММ.ГГГГ или Д. М.ГГГГ, второй — григорианский/юлианский календарь, «г» или «ю»
  • Необязательные аргументы bc (до нашей эры), а также параметры викификации wd, wm и wy, связанные, соответственно с вифификацией дня, месяца и года
  • Можно использовать параметр sq_brts для использования квадратных скобок и параметр yearmark для нестандартного обозначения года
    • {{#invoke:Calendar|OldDate|1.1.1|ю|wd=1}} = 19 мая (1 июня) 2024 года ошибка!
    • {{#invoke:Calendar|OldDate|31.12.1|г|bc=1}} = Ошибка Lua: bad argument #2 to 'formatDate' (not a valid timestamp).
    • {{#invoke:Calendar|OldDate|{{#time: d.m.Y }}|г}} = 6 (19) мая 2024 года
    • {{#invoke:Calendar|OldDate|11.2.1602|j|wd=1|wm=0|wy=1}} = 11 (21) февраля 1602 года
    • {{#invoke:Calendar|OldDate|11.2.1602|j|wd=1|wm=1|wy=1}} = 11 (21) февраля 1602 года
    • {{#invoke:Calendar|OldDate|11.2.1602|g|bc=1|yearmark=г.}} = 25 (11) февраля 1602 г. до н. э.
    • {{#invoke:Calendar|OldDate|11.2.1602|g|sq_brts=1|yearmark=0}} = 1 [11] февраля 1602

NewDate

{{DateStyle}}

  • Аналогично функции выше, но может обрабатывать отрицательные даты и принимает жёстко только 2 формата d.m.y и y-m-d
    • {{#invoke:Calendar|NewDate|1.1.1|ю|wd=1}} = 1 января 1 (30 декабря 1 до н. э.)
    • {{#invoke:Calendar|NewDate|31.12.1|г|bc=1}} = 2 января 1 (31 декабря 1 до н. э.)
    • {{#invoke:Calendar|NewDate|{{#time: d.m.Y }}}} = 6 (19) мая 2024 (по умолчанию григорианский)
    • {{#invoke:Calendar|NewDate|11.2.1602|j|wd=1|wm=0|wy=1}} = 11 (21) февраля 1602
    • {{#invoke:Calendar|NewDate|11.2.1602|j|wd=1|wm=1|wy=1}} = 11 (21) февраля 1602
    • {{#invoke:Calendar|NewDate|11.2.1602|g|bc=1|yearmark=г.}} = 25 (11) февраля 1602 г. до н. э.
    • {{#invoke:Calendar|NewDate|11.2.1602|g|sq_brts=1|yearmark=0}} = 1 [11] февраля 1602

ToIso

  • Получает полную дату дату в форматах с четырёхзначным годом и выдаёт дату в формате ГГГГ-ММ-ДД
    • 1.2.1602 ={{#invoke:Calendar|ToIso|1.2.1602}}= 1602-02-01
    • -2020-12-12 ={{#invoke:Calendar|ToIso|-2020-12-12}}= -2020-12-12
    • 5 января 1002 ={{#invoke:Calendar|ToIso|5 января 1002}}= 1002-01-05

BoxDate

  • Получает дату с четырёхзначным годом, месяцем и опционально днём месяца, выдаёт читаемую
    • 06.1280 ={{#invoke:Calendar|BoxDate|06.1280}}= июнь 1280
    • 1820-07 ={{#invoke:Calendar|BoxDate|1820-07}}= июль 1820
    • 2020-12, xg Y = {{#invoke:Calendar|BoxDate|2020-12|xg Y}}= декабря 2020
    • 08.08.1828 ={{#invoke:Calendar|BoxDate|08.08.1828}}= 8 августа 1828
    • July 12, 2020 ={{#invoke:Calendar|BoxDate|Jule 12, 2020}}= 12 июля 2020
    • 12 July 2020 ={{#invoke:Calendar|BoxDate|12 Jule 2020}}= 12 июля 2020
    • July 2020 ={{#invoke:Calendar|BoxDate|Jule 2020}}= июль 2020
    • 13 août 1281, l W недели Y года ={{#invoke:Calendar|BoxDate|13 août 1281|l W недели Y года}}= среда 33 недели 1281 года
    • 13 января ={{#invoke:Calendar|BoxDate|13 января}}= Ошибка Lua на строке 724: строка «13 января» не является верной датой, пожалуйста, укажите дату в формате ГГГГ-ММ-ДД.
bxDate

Реализует указанные выше функции для вызова из других модулей (см. пример использования в Message box), todo:

  • поддержка отрицательных лет (запоминание знака, обработка в положительном виде, приделывание "до н.э." в конце; 0000-01-01 невалидно)
  • преобразование даты в ISO формат, получение строки форматирования и преобразование по ней (необходимы доп.проверки для неточных дат)
  • отдавать параметры errorText и errorCat

ToDate

  • Получает дату в формате Википедия:Функции парсера##time и возвращает в формате <число> <месяц в родительном падеже> <год>
  • Если в строке нету символов препинания, то возвращает её неизменённой
    • 1.2.1602 ={{#invoke:Calendar|ToDate|1.2.1602}}= 1 февраля 1602
    • 1/2/1602 ={{#invoke:Calendar|ToDate|1/2/1602}}= 2 января 1602
    • 1602-02-01 ={{#invoke:Calendar|ToDate|1602-02-01}}= 1 февраля 1602
    • 1 февраля 1602 ={{#invoke:Calendar|ToDate|1 февраля 1602}}= 1 февраля 1602
    • Завтра (+ 1 day) ={{#invoke:Calendar|ToDate|+ 1 day}}= 21 мая 2024

local p = {}
-- Необходимые модули и переменные
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local mwlang = mw.getContentLanguage()
local err = "―" -- NthDay nil result
local tCon = table.concat

-- 00) Блок многократно используемых списков
local bool_to_number={ [true]=1, [false]=0 }
local monthlang = {"января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"}
local month_to_num = {["января"]=1,["февраля"]=2,["марта"]=3,["апреля"]=4,["мая"]=5,["июня"]=6,
	["июля"]=7,["августа"]=8,["сентября"]=9,["октября"]=10,["ноября"]=11,["декабря"]=12,["-"]=""}
local monthd = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
local params = { {"г", "g"}, {"ю", "j"}}
local comment = { '<span style="border-bottom: 1px dotted; cursor: help" title="по юлианскому календарю">','</span>'}

-- duplicates:
-- AST, BST, CST, ECT, IST, MST, PST, SST, 
local known_tzs = {
   ACDT='+10:30', ACST='+09:30', ACT ='+08:00', ADT  ='-03:00', AEDT ='+11:00',
   AEST='+10:00', AFT ='+04:30', AKDT='-08:00', AKST ='-09:00', AMST ='+05:00',
   AMT ='+04:00', ART ='-03:00', AST ='+03:00', AST  ='+04:00', AST  ='+03:00',
   AST ='-04:00', AWDT='+09:00', AWST='+08:00', AZOST='-01:00', AZT  ='+04:00',
   BDT ='+08:00', BIOT='+06:00', BIT ='-12:00', BOT  ='-04:00', BRT  ='-03:00',
   BST ='+06:00', BST ='+01:00', BTT ='+06:00', CAT  ='+02:00', CCT  ='+06:30',
   CDT ='-05:00', CEDT='+02:00', CEST='+02:00', CET  ='+01:00', CHAST='+12:45',
   CIST='-08:00', CKT ='-10:00', CLST='-03:00', CLT  ='-04:00', COST ='-04:00',
   COT ='-05:00', CST ='-06:00', CST ='+08:00', CVT  ='-01:00', CXT  ='+07:00',
   CHST='+10:00', DFT ='+01:00', EAST='-06:00', EAT  ='+03:00', ECT  ='-04:00',
   ECT ='-05:00', EDT ='-04:00', EEDT='+03:00', EEST ='+03:00', EET  ='+02:00',
   EST ='-05:00', FJT ='+12:00', FKST='-03:00', FKT  ='-04:00', GALT ='-06:00',
   GET ='+04:00', GFT ='-03:00', GILT='+12:00', GIT  ='-09:00', GMT  ='+00:00',
   GST ='-02:00', GYT ='-04:00', HADT='-09:00', HAST ='-10:00', HKT  ='+08:00',
   HMT ='+05:00', HST ='-10:00', IRKT='+08:00', IRST ='+03:30', IST  ='+05:30',
   IST ='+01:00', IST ='+02:00', JST ='+09:00', KRAT ='+07:00', KST  ='+09:00',
   LHST='+10:30', LINT='+14:00', MAGT='+11:00', MDT  ='-06:00', MIT  ='-09:30',
   MSD ='+04:00', MSK ='+03:00', MST ='+08:00', MST  ='-07:00', MST  ='+06:30',
   MUT ='+04:00', NDT ='-02:30', NFT ='+11:30', NPT  ='+05:45', NST  ='-03:30',
   NT  ='-03:30', OMST='+06:00', PDT ='-07:00', PETT ='+12:00', PHOT ='+13:00',
   PKT ='+05:00', PST ='-08:00', PST ='+08:00', RET  ='+04:00', SAMT ='+04:00',
   SAST='+02:00', SBT ='+11:00', SCT ='+04:00', SLT  ='+05:30', SST  ='-11:00',
   SST ='+08:00', TAHT='-10:00', THA ='+07:00', UTC  ='+00:00', UYST ='-02:00',
   UYT ='-03:00', VET ='-04:30', VLAT='+10:00', WAT  ='+01:00', WEDT ='+01:00',
   WEST='+01:00', WET ='+00:00', YAKT='+09:00', YEKT ='+05:00',
   -- US Millitary (for RFC-822)
   Z='+00:00', A='-01:00', M='-12:00', N='+01:00', Y='+12:00',
}

local category = {
	["no_parameters"]=
	"<!--[[Категория:Модуль:Calendar:Страницы без параметров]]-->",
	["incomplete_parameters"]=
	"<!--[[Категория:Модуль:Calendar:Страницы с неполными или некорректными параметрами]]-->",
	["without_verification"]=
	"<!--[[Категория:Модуль:Calendar:Страницы без проверки параметров]]-->",
	["erroneous_parameters"]=
	"<!--[[Категория:Модуль:Calendar:Страницы с ошибочными параметрами]]-->"
}

-- несколько параметров передаются вместе с кодом ошибки в таблице, один может быть передан простым значением
local e = {
	["start"]="<span class=error>Ошибка: ",
	["ending"]=".</span>",
	["no_pattern_match"]="строка «%s» не совпадает с заданными паттернами",
	["no_valid_date"]="дата «%s» не является корректной",
	["wrong_jd"]="юлианская дата %s вне диапазона",
	["no_data"]="нет входящих данных",
	["too_many_arguments"]="ожидается менее %i аргументов",
	["too_little_arguments"]="ожидается более %i аргументов",
	["wrong_calculation"]="даты %s и %s не прошли проверку, %s дней разница",
	["unknown_param"]="параметр %s неизвестен",
	["unknown_error"]="неизвестная ошибка",
	["tech_error"]="ошибка в функции %s",
	["box_date"]="строка «%s» не является верной датой, пожалуйста, укажите дату в формате ГГГГ-ММ-ДД"
--	[""]="",
	}

local tzs_names = {"ACDT","ACST","ACT","ADT","AEDT","AEST","AFT","AKDT","AKST",
"AMST","AMT","ART","AST","AST","AST","AST","AWDT","AWST","AZOST","AZT","BDT",
"BIOT","BIT","BOT","BRT","BST","BST","BTT","CAT","CCT","CDT","CEDT","CEST",
"CET","CHAST","CIST","CKT","CLST","CLT","COST","COT","CST","CST","CVT","CXT",
"CHST","DFT","EAST","EAT","ECT","ECT","EDT","EEDT","EEST","EET","EST","FJT",
"FKST","FKT","GALT","GET","GFT","GILT","GIT","GMT","GST","GYT","HADT","HAST",
"HKT","HMT","HST","IRKT","IRST","IST","IST","IST","JST","KRAT","KST","LHST",
"LINT","MAGT","MDT","MIT","MSD","MSK","MST","MST","MST","MUT","NDT","NFT",
"NPT","NST","NT","OMST","PDT","PETT","PHOT","PKT","PST","PST","RET","SAMT",
"SAST","SBT","SCT","SLT","SST","SST","TAHT","THA","UTC","UYST","UYT","VET",
"VLAT","WAT","WEDT","WEST","WET","YAKT","YEKT","Z","A","M","N","Y","MSK"}

local pattern = { -- для распознавания дат, переданных одним строчным параметром
	{"(-?%d%d%d%d?)[-%.%s/\\](%d%d)[-%.%s/\\](%d%d)",  	["order"] = {3,2,1} },  -- yyyy mm dd
	{"(%d+)[-%.%s/\\](%d+)[-%.%s/\\](%d%d%d%d?)",	["order"] = {1,2,3} }, 		-- dd mm yyyy
	{"(%d%d)[-%.%s/\\](%d%d%d%d?)", ["order"] = {2,3} }, 	-- mm yyyy
	{"(%d%d%d%d?)[-%.%s/\\](%d%d)", ["order"] = {3,2} }, 	-- yyyy mm
	{"(%d+)%s(%l+)%s(%d%d%d%d?)", 	["order"] = {1,2,3} }, 	-- d mmm y
	{"(%l+)%s(%d+),?%s(%d%d%d%d?)", ["order"] = {2,1,3} }, 	-- mmm d, y
	{"(%l+)%s(%d%d%d%d?)", 	["order"] = {2,3} }, 			-- mmm y
}

local time_units = {"year","month","day"} --не используется
--[[ local time_units = {"second", "minute", "hour",
    "day_of_month", "day_of_week", "day_of_year",
    "week", "month", "year", "year_of_century", "century"} ]]--
-- напоминание чтобы сделать более точные пересчёты - с часами / расчёт длительностей периодов

local mnlang = {"ru_G", "ru_N", "en", "de", "fr"}
local month_lang = {
	["ru_G"] = {"января","февраля","марта","апреля","мая","июня",
		"июля","августа","сентября","октября","ноября","декабря"},
	["ru_N"] = {"январь","февраль","март","апрель","май","июнь",
		"июль","август","сентябрь","октябрь","ноябрь","декабрь"},
	["en"] = {"january", "february", "march", "april", "may", "june",
		"july", "august", "september", "october", "november", "december"},
	["de"] = {"januar", "februar", "märz", "april", "mai", "juni",
		"juli", "august", "september", "oktober", "november", "dezember"},
	["fr"] = {"janvier", "février", "mars", "avril", "mai", "juin",
		"juillet", "août", "septembre", "octobre", "novembre", "décembre"}
	}

-- заполняется автоматически
local reverse_month_lang = {}

-- вспомогательная функция для обращения таблиц (смена ключей со значениями)
local reverse_table = function (strait_table)
	local reversed_table = {}
	for k,v in pairs(strait_table) do
		reversed_table[v] = k
	end
	return reversed_table
end

-- запуск цикла по заполнению обратных таблиц, необходимых для распознавания дат
local filling_months = function (mnlang, month_lang)
	for i=1, #mnlang do
		reverse_month_lang[mnlang[i]] = reverse_table(month_lang[mnlang[i]])
	end
end

-- 10) Блок общих функций
local function trim(str)
	if not str then return nil
	else return str:match'^()%s*$' and '' or str:match'^%s*(.*%S)'
	end
end

local function purif(str)
    if str == "" or str == nil then
        return nil
    elseif type(tonumber(str)) == "number" then
        return math.floor(tonumber(str))
    else
        return nil
    end
    -- need .5 -- ,5 number format converter?
end

local function is(str)
	if (not str) or (str == "") then return false
	else return yesno(str,false)
	end
end

local function init(num)
	local output = {}
	for i=1,num do
		table.insert(output, {["year"]="", ["month"]="", ["day"]=""})
	end
	return unpack(output)
end

local function isyear(tbl)
	if type(tbl) ~= 'table' then return false
	elseif not tbl["year"] then return false
	elseif type(tbl["year"]) == 'number' then return true
	else return false
	end
end

local function inbord(val, down, up)
	return not (type(up) ~= "number" or type(down) ~= "number" or type(val) ~= "number" or up < down or val < down or val > up)
end

local function shallowcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in pairs(orig) do
            copy[orig_key] = orig_value
        end
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

local inlist = function ( var, list )
    local n = #list
	local inlist = false
	for i=1,n do
		if var == list[i] then inlist = true end
	end
    return inlist
end

-- 20) Блок общих проверочных функций, связанных с датами
local function unwarp(tbl)
	if not tbl then return ""
	elseif type(tbl) ~= "table" then return tbl
	elseif (tbl.day or tbl.month or tbl.year) then
		return (tbl.year or "?").."-"..(tbl.month or "?").."-"..(tbl.day or "?")
	else return (tbl[3] or "?").."-"..(tbl[2] or "?").."-"..(tbl[1] or "?")
	end
end

local function leap_year(y,jul)
	if (not y) or (type(y) ~= "number")
		then return false
	elseif (y % 4) ~= 0
		then return false
	elseif not jul and (y % 100 == 0 and y % 400 ~= 0)
		then return false
	else return true
	end
end

-- функция для вычисления последнего дня месяца для юлианского и григорианского календарей
local function month_end_day (month,year,is_julian)
	local month_end_day = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -- если не задан год, дата 29 февраля считается допустимой
	if not month or type(month) ~= "number" or month < 1 or month > 12 then return nil
	elseif month ~= 2 or not year then return month_end_day[month] 
	elseif month == 2 and (year % 4) == 0 and not ((not is_julian) and (year % 100 == 0 and year % 400 ~= 0)) then return 29
	elseif month == 2 then return 28
	else return nil -- в случае не целого значения входящих параметров или при иных непредусмотренных событиях
	end
end

local function isdate ( chain , jul ) -- можно использовать для проверки таблиц с полями day, month, year
	if not chain then return false
	elseif (not type(chain) == "table")
	or (not inbord(chain.year,-9999,9999))
	or (not inbord(chain.month,1,12))
	or (not inbord(chain.day,1,31))
	or chain.day > monthd[chain.month]
--	or chain.year == 0
	then return false
	elseif chain.month == 2 and chain.day == 29 and not leap_year(chain.year,jul)
		then return false
	else return true end
--  check for other calendars needed?
end

local function ispartdate ( chain )
	if not chain then return false
	elseif not (type(chain) == "table") then return false
	elseif (inbord(chain.year,-9999,9999)
	or inbord(chain.month,1,12)
	or inbord(chain.day,1,31)) then return true
	else return false
	end
--	partial date
--  more detailed check for 31.02.0000 needed
--  check for other calendars needed
end

-- from date1 to date2 in one year (beetwen jan-dec, dec-jan needed)
local function partdist(date1,date2)
	local mont, dist = 0, 0
	local d1d, d1m, d2d, d2m = (date1["day"] or ""), (date1["month"] or ""),(date2["day"] or ""), (date2["month"] or "")
	if not (inbord(d1d,1,31) and inbord(d2d,1,31)) then return false end
	-- нужна доп. проверка частичных дат на корректность
	if (inbord(d1m,1,12) or inbord(d2m,1,12))
	and (d1m == "" or d2m == "") then
		mont = purif(date1["month"] or date2["month"])
		d1m, d2m = mont, mont
	end
--	mw.log("📏 day: " ..d1d .."->"..d2d.." month: ".. d1m.."->"..d2m )
	if (inbord(d1m,1,12) and d1d <= monthd[d1m])
	and (inbord(d2m,1,12) and d2d <= monthd[d2m])	then 
		if d2m == d1m 
		then dist = d2d - d1d
		else dist = monthd[d1m] - d1d + d2d
		end
		return dist
	else return math.huge
	end
end

local function dmdist(d1,d2)
	local p1,p2 = math.huge,math.huge
	if not not partdist(d1,d2) then 
		p1=partdist(d1,d2)
	end
	if not not partdist(d2,d1) then 
		p1=partdist(d2,d1)
	end
--	if (not p1) or (not p2) then
--		return  (p1 or "") .. (p2 or "")
--	else
--		mw.log("d1, d2 = " .. undate(d1) .. ", " .. undate(d2))
		return math.min(tonumber(partdist(d1,d2)) or math.huge,tonumber(partdist(d2,d1)) or math.huge)
--	end
end

-- 30) Блок функций для обработки ввода-вывода дат

local function undate(tbl)
	if not tbl then return ""
	else return (tbl.year or "").."-"..(tbl.month or "").."-"..(tbl.day or "")
	end
end

-- функция для нормализации значений дат и перевода месяцев в числа
local function numerize(str)
    if type(str) == "number" then
        return math.floor(str)
	elseif str == "" or str == nil or type(str) ~= "string" then
		return nil
    elseif type(tonumber(str)) == "number" then
        return math.floor(tonumber(str))
    else
    	for i=1, #mnlang do
    		if inlist(mw.ustring.lower(str),month_lang[mnlang[i]]) then
				return reverse_month_lang[mnlang[i]][mw.ustring.lower(str)]
			end
    	end
    end
end

-- функция распознавания даты, переданной одной строкой
local function parse_date(date_string)
	if type(date_string) ~= "string" or date_string == "" then return nil end
	local out_date_str = {"","",""}
	local error_data = {}
	for i=1, #pattern do
		local result_1, result_2, result_3 = mw.ustring.match(mw.ustring.lower(date_string),pattern[i][1])
		if (result_1 or "") > "" then
			out_date_str[pattern[i].order[1]] = result_1
    		out_date_str[pattern[i].order[2]] = result_2
    		if (pattern[i].order[3]) then out_date_str[pattern[i].order[3]] = result_3 end
    	--	mw.log("Паттерн " .. i .. ", строка: " .. date_string)
    		break
		end
	end
	local date = {
		["day"]  =numerize(out_date_str[1]),
		["month"]=numerize(out_date_str[2]),
		["year"] =numerize(out_date_str[3])}
	return date --, error_data
end
----[[ УСТАРЕЛО ]]----
local numstr2date = function(numstr)
	local format = "Y-m-d"
	local iso_date = mwlang:formatDate(format,numstr)
    local y,m,d = string.match(iso_date, "(%d+)-(%d+)-(%d+)")
	local dateout = {["year"]=purif(y), ["month"]=purif(m), ["day"]=purif(d)}
    return dateout
end
--local numstr2date = function(numstr)
--	local nums = {}
--    local dateout = {}
--    for num in string.gmatch(numstr,"(%d+)") do
--        table.insert(nums,purif(num))
--    end
--    if #nums ~= 3 then error("В поле даты вместо трёх чисел с разделителями указано " .. #nums)
--    elseif not inbord(nums[2],1,12) then error("Месяц с номером " .. nums[2] .. " не найден")
--    elseif not inbord(nums[3],1,31) then
--        dateout = {["year"]=nums[3], ["month"]=nums[2], ["day"]=nums[1]}
--    elseif not inbord(nums[1],1,31) then
--        dateout = {["year"]=nums[1], ["month"]=nums[2], ["day"]=nums[3]}
--    elseif inbord(nums[1],1,31) then
--        dateout = {["year"]=nums[3], ["month"]=nums[2], ["day"]=nums[1]}
--    else
--		local mwlang = mw.getContentLanguage()
--		implement mwlang:formatDate(format,datein,true) here
--        return error("Не распознано " .. numstr .. " как дата")
--    end
--    return dateout
--end

local function year2lang(numyear,yearmark,wiki)
	if not numyear then return "" end
	if not yearmark then yearmark = "" end
	local output = ""
	local bcmark = " до н. э."
	if numyear > 0 then	bcmark = ""
	else numyear = 1 - numyear end
	if wiki then 
--		output = tCon({'[[', numyear,' год',bcmark,'|', numyear,']]', " ", yearmark, " ", bcmark})
		output = tCon({'[[', numyear,' год',bcmark,'|', trim(numyear .. " " .. yearmark .. " " .. bcmark), ']]'})
	else
		output = tCon({numyear, " ", yearmark, bcmark})
	end
	return trim(output)
end
	
local function day2lang(datein,wikidate,wiki,inner_brt)
--	if not isdate(wikidate) then wiki = false end
	if not ispartdate(datein) then return "" end
	local dm_separ, output = "", nil
	if (not (not datein.day)) and (not (not datein.month)) then dm_separ = " " end
	if (not datein.month) then datein.month = "" end
	if (not datein.day) then datein.day = "" end
	local monlan = monthlang[datein.month] or ""
	if wiki and not inner_brt then
		output = tCon({"[[", wikidate.day, " ", monthlang[wikidate.month] or "",
			"|", (datein.day or ""), dm_separ, monlan, "]]"})
	elseif wiki then
		output = tCon({"[[", wikidate.day, " ", monthlang[wikidate.month] or "",
			"|", (datein.day or ""), dm_separ, monlan})
	else
		output = tCon({datein.day, dm_separ, monlan})
	end
    return trim(output)
end

local function triple_txt2date(d,m,y)
	-- добавить (args[1]:match("(%a+)") or "-") для нестандартной записи
	-- mw.ustring.match((m or ""),"(%a+)")
	local msg = ""
	local year = purif((y or "-"):match("(%d+)"))
	local month = purif(month_to_num[string.lower(mw.ustring.match((m or ""),"(%a+)"))])
	local day = purif((d or "-"):match("(%d+)"))
	if not month then 
		msg = category.incomplete_parameters
		month = purif(month_to_num[string.lower(mw.ustring.match((d or ""),"(%a+)") or "-")]) 
	end
	if (not day) and ((purif(string.match(m or "","(%d+)") or "") or 32) <= (monthd[month] or 31)) then 
		msg = category.incomplete_parameters
		day = purif(m:match("(%d+)") or "") 
	end
	if not year then 
		msg = category.incomplete_parameters
		year = purif(string.match(m or "","(%d+)") or "") 
	end
	local dateout = {["year"]=year, ["month"]=month, ["day"]=day, ["msg"]=msg}
	return dateout
end

local function glue(d1,m1,y1,d2,m2,y2)
	if (not d1) and (not m1) and (not y1) and (not d2) and (not m2) and (not y2) then
		return category.incomplete_parameters end
	local gd,gm,gy,jd,jm,jy = 
		(d1 or ""),
		(m1 or ""),
		(y1 or ""),
		(d2 or ""),
		(m2 or ""),
		(y2 or "")
	--mw.log(tCon({gd,gm,gy,jd,jm,jy}))
	local gm_sep = {" [["," год|","]]"}
	if (not gy) or (gy == "") then gm_sep = {"","",""} end
	return tCon({comment[1],trim(trim(jd .. " " .. jm) .. " " .. jy ),
		comment[2]," ([[",trim(gd .. " " .. gm),"]]",gm_sep[1],(gy:match("(%d+)") or ""),
		gm_sep[2],gy,gm_sep[3],")",category.incomplete_parameters})
end

-- добавить отображение без года
local function double_couple(jdate, gdate, wd, wm, wy, sq_brts, yearmark)
	local msg = ""
	msg = (jdate.msg or "") .. (gdate.msg or "")
	local cd = {}
	local jd = shallowcopy(jdate)
	local gd = shallowcopy(gdate)
	local left = "("
	local right = ")"
	if sq_brts then 
		left = "&#091;"
		right = "&#093;"
	end
	if (not isdate(jdate,true)) then return error((jdate.day or "") .. "." .. (jdate.month or "") .."." .. (jdate.year or "") .. " неподходящая дата")
	elseif (not isdate(gdate)) then return error((gdate.day or "") .. "." .. (gdate.month or "") .."." .. (gdate.year or "") .. " неподходящая дата") end
	if jd.year == gd.year then
		cd.year = gd.year
		gd.year, jd.year = nil, nil
	end
	if jd.month == gd.month then
		cd.month = gd.month
		gd.month, jd.month = nil, nil
	end	
	if (not not cd.month) and wm then 
		return tCon({comment[1] .. trim(day2lang(jd,jdate,false) .. " " .. year2lang(jd.year,yearmark,false)) .. comment[2], 
		trim(left .. day2lang(gd,gdate,wd,wm) .. " " .. year2lang(gd.year,yearmark,wy)) .. right, 
		day2lang(cd,gdate,false) .. "]]", trim(year2lang(cd.year,yearmark,wy)..msg)}, " ")
	end 
	return tCon({comment[1] .. trim(day2lang(jd,jdate,false) .. " " .. year2lang(jd.year,yearmark,false)) .. comment[2], 
		trim(left .. day2lang(gd,gdate,wd) .. " " .. year2lang(gd.year,yearmark,wy)) .. right, 
		trim(day2lang(cd,gdate,false)), trim(year2lang(cd.year,yearmark,wy)..msg)}, " ")
end

-- 40) Блок функций для перевода дат с использованием [[Юлианская дата]]

local function gri2jd( datein )
	if not isdate(datein) then return error((datein.day or "") .. "." .. (datein.month or "") .."." .. (datein.year or "") .. " неподходящая дата") end
    local year = datein.year
    local month = datein.month
    local day = datein.day
    -- jd calculation
    local a = math.floor((14 - month)/12)
    local y = year + 4800 - a
    local m = month + 12*a - 3
    local offset = math.floor(y/4) - math.floor(y/100) + math.floor(y/400) - 32045
    local jd = day + math.floor((153*m + 2)/5) + 365*y + offset
    -- jd validation
    local low, high = -1931076.5, 5373557.49999
    if not (low <= jd and jd <= high) then
        return error((datein.day or "") .. "." .. (datein.month or "") .. "." .. (datein.year or "") .. " выходит за пределы разрешённого диапазона")
    end
	return jd
end

local function jd2jul( jd )
	if type(jd) ~= "number" then return error("Промежуточная переменная " .. (jd or "") .. " не является числом") end
    -- calendar date calculation
    local c = jd + 32082
    local d = math.floor((4*c + 3)/1461)
    local e = c - math.floor(1461*d/4)
    local m = math.floor((5*e + 2)/153)
    local year_out = d - 4800 + math.floor(m/10)
    local month_out = m + 3 - 12*math.floor(m/10)
    local day_out = e - math.floor((153*m + 2)/5) + 1
    -- output
    local dateout = {["year"]=year_out, ["month"]=month_out, ["day"]=day_out}
    return dateout
end

local function jul2jd( datein )
	if not isdate(datein,true) then return error((datein.day or "") .. "." .. (datein.month or "") ..".".. (datein.year or "") .. " неподходящая дата") end
    local year = datein.year
    local month = datein.month
    local day = datein.day
    -- jd calculation
    local a = math.floor((14 - month)/12)
    local y = year + 4800 - a
    local m = month + 12*a - 3
    local offset = math.floor(y/4) - 32083
    local jd = day + math.floor((153*m + 2)/5) + 365*y + offset
    -- jd validation
    local low, high = -1930999.5, 5373484.49999
    if not (low <= jd and jd <= high) then
        return error((datein.day or "") .. "." .. (datein.month or "") .."." .. (datein.year or "") .. " выходит за пределы разрешённого диапазона")
    end
	return jd
end

local function jd2gri( jd )
	if type(jd) ~= "number" then return error("Промежуточная переменная " .. (jd or "") .. " не является числом") end
    -- calendar date calculation
    local a = jd + 32044
    local b = math.floor((4*a + 3) / 146097)
    local c = a - math.floor(146097*b/4)
    local d = math.floor((4*c+3)/1461)
    local e = c - math.floor(1461*d/4)
    local m = math.floor((5*e+2)/153)
    local day_out =  e - math.floor((153*m+2)/5)+1
    local month_out = m + 3 - 12*math.floor(m/10)
    local year_out = 100*b + d - 4800 + math.floor(m/10)
    -- output
    local dateout = {["year"]=year_out, ["month"]=month_out, ["day"]=day_out}
    return dateout
end

local function astroyear(num, bc)
	if not num then return error()
	elseif type(num) ~= "number" then return error()
	end
	if num < 1 then return num end
	if not bc then return num
	else return 1 - num
	end
end

local function recalc(datein,calend)
	if inlist(calend,params[1]) then 
		return jd2jul(gri2jd(datein)), datein
   	elseif inlist(calend,params[2]) then
		return datein, jd2gri(jul2jd(datein))
   	else error("Параметр " .. (calend or "") .. " не опознан, разрешённые: " .. tCon(params[1]," ") .. " и " .. tCon(params[2]," "))
   	end
end

-- 50) Функции для обработки UTC

local function utc(str,margin)
	local d = 1
	local dchar = "+"
	local beginning = "[[UTC"
	local ending = "]]"
	local cat = ""
	local nums = {}
	local hmarg, timedec = 0, 0
	local mmarg = "00"
	local output = ""
-- checking type of input
	if not margin then margin = 0
	elseif type(tonumber(margin)) ~= 'number' then
		output = "Can't shift by " .. margin
		error(output)
	end
	if type(str) ~= 'string' then
		error("Нет входящей строки")
	elseif str:byte(1) == 43 then
	elseif inbord(str:byte(1),48,57) then cat = "[[Категория:Википедия:Ошибка в часовом поясе НП]]"
	elseif str:byte(1) == 45 or string.sub(str,1,3) == "−" or string.sub(str,1,1)=="-" then d = -1
	else
		error(string.char(str:byte(1)) .. " недопустимый первый символ")
	end
-- parsing input
	for num in string.gmatch(str,"(%d+)") do
        table.insert(nums,purif(num))
    end
	if #nums > 2 then error("Ожидается всего 2 числа, а не " .. #nums)
	elseif #nums == 0 then error("Необходимо что-то ввести")
	elseif #nums == 1 then
		if inbord(nums[1],0,14) then timedec = d*nums[1] + margin
		else error("Только часы от -14 до 14") end
	elseif #nums == 2 then
		if not inbord(nums[1],0,14) then error("Только часы от -14 до 14")
		elseif not inbord(nums[2],0,59) then error("Минуты только от 0 до 59")
		else
			timedec = d*(nums[1] + nums[2]/60) + margin
		end
	end
	if tonumber(timedec) == purif(timedec) then hmarg = timedec
	else
		local h, m = math.modf(math.abs(timedec))
		hmarg = h
		mmarg = math.floor(m*60)
	end
	if timedec == 0 then
		dchar = "±"
	elseif timedec > 0 then
	elseif timedec < 0 then
		dchar = "&minus;"
	end
-- output
	output = beginning .. dchar .. math.abs(hmarg) .. ":" .. string.format("%02d",mmarg) .. ending .. cat
	return output
end

-- 60) Блок функций ввода-вывода

function p.NthDay( frame )
    local args = getArgs(frame, { frameOnly = true })
    local num, wday, mont, yea, format = 
    	purif(args[1]), purif(args[2]), purif(args[3]), purif(args[4]), args[5]
	if not format then format = "%d.%m.%y" end
    if not inbord(num,-5,5) then 
    	return error("The number must be between -5 and 5")
    elseif num == 0 then 
    	return error("The number must not be zero") end
    if not inbord(wday,0,6) then 
    	return error("The day of the week must be between 0 and 6") end
    if not inbord(mont,1,12) then 
    	return error("The month must be between 1 and 12") end
    if not inbord(yea,0,9999) then 
    	return error("Wrong year number") end
    if inbord(num,1,5) then
        local m_start = os.time{year=yea, month=mont, day=1, hour=0}
        local m_wds = tonumber(os.date("%w", m_start)) 
        local start_shift = (
            (num - bool_to_number[wday >= m_wds]) * 7 
            - (m_wds - wday)
            ) * 24 * 60 * 60
        local tim = m_start + start_shift
        if tonumber(os.date("%m", tim)) == mont then
            return (os.date(format, tim))
        else
            return (err)
        end
    elseif inbord(num,-5,-1) then
        local m_end = os.time{year = yea, month = mont + 1, day = 1, hour = 0} - 24 * 60 * 60
        local m_wde = tonumber(os.date("%w", m_end))
        local end_shift = ((math.abs(num + 1) + bool_to_number[wday > m_wde]) * 7 
            + (m_wde - wday)) * 24 * 60 * 60
        local tim = m_end - end_shift
        if tonumber(os.date("%m", tim)) == mont then
            return (os.date(format, tim))
        else
            return (err)
        end
    end
end

-- =p.ToIso(mw.getCurrentFrame():newChild{title="smth",args={"12 декабря 2020"}})
-- =p.ToIso(mw.getCurrentFrame():newChild{title="smth",args={"1.2.1602"}})
-- =p.ToIso(mw.getCurrentFrame():newChild{title="smth",args={"12.12.2021"}})
-- =p.ToIso(mw.getCurrentFrame():newChild{title="smth",args={"2021.12.12"}})
function p.ToIso( frame ) 
    local args = getArgs(frame, { frameOnly = true })
    local datein = args[1]
    -- инициализация, заполнение обратных таблиц, копирование параметров
	filling_months(mnlang, month_lang)
    -- парсинг входящей даты по шаблону
    local date = parse_date(datein)
    if not (type(date.year) == 'number') then 
        return ("Wrong year: " .. unwarp(date))
    end
    if not (1 <= date.month and date.month <= 12) then 
        return ("Wrong month: " .. unwarp(date))
    end
    if not date.day or not (1 <= date.day and date.day <= month_end_day(date.month,date.year)) then 
        return ("Wrong day: " .. unwarp(date))
    end
    local timedate = os.time{year=date.year, month=date.month, day=date.day}
    local date = os.date("%Y-%m-%d", timedate)
    return date
end

-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"12 декабря 2020"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"1.2.1602"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"декабрь 2020"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"12-2020"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"12.12.2021"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"2021.12.12"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"2021.11"}})
-- =p.BoxDate(mw.getCurrentFrame():newChild{title="smth",args={"11.2021"}})
function p.BoxDate( frame ) 
    local args = getArgs(frame, { frameOnly = true })
    local txtDateIn, strFormat  = args[1], args[2]
    local txtDateOut, date, status = p.bxDate(txtDateIn, strFormat, params)
	if status.brk then
		return error(status.errorText)
	else
		return txtDateOut
	end
end

function p.bxDate( txtDateIn , strFormat, params ) -- к отладке
	local txtDateOut, date, status = "", {}, {brk = false, errorCat = "", errorText = ""}
	strFormat = strFormat or "j xg Y"
	-- заглушка - таблица параметров на будущее
	params = params or {}
	if not txtDateIn then 
		status.errorText = e.no_data
		status.errorCat = category.no_parameters
		status.brk = true
	else
		-- заполнение служебных таблиц
		filling_months(mnlang, month_lang)
	end
	if not status.brk then
		-- парсинг входящей даты по шаблону
		date = parse_date(txtDateIn)
	    -- заменить сообщения об ошибках на списочные
	    if not (date.year and type(date.year) == 'number') then 
	    	status.errorText = string.format(e.box_date,txtDateIn)
	    	status.errorCat = category.incomplete_parameters
	    	status.brk = true
	    end
	    if not inbord(date.month,1,12) then 
	    	status.errorText = string.format(e.box_date,txtDateIn)
	    	status.errorCat = category.incomplete_parameters
	    	status.brk = true
	    end
	    if not date.day and string.find(strFormat,"[dDjlNwzW]") then
	    	strFormat = trim(string.gsub(string.gsub(strFormat,"xg","F"),"[dDjlNwzW]",""))
	    elseif not date.day then
	    elseif not inbord(date.day,1,month_end_day(date.month,date.year)) then 
	    	status.errorText = string.format(e.box_date,txtDateIn)
	    	status.errorCat = category.incomplete_parameters
	    	status.brk = true
	    end
	end
	if not status.brk then
		txtDateOut = mwlang:formatDate(strFormat,tCon({date.year,date.month,date.day},"-"),true)
	end
    return txtDateOut, date, status
end

function p.ToDate( frame ) -- возможно неиспользуемая
    local args = getArgs(frame, { frameOnly = true })
    local mwlang = mw.getContentLanguage()
    local datein = args[1]
    local format = "j xg Y"
    if not string.match(datein, "%p") then return datein
    elseif not args[2] then
    else format = args[2]
    end
    return mwlang:formatDate(format,datein,true)
end

-- =p.unitime(mw.getCurrentFrame():newChild{title="smth",args={"−1:30","1"}})

function p.unitime( frame )
    local args = getArgs(frame, { frameOnly = true })
    local DST = 0
    if not args[2] then 
    else DST = 1 end
    local utcin = ""
    local input = args[1]
    if not input then return "" end
    if inlist(input:upper(),tzs_names) then 
    	utcin = known_tzs[input:upper()] 
    elseif (string.sub(input:upper(),1,3) == 'UTC') and (string.len(input) < 10) then
    	utcin = string.sub(input,4)
    else 
    	if string.sub(input,1,1) == '[' 
        or string.sub(input,1,1) == '{' 
        or string.sub(input,1,1):upper() == 'U' 
        or string.sub(input,1,1):upper() == 'M' then
    	    return input 
--      elseif not string.find(string.upper(string.sub(input,1,1)),"[\65-\90]") or
--      not string.find(string.upper(string.sub(input,1,1)),"[\192-\223]") then
--    	return input
    	else utcin = input end 
    end
--  elseif string.sub(input,1,3) ~= "−" then utcin = input
--  or not (not input:find("[А-я]")) при наличии в строке юникода не работает
    local output = ""
    if DST == 0 then output = utc(utcin)
    else output = utc(utcin) .. ", [[летнее время|летом]] " .. utc(utcin,DST)
    end
    return output
end


-- УСТАРЕЛО
-- =p.OldDate(mw.getCurrentFrame():newChild{title="smth",args={"20.02.2020","ю",["bc"]="1",["wd"]="1",["wy"]="1",["sq_brts"]="1",["yearmark"]="г."}})
function p.OldDate( frame )
    local args = getArgs(frame, { frameOnly = true })
    if not args[1] then return err end
    local gdate, jdate = {}, {}
    local strin = args[1] 
    local cal = args[2]:lower() or "г"
    local bc = is(args["bc"])
    local wd = is(args["wd"])
    local wm = is(args["wm"])
    local wy = is(args["wy"])
    if not wd then wm = false end
    local sq_brts = is(args["sq_brts"])
    local yearmark = "года"
    if yesno(args["yearmark"]) then
    elseif yesno(args["yearmark"]) == false then yearmark = ""
    else yearmark = trim(args["yearmark"]) or "года" end
--  local infocard = is(args["infocard"])
--  local catName = args["catName"] or false
    local datein = numstr2date(strin)
    datein.year = astroyear(datein.year, bc)
    jdate, gdate = recalc(datein,cal)
	return double_couple(jdate, gdate, wd, wm, wy, sq_brts, yearmark)
end

-- =p.NewDate(mw.getCurrentFrame():newChild{title="Salt",args={"2020-02-20"}})
-- =p.NewDate(mw.getCurrentFrame():newChild{title="smth",args={"20.02.2020","ю",["bc"]="1",["wd"]="1",["wy"]="1",["sq_brts"]="1",["yearmark"]="г."}})
-- =p.NewDate(mw.getCurrentFrame():newChild{title="smth",args={"20.02.2020",["bc"]="0",["wd"]="1",["wy"]="1",["sq_brts"]="0",["yearmark"]=""}})
function p.NewDate( frame )
    local args = getArgs(frame, { frameOnly = true })
    if not args[1] then return err end
	local strin = args[1] 
    local year, month, day
    if     not not strin:match( "(-?%d%d%d%d%d)-(%d%d)-(%d%d)" ) then
    	year, month, day = strin:match( "(-?%d%d%d%d%d)-(%d%d)-(%d%d)" )
    elseif not not strin:match( "(-?%d+)-(%d+)-(%d+)" ) then
    	year, month, day = strin:match( "(-?%d+)-(%d+)-(%d+)" )
    elseif not not strin:match( "(%d%d)%.(%d%d)%.(-?%d%d%d%d%d)" ) then
    	day, month, year = strin:match( "(%d%d)%.(%d%d)%.(-?%d%d%d%d%d)" )
    elseif not not strin:match( "(%d+)%.(%d+)%.(-?%d+)" ) then
    	day, month, year = strin:match( "(%d+)%.(%d+)%.(-?%d+)" )
	end
	if not year then return error(args[1] .. " не подходит под форматы yyyy-mm-dd или dd.mm.yyyy")
	end
	
	local cal = "г"
	if (not args[2]) or (args[2] == "") then cal = "г"
	else cal = args[2]:lower() end

	local bc,wd,wm,wy,sq_brts = 
		is(args["bc"]),
		is(args["wd"]),
		is(args["wd"]) and is(args["wm"]),
		is(args["wy"]),
		is(args["sq_brts"])
		
	year = astroyear(purif(year),bc)
	local datein = {["year"]=purif(year), ["month"]=purif(month), ["day"]=purif(day)}

	local jdate, gdate = recalc(datein,cal)

    local yearmark = "года"
    local ym = args["yearmark"] or ""
    if yesno(ym) then
    elseif yesno(ym) == false then yearmark = "" 
    else
    	if not not ym:match("(%d+)") then 
    		error("Цифры в обозначении года: " .. ym)
    	else yearmark = trim(ym) or "года" end
    end

	return double_couple(jdate, gdate, wd, wm, wy, sq_brts, yearmark)
end

-- =p.Test(mw.getCurrentFrame():newChild{title="smth",args={}})
-- =p.Test(mw.getCurrentFrame():newChild{title="smth",args={"3","июня",nil,"21","мая"}})
-- =p.Test(mw.getCurrentFrame():newChild{title="smth",args={"28 августа","","1916 года","15"}})
-- =p.Test(mw.getCurrentFrame():newChild{title="smth",args={"3","июня","1900","21","мая"}})
-- =p.Test(mw.getCurrentFrame():newChild{title="smth",args={"6","июня","1889 год","25","мая"}}) 
-- =p.Test(mw.getCurrentFrame():newChild{title="smth",args={"28","ноября","1917","15"}})
-- =p.Test(mw.getCurrentFrame():newChild{title="smth",args={"28 августа","nil","1916 года","15"}}) 
-- =p.Test(mw.getCurrentFrame():newChild{title="smth",args={"4","января","1915","22","декабря","1914 года"}}) 
-- {{OldStyleDate|день (НС)|месяц (НС)|год (НС)|день (СС)|месяц (СС)|год (СС)}}

function p.Test( frame )
	local args = getArgs(frame, { frameOnly = true })
	-- необходима проверка и замена nil на " "
--[[mw.log((args[1] or "") .. " " .. 
		(args[2] or "") .. " " .. 
		(args[3] or "") .. " " .. 
		(args[4] or "") .. " " .. 
		(args[5] or "") .. " " .. 
		(args[6] or "")) ]]--
	local ingdate = triple_txt2date(args[1],args[2],args[3])
	local injdate = triple_txt2date(args[4],args[5],args[6])
	local j1date, g1date, j2date, g2date = init(4)
		mw.log("ingdate-".. (undate(ingdate) or ""))
		mw.log("injdate-".. (undate(injdate) or ""))
	local bc,wd,wm,wy,sq_brts,ny = 
		is(args["bc"]),
		is(args["wd"]),
		is(args["wd"]) and is(args["wm"]),
		is(args["wy"]),
		is(args["sq_brts"]),
		is(args["ny"])
	-- подавление формата для локальных тестов
    local wd, wm, wy = true, true, true
    
    local yearmark = "года" 
    local ym = args["yearmark"] or ((mw.ustring.match((args[3] or ""),"(%a+)") or mw.ustring.match((args[6] or ""),"(%a+)")) or "")
    -- mw.log("ym " .. ym)
    if yesno(ym) then
    elseif yesno(ym) == false then yearmark = "" 
    else
    	if not not ym:match("(%d+)") then 
    		error("Цифры в обозначении года: " .. ym)
    	else yearmark = trim(ym) or "года" end
    end
    if isdate(ingdate) or isdate(injdate) then
		if isdate(ingdate) then
			j1date, g1date = recalc(ingdate,"g")
			ingdate["full"] = true
		end
		if isdate(injdate) then
			j2date, g2date = recalc(injdate,"j")
			injdate["full"] = true
		end
		if ispartdate(ingdate) and ispartdate(injdate) then
			mw.log("📏 " .. dmdist(ingdate,injdate))
			mw.log("📏 " .. dmdist(j1date,g1date))
			mw.log("📏 " .. dmdist(j2date,g2date))
			mw.log("📏 " .. dmdist(ingdate,g1date))
			mw.log("📏 " .. dmdist(injdate,j2date))
		end
	end
	
	if ny then 
		if isyear(j1date) then
		else j1date["year"] = "" end
		if isyear(j2date) == nil then
		else j2date["year"] = "" end
		if isyear(g1date) == nil then
		else g1date["year"] = "" end
		if isyear(g2date) == nil then
		else g2date["year"] = "" end
	end
	if (isdate(j1date) and isdate(g1date) and isdate(j2date) and isdate(g2date)) then
		if ((j1date.year == j2date.year)
		and (j1date.month == j2date.month)
		and (j1date.day == j2date.day)) then
			return double_couple(j1date, g1date, wd, wm, wy, sq_brts, yearmark)
		else 
			mw.log("📏 " .. (tostring(dmdist(ingdate,injdate)) or ""))
			return glue(args[1],args[2],args[3],args[4],args[5],args[6])  
			-- категория (предположительная разница в днях) и частичный вывод
		end
	elseif isdate(j1date) and isdate(g1date) then
		return double_couple(j1date, g1date, wd, wm, wy, sq_brts, yearmark) -- категория плюс частичная проверка
	elseif isdate(j2date) and isdate(g2date) then
		return double_couple(j2date, g2date, wd, wm, wy, sq_brts, yearmark) -- категория плюс частичная проверка
	elseif (ispartdate(ingdate) and ispartdate(injdate)) then
		mw.log("ingdate ".. (undate(ingdate) or ""))
		mw.log("injdate ".. (undate(injdate) or ""))
		mw.log("j1date " .. (undate(j1date ) or ""))
		mw.log("j2date " .. (undate(j2date ) or ""))
		mw.log("g1date " .. (undate(g1date ) or ""))
		mw.log("g2date " .. (undate(g2date ) or ""))
		mw.log("📏 " .. (tostring(partdist(ingdate,injdate)) or "").. " — " .. (tostring(partdist(injdate,ingdate)) or ""))
		return glue(args[1],args[2],args[3],args[4],args[5],args[6]) 
		-- частичный или полный вывод, категория
	else 
		mw.log("ingdate ".. (undate(ingdate) or ""))
		mw.log("injdate ".. (undate(injdate) or ""))
		mw.log("j1date " .. (undate(j1date ) or ""))
		mw.log("j2date " .. (undate(j2date ) or ""))
		mw.log("g1date " .. (undate(g1date ) or ""))
		mw.log("g2date " .. (undate(g2date ) or ""))
		return err .. category.incomplete_parameters
	end
end

return p