Модуль:Песочница/Carn/Calendar
Для документации этого модуля может быть создана страница Модуль:Песочница/Carn/Calendar/doc
local p = {}
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local snippet = require('Module:Песочница/Carn/Text')
local function is(str)
if (not str) or (str == "") then return false
else return yesno(str,false) end
end
--[==[ Таблицы с данными для работы модуля ]==]
local pattern = { -- для распознавания дат, переданных одним строчным параметром
{"(-?%d+%d*)-(%d+)-(%d+)", ["order"] = {3,2,1} }, -- y-m-d
{"(%d+)%.(%d+)%.(-?%d+%d*)", ["order"] = {1,2,3} }, -- d.m.y
{"(%d+)%s(%d+)%s(-?%d+%d*)", ["order"] = {1,2,3} }, -- d m y
{"(%d+)%s(%a+)%s(-?%d+%d*)", ["order"] = {1,2,3} }, -- d 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 category_msg = ""
local category = {
["incomplete_parameters"]=
"<!--[[Категория:Модуль:Calendar:Страницы с неполными или некорректными параметрами]]-->",
["without_verification"]=
"<!--[[Категория:Модуль:Calendar:Страницы без проверки параметров]]-->",
["erroneous_parameters"]=
"<!--[[Категория:Модуль:Calendar:Страницы с ошибочными параметрами]]-->"
}
-- несколько параметров передаются вместе с кодом ошибки в таблице, один может быть передан простым значением
local errors = {
["start"]="<span class=error>Ошибка: ",
["ending"]=".</span>",
["no_pattern_match"]="строка «%s» не совпадает с заданными паттернами",
["no_valid_date"]="дата «%s·%s·%s» не является корректной",
["wrong_jd"]="юлианская дата %s вне диапазона",
["too_many_arguments"]="ожидается менее %i аргументов",
["too_little_arguments"]="ожидается более %i аргументов",
["wrong_calculation"]="даты %s и %s не прошли проверку, %s дней разница",
["unknown_calendar"]="параметр календаря %s неизвестен",
["unknown_error"]="неизвестная ошибка",
["tech_error"]="ошибка в функции %s",
-- [""]="",
}
-- для повышения гибкости вывода можно указать отдельные параметры для первой и второй даты
-- для повышения удобства пользователя заданные одним параметром аргументы дублируются
local calendars = {{"г", "g"}, {"ю", "j"}}
local unik_args = { "order","lang", "cal", "bc", "sq_brts" }
local unik_args_bool = {false,false,false,true,true,true}
-- before christ, calendar, brackets inside, square brackets
local dual_args = { "wdm", "wy", "ny", "ym"}
local dual_args_bool = {true,true,true,false}
-- wikify day and month, wikify year, no year, year mark
local status = {category="",error={msg="",params=""}}
local bool2num={[1]=1, [0]=0, ["1"]=1, ["0"]=0, [true]=1, [false]=0,
["__index"]=function(self,v)
return tostring(v)
end }
setmetatable(bool2num,bool2num)
-- local options = {"cal","bc","wiki_date","wiki_year","sq_brts","no_year","brackets_inside"}
-- в случае обновления таблицы названий месяцев необходимо также обновлять список кодов языков
local bc_mark = "до н. э."
local lang = {"ru", "en", "de", "fr"}
local month_lang = {
["ru"] = {"января","февраля","марта","апреля","мая","июня",
"июля","августа","сентября","октября","ноября","декабря"},
["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 (lang, month_lang)
for i=1, #lang do
reverse_month_lang[lang[i]] = reverse_table(month_lang[lang[i]])
end
end
--[==[ Функции универсального назначения ]==]
-- вспомогательная функция для проверки вхождения числа в диапазон
local function number_in_range(value, bottom, top)
if type(value) ~= "number" or type(top) ~= "number" or type(bottom) ~= "number"
or top < bottom or value < bottom or value > top then return false
else return true end
end
-- mw.clone копирует с метатаблицами
-- для определения наибольшего индекса в таблице есть table.maxn
local function copy_it(original)
local c = {}
if type(original) == "table" then
for key, value in pairs(original) do
if value == "" or value == " " then
value = nil
end
c[key] = value
end
else return original, 1
end
for i = 7, 1, -1 do
if c[i] then
return c, i
end
end
return c, 0
end
-- функция, определяющая, содержит ли таблица необходимое число аргументов
local function is_complete(table_in,start,finish)
if type(table_in) ~= "table" or type(start) ~= "number" or type(finish) ~= "number" or start > finish then
return nil
else
for i=start,finish do
if not table_in[i] then
return false
end
end
end
return true
end
-- функция для проверки, содержит ли массив запрашиваемое значение
local is_in_list = function ( var, list )
for i=1, #list do
if var == list[i] then
return true
end
end
return false
end
--XXX--
--[==[ Календарные функции ]==]
-- функция для вычисления последнего дня месяца для юлианского и григорианского календарей
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 is_date ( date, is_julian )
if not date or type(date) ~= "table" then return false
elseif not number_in_range(date.month,1,12) or
not number_in_range(date.day,1,month_end_day(date.month,date.year,is_julian)) or
not number_in_range(date.year,-9999,9999) then
return false
elseif date.year ~= 0 then return true
end
end
-- функция для проверки, содержит ли таблица частичные сведения о дате
local function is_date_part ( date )
if not date then return false
elseif not (type(date) == "table") then return false
elseif (number_in_range(date.year,-9999,9999)
or number_in_range(date.month,1,12)
or number_in_range(date.day,1,31)) then return true
else return false
end
end
-- для дат, порядок которых неизвестен, пробует сначала прямой (dmy), затем обратный (ymd) порядок
local function guess_date( triplet, is_julian ) -- только для дат после 31 года, пока не используется
local date = {["day"]=triplet[1],["month"]=triplet[2],["year"]=triplet[3]}
if is_date(date,is_julian) then return date end
local date = {["day"]=triplet[3],["month"]=triplet[2],["year"]=triplet[1]}
if is_date(date,is_julian) then return date end
end
-- конвертация григорианской даты в jd [[Julian day]]
function gri2jd( datein )
if not is_date(datein) then
-- if type(status.error) ~= "table" then
-- status.error = {}
-- end
-- status.error.msg = "no_valid_date"
-- status.error.params = datein
return --status
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
-- status.error.msg = "wrong_jd"
-- status.error.params = jd
return --status
end
return jd
end
-- конвертация jd в дату по юлианскому календарю
function jd2jul( jd )
if type(jd) ~= "number" then return error("Wrong jd") 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 = {["jd"]=jd, ["year"]=year_out, ["month"]=month_out, ["day"]=day_out,["calendar"]="julian"}
return dateout
end
-- конвертация даты по юлианскому календарю в jd
function jul2jd( datein )
if not is_date(datein,true) then
-- if type(status.error) ~= "table" then
-- status.error = {}
-- end
-- status.error.msg = "no_valid_date"
-- status.error.params = datein
return --status
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
-- status.error.msg = "wrong_jd"
-- status.error.params = jd
return --status
end
return jd
end
-- конвертация jd в григорианскую дату
function jd2gri( jd )
-- 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 = {["jd"]=jd, ["year"]=year_out, ["month"]=month_out, ["day"]=day_out, ["calendar"]="gregorian"}
return dateout
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, #lang do
if is_in_list(mw.ustring.lower(str),month_lang[lang[i]]) then
return reverse_month_lang[lang[i]][mw.ustring.lower(str)]
end
end
end
end
-- функция для распознавания дат, заданных тремя значениями подряд, с исправлением ошибок
local function decode_triple(d,m,y)
local year = numerize((y or ""):match("(%d+)"))
local month = numerize(mw.ustring.match((m or ""),"(%a+)"))
local day = numerize((d or ""):match("(%d+)"))
if not month then month = numerize(mw.ustring.match((d or ""),"(%a+)")) end
if not day then day = numerize((m or ""):match("(%d+)")) end
if not year then year = numerize((m or ""):match("(%d+)")) end
local dateout = {["year"]=year, ["month"]=month, ["day"]=day}
return dateout
end
-- функция распознавания даты, переданной одной строкой
local function parse_date(date_string)
if type(date_string) ~= "string" or date_string == "" then return nil end
local out_date_str = {nil,nil,nil}
local error_data = {}
for i=1, #pattern do
local result_1, result_2, result_3 = mw.ustring.match(date_string,pattern[i][1])
if (result_1 or "") > "" then
out_date_str[pattern[i].order[1]],
out_date_str[pattern[i].order[2]],
out_date_str[pattern[i].order[3]] =
result_1, result_2, result_3
break
end
end
if (not out_date_str[1]) or (not out_date_str[2]) or (not out_date_str[3]) then
error_data.msg = "no_pattern_match"
error_data.params = date_string
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 function o(str,arg)
return (arg and (arg .. ": ") or "") .. (str and (bool2num[str] .. " — ") or "")
end
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 error_output(status)
if (status.error.msg or "") > "" then
if type(status.error.params) == "table" and not status.error.params[1] then
return errors.start .. string.format(errors[status.error.msg],
status.error.params.day or "", status.error.params.month or "",
status.error.params.year or "") .. errors.ending
elseif type(status.error.params) == "table" and status.error.params[1] then
return errors.start .. string.format(errors[status.error.msg],
unwarp(status.error.params[1] or ""), unwarp(status.error.params[2] or ""),
unwarp(status.error.params[3] or "")) .. errors.ending
else
return errors.start .. string.format(errors[status.error.msg],status.error.params or "") .. errors.ending
end
end
end
local function processing(status,input,max_arg)
local first_date_string, second_date_string, category = "", "", ""
local first_date, second_date = {}, {}
if max_arg <= 3 then
first_date_string = table.concat({input[1] or "", input[2] or "", input[3] or ""}, " ")
first_date, status.error = parse_date(first_date_string)
if (status.error.msg or "") > "" then return status end
if is_date(first_date,true) then
status.dates, status.processed, status.first_date = 1, true, true
return status, first_date
else
status.dates = 0
status.error.msg = "no_valid_date"
status.error.params = first_date
return status
end
elseif max_arg > 3 and max_arg <= 6 then
if is_complete(input,1,6) then
first_date_string = table.concat({input[1], input[2], input[3]}, " ")
second_date_string = table.concat({input[4], input[5], input[6]}, " ")
first_date, second_date = parse_date(first_date_string), parse_date(second_date_string)
else
first_date = decode_triple(input[1], input[2], input[3])
second_date = decode_triple(input[4], input[5], input[6])
end
if is_date(first_date,true) then status.first_date = true
elseif is_date_part(first_date) then status.first_date = 1 end
if is_date(second_date,true) then status.second_date = true
elseif is_date_part(second_date) then status.second_date = 1 end
if status.first_date == true and status.second_date == true then
status.dates, status.processed = 2, true
else
status.dates = bool2num[status.first_date] + bool2num[status.second_date]
status.category = "incomplete_parameters"
end
return status, first_date, second_date
elseif max_arg> 6 then
status.error.msg = "too_many_arguments"
status.error.params = max_arg
return status
end
end
local function mix_data(status,first_date,second_date)
status.processed = 1
for i, k in pairs(time_units) do
if not first_date[k] and second_date[k] then
first_date[k] = second_date[k]
elseif not second_date[k] and first_date[k] then
second_date[k] = first_date[k]
end
end
return status, first_date, second_date
end
function recalc(status,date,cal)
if is_in_list(cal,calendars[1]) then
date.jd, date.calendar = gri2jd(date), "gregorian"
status.processed, status.second_date, status.dates = true, true, 2
return status, date, jd2jul(date.jd)
elseif is_in_list(cal,calendars[2]) then
date.jd, date.calendar = jul2jd(date), "julian"
status.processed, status.second_date, status.dates = true, true, 2
return status, date, jd2gri(date.jd)
else
status.error.msg = "unknown_calendar"
status.error.params = cal
return status
end
end
local function partdist(status,date1,date2)
local mont, dist = 0, 0
local d1d, d1m, d2d, d2m = date1["day"], date1["month"], date2["day"], date2["month"]
local d1de, d2de = month_end_day(d1m), month_end_day(d2m)
if not (number_in_range(d1m,1,12) and number_in_range(d2m,1,12)) then
return status, math.huge
elseif not (number_in_range(d1d,1,d1de) and number_in_range(d2d,1,d2de)) then
return status, math.huge
else
return status, (d1m == d2m and math.abs(d1d-d2d)) or ((d1d > d2d and (d1de - d1d + d2d)) or (d2de - d2d + d1d))
end
end
local function guess_jd(status, first_date, second_date)
-- if not is_date(first_date) or is_date(second_date) then
-- return status
-- end
local first_j_jd = jul2jd(first_date)
local first_g_jd = gri2jd(first_date)
local second_j_jd = jul2jd(second_date)
local second_g_jd = gri2jd(second_date)
-- mw.log(first_j_jd,first_g_jd,second_j_jd,second_g_jd)
if not first_j_jd or not first_g_jd or not second_j_jd or not second_g_jd then
local status, difference = partdist(status,first_date,second_date)
status.category = "erroneous_parameters"
status.error.msg = "wrong_calculation"
status.error.params = {unwarp(first_date),unwarp(second_date),difference}
elseif first_j_jd == second_g_jd then
first_date.jd, first_date.calendar = first_j_jd, "julian"
second_date.jd, second_date.calendar = second_g_jd, "gregorian"
elseif first_g_jd == second_j_jd then
first_date.jd, first_date.calendar = first_g_jd, "gregorian"
second_date.jd, second_date.calendar = second_j_jd, "julian"
else
local difference = math.min(math.abs(first_j_jd-second_g_jd),math.abs(first_g_jd-second_j_jd))
status.category = "erroneous_parameters"
status.error.msg = "wrong_calculation"
status.error.params = {unwarp(first_date),unwarp(second_date),difference}
end
return status, first_date, second_date
end
-- для записи типа -100 год = 100 год до н.э. (с разрывом в нуле)
function astroyear(status, num, bc)
local year
if not num or type(num) ~= "number" then
status.error.msg = "tech_error"
status.error.params = "astroyear"
elseif num < 1 then
year = 1 + num
end
-- todo: запрет нулевого года?
if not bc then return status, num
else year = 1 - num
end
return status, year
end
-- в соответствии с таблицами принимаемых аргументов обрабатывает ввод
function read_args(status, input)
if not status or type(status) ~= "table" then
status = {}
status.error = {}
status.error.msg = "tech_error"
status.error.params = "read_args"
elseif not input or type(input) ~= "table" then
status.error.msg = "tech_error"
status.error.params = "read_args"
else
for i,v in pairs(unik_args) do
if unik_args_bool[i] then
input[v] = is(input[v])
end
end
for i,v in pairs(dual_args) do
if dual_args_bool[i] then
local both = is(input[v])
if both then
input[v..1], input[v..2] = true, true
else
input[v..1], input[v..2] = is(input[v..1]), is(input[v..2])
end
else
if input[v] and input[v]>"" then
input[v..1], input[v..2] = input[v], input[v]
end
end
end
end
if input.ny1 then input.ym1, input.wy1 = nil, false end
if input.ny2 then input.ym2, input.wy2 = nil, false end
return status, input
end
--[[
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"15","августа"," ","2"," "," ",["cal"]="g",["wdm2"]=1,["wy2"]=1}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"15","августа",nil,"2",["cal"]="g",["wdm2"]=1,["wy2"]=1}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"32.1.2020",["cal"]="j"}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"23.12.1855",["cal"]="j",["wy2"]=1,["wdm2"]=1}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"+2017-10-09T00:00:00Z",["cal"]="g",["wy"]=1,["wdm"]=1,["ny2"]=1,["sq_brts"]=1,["ym1"]="г."}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"+2017-10-09T00:00:00Z",["cal"]="g",["sq_brts"]=true}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"+2017-10-09T00:00:00Z",["cal"]="g",["bc"]=1,["wy"]=1,["br_in"]=1,["wdm2"]=1,["ny1"]=1,}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"+2017-10-09T00:00:00Z",["cal"]="j"}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"30","апреля",nil,"17"}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"30","апреля","2020","17"}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"31","апреля","2020"}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"23 juin 2020"}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"23 октября 2020"}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"23.10.2020",cal="г"}})
=p.Test(mw.getCurrentFrame():newChild{title="smth",args={"2020-10-23",cal="ю"}})
]]--
function p.Test( frame )
-- инициализация, заполнение обратных таблиц, копирование параметров
filling_months(lang, month_lang)
local args = getArgs(frame, { removeBlanks = false, frameOnly = true })
local input, max_arg = copy_it(args)
-- mw.log("(" .. #input .. ", " .. max_arg .. ")", unpack(input))
-- перевод строковых параметров в числовые
input.cal = input.cal or "j"
local status, first_date, second_date = {processed=false,first_date=false,second_date=false,category="",error={msg="",params=""}}
status, first_date, second_date = processing(status,input,max_arg)
-- перевод параметров оформления в булевые
status, input = read_args(status, input)
-- применение параметра до нашей эры или сдвиг отрицательных дат, чтобы не было разрыва в нулевом году
if first_date then status, first_date.year = astroyear(status, first_date.year, input.bc) end
if second_date then status, second_date.year = astroyear(status, second_date.year, input.bc) end
-- проверка и дополнение дат
if (status.dates or 0) > 1 and status.processed ~= true then
status, first_date, second_date = mix_data(status,first_date,second_date)
status, first_date, second_date = guess_jd(status,first_date,second_date)
elseif status.dates == 1 then
status, first_date, second_date = recalc(status,first_date,input.cal)
elseif max_arg < 3 then
if status.error.msg then
else
status.error.msg = "too_little_arguments"
status.error.params = max_arg
end
else
status.error.msg = "unknown_error"
status.error.params = ""
end
--[[ ошибка если даты сформированы не полностью
if first_date and not first_date.year and second_date and not second_date.year then
error("Даты " .. unwarp(first_date) .. " и " .. unwarp(second_date) .. " не содержат год. " ..
(error_output(status) or "") .. " — " ..
(o(status.processed,"processed") or "") ..
(o(status.first_date,"first_date") or "") ..
(o(status.second_date,"second_date") or "") ..
(o(status.category,"category") or ""))
end
--]]
-- ошибка в случае если даты не сформированы
if not first_date or not second_date then return error_output(status) end
if first_date.calendar == "julian" and second_date.calendar == "gregorian" then
elseif second_date.calendar == "julian" and first_date.calendar == "gregorian" then
local swap_date = first_date
first_date = second_date
second_date = swap_date
else
status.error.msg = "unknown_error"
status.error.params = ""
end
input.wy1 = first_date.year and input.wy1 or nil
input.wy2 = second_date.year and input.wy2 or nil
-- mw.logObject(input)
-- mw.logObject(status)
-- mw.logObject(first_date)
-- mw.logObject(second_date)
-- ниже задаются условия поведения кусков текста - в зависимости от каких параметров они принимают какие значения
-- если нужно более сложное поведение чем "bool and true_result or false_result", то их можно заменить на анонимные функции
input.lang = input.lang or "ru"
local space = snippet:dress{["text"]= " ", a=0, z=0}
local empty = snippet:dress{["text"]= "", a=0, z=0}
local left = snippet:dress{["text"] = args.sq_brts and "[" or "(", ["a"] = 3, ["z"] = 0}
local right = snippet:dress{["text"] = args.sq_brts and "]" or ")", ["a"] = 0, ["z"] = 3}
local bc_mark1 = (first_date.year and first_date.year < 1) and snippet:dress{["text"]= "до н. э." } or empty
local bc_mark2 = (second_date.year and second_date.year < 1) and snippet:dress{["text"]= "до н. э." } or empty
first_date.year = (first_date.year and first_date.year < 1) and -first_date.year or first_date.year
second_date.year = (second_date.year and second_date.year < 1) and -second_date.year or second_date.year
local jdd, jdm, jdy =
snippet:dress{["text"]=first_date.day},
snippet:dress{["text"]=month_lang[input.lang][first_date.month]},
snippet:dress{["text"]=(input.ny1 or not first_date.year) and "" or first_date.year .. ((input.ym1 and " " or "") .. (input.ym1 or "")),
a = input.ny1 and 0 or nil, z= input.ny1 and 0 or nil}
local gdd, gdm, gdy =
snippet:dress{["text"]=second_date.day},
snippet:dress{["text"]=month_lang[input.lang][second_date.month]},
snippet:dress{["text"]=(input.ny2 or not second_date.year) and "" or second_date.year .. ((input.ym2 and " " or "") .. (input.ym2 or "")),
a = input.ny2 and 0 or nil, z= input.ny2 and 0 or nil}
local wdm1_, wdm2_, wy1_, wy2_ =
snippet:dress{["text"]= input.wdm1 and table.concat{
"[[", jdd.text," ",jdm.text ,"|"} or "", a=input.wdm1 and 2 or 0, z=0},
snippet:dress{["text"]= input.wdm2 and table.concat{
"[[", gdd.text," ",gdm.text ,"|"} or "", a=input.wdm2 and 2 or 0, z=0},
snippet:dress{["text"]= (input.wy1 and first_date.year) and ("[[" .. first_date.year .. " год" ..
(bc_mark1.text > "" and (" " .. bc_mark1.text) or "") .. "|") or "", a=input.wy1 and 2 or 0, z=0},
snippet:dress{["text"]= (input.wy2 and second_date.year) and ("[[" .. second_date.year .. " год" ..
(bc_mark2.text > "" and (" " .. bc_mark2.text) or "") .. "|") or "", a=input.wy2 and 2 or 0, z=0}
local wdm_1, wdm_2, wy_1, wy_2 =
snippet:dress{["text"]= input.wdm1 and "]]" or "", a=0, z=input.wdm1 and 2 or 0},
snippet:dress{["text"]= input.wdm2 and "]]" or "", a=0, z=input.wdm2 and 2 or 0},
snippet:dress{["text"]= input.wy1 and "]]" or "", a=0, z=input.wy1 and 2 or 0},
snippet:dress{["text"]= input.wy2 and "]]" or "", a=0, z=input.wy2 and 2 or 0}
local cdm, cdy = empty, empty
input.order = input.order or "zip"
if input.order == "zip" then
if first_date.month == second_date.month then
cdm = mw.clone(jdm)
jdm, gdm = empty, empty
end
if first_date.year == second_date.year then
cdy = mw.clone(jdy)
jdy, gdy = empty, empty
cdy = wy2_ + cdy + bc_mark2 + wy_2
wy1_, wy_1 = empty, empty
wy2_, wy_2 = empty, empty
bc_mark1, bc_mark2 = empty, empty
end
end
local j_day_month = wdm1_ + jdd + jdm + wdm_1
local j_year = wy1_ + jdy + bc_mark1 + wy_1
local g_day_month = wdm2_ + gdd + gdm + wdm_2
local g_year = wy2_ + gdy + bc_mark2 + wy_2
if input.order == "full" then
return mw.text.trim(tostring(j_day_month + j_year + left + g_day_month + g_year + right))
elseif input.order == "zip" then
return mw.text.trim(tostring(j_day_month + j_year + left + g_day_month + g_year + right + cdm + cdy))
else
return
end
if status.error then
return error_output(status)
end
-- todo - part date dist check, year mark from double triplets, julian span comment, br_in?, check if format table is full, add short formats d.m.y - 1/01
end
return p