Siirry sisältöön

Moduli:Dates

On otettu Мегавики-späi

--[[

В это модуле собраны функции, связанные с работой с датами.

]] local monthg = {'pakkaskuudu', 'tuhukuudu', 'kevätkuudu', 'sulakuudu', 'oraskuudu', 'kezäkuudu',

   'heinykuudu', 'elokuudu', 'syvyskuudu', 'ligakuudu', 'kylmykuudu', 'talvikuudu'}

local monthd = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}

local function DecodeDate(d)-- Ч, М, Г, СЧ, СМ, СГ, хвост

   --дата: "%-?%d+"=год, "%d+%.%d+"=число месяца, "%d+%.%d+%.%-?%d+"=ЧМГ,
   -- потом в скобках м.б. переопределено для старого стиля начиная с числа
   local nd=d:match("^[%d.-]*");
   local od=d:match("^[%d.-]*%s*%(%s*([%d.-]*)%s*%)");
   local tail = d:match("^[%d.-]+%s*%(%s*[%d.-]+%s*%)%s*(%S.*)") or d:match("^[%d.-]+%s*([^%s%d].*)");
   if nd:match('^%-?%d+$' ) then
       return nil, nil, tonumber(nd), nil, nil, od and tonumber(od:match("%-?%d+$")),tail
   else
       local j,m,y=nd:match("^(%d+)%.(%d+)%.?(%-?%d*)");
       if j then
           if od then
               local oj, om, oy = od:match("^(%d+)%.?(%d*)%.?(%-?%d*)");
               return j and tonumber(j),
                      m and tonumber(m),
                      y> and tonumber(y) or nil,
                     oj and tonumber(oj),
                     om> and tonumber(om) or nil,
                     oy> and tonumber(oy) or nil,
                     tail
           end
           return j and tonumber(j), m and tonumber(m), y> and tonumber(y) or nil, nil, nil, nil, tail
       else return nil
       end
   end

end

local function Diffy(d1,m1,y1,d0,m0,y0)--аналог Персона/Дата/Прошло лет

   return y1-y0 - ( y1*y0<=0 and 1 or 0 ) - ( (m1<m0 or m1==m0 and d1<d0) and 1 or 0 )

end

local function Year0(y,t)-- аналог Год0

   if y>0 then return table.concat{
       '', t and tostring(y)..' '..t or tostring(y), ''
   } else return table.concat{
       '', 
        t and tostring(-y)..' '..t or tostring(-y),
        ' до н. э.'
   }
   end

end

local function FormDate(j,m,y,oj,om,oy,mo)-- ~ Персона/Дата/Logic 4

   if j then
       if not m then return "формат неверен" end
       if y then return
        string.format(
           '%s(%04i-%02i-%02i)',
           table.concat(
               oj and (
                   om and (
                       oy and {-- ДД ММММ ГГГГ (ДД ММММ ГГГГ)
                           oj,'. ',monthg[om],' ',oy,
                           ' ([[',
                           j, '. ', monthg[m],']] ',Year0(y),')'
                       } or {-- ДД ММММ (ДД ММММ) ГГГГ
                           oj,'. ',monthg[om],' ([[',j,'. ',monthg[m],']]) ',Year0(y)
                       }
                   ) or {-- ДД (ДД) ММММ ГГГГ
                       oj,'. [[',j,'. ',monthg[m],'|','(',j,'). ',monthg[m],']] ',Year0(y)
                   }
               ) or {'[[',j,'. ',monthg[m],']] ',Year0(y)}
           ),--/table.concat
           ({['Рождения']='bday',['Смерти']='dday'})[mo] or ,
           y,m,j
        )--/string.format
       else return
           '' .. table.concat(
               oj and (
                   om and {-- ДД ММММ (ДД ММММ)
                           oj,'. ',monthg[om],' ([[',j,'. ',monthg[m],']])'
                   } or {-- ДД (ДД) ММММ
                       oj,'. [[',j,'. ',monthg[m],'|','(',j,') ',monthg[m],']]'
                   }
               ) or {'[[',j,'. ',monthg[m],']]'}
           )
       end
   else
       return y and string.format(
           '%s(%04i)',
           Year0(y,'год'),y) or "формат неверен"
   end

end

local function GetDate(D)--dd.mm.-?yyyy или -?yyyy-mm-dd в три переменных d,m,y

   local d,m,y = d:match('^%s*(%d%d?)[/.]([01]?%d)[/.](%-?%d+)')
   if not d then y,m,d = D:match('^%s*(%-?%d+)[-\\]0*(1?%d)[-\\]0*(%d+)') end
   return tonumber(d),tonumber(m),tonumber(y)

end

local function Cmp(a,b)--Сравнивает две даты, результат соответственно -1, 0 или 1

   local d1,m1,y1 = GetDate(a)
   local d2,m2,y2 = GetDate(b)
   return d1 and d2 and (--nil, если формат не опознан
       y1==y2 and (
           m1==m2 and (
               d1==d2 and 0 or d1<d2 and -1 or 1
           ) or m1<m2 and -1 or 1
       ) or y1<y2 and -1 or 1
   )

end

local function Yyyymmdd(r)--Переводит русскую дату в YYYY,MM,DD

   local d, m, y, M = mw.ustring.match(r, "^%s*(%d%d?)%s+([а-яА-Я]+)%s+(%d+)")
   if not m then return nil end
   m = mw.ustring.lower(m)
   --тупо перебор
   for i = 1, 12 do
   	if m == monthg[i] then
   		M = i
   		break

end end

   if not M then
   	return nil

end

   return tonumber(y), M, tonumber(d)

end

local p = {}

p = {

ifdate=function(f)-- Для шаблона "Если дата", имитирует старое поведение

-- Аргументы передаются шаблону 
return f:getParent().args[ mw.ustring.match(frame.args[1],"^[ %d.%-−%()]*$") and 2 or 3 ]

end;

DecodeDate = DecodeDate; Diffy = Diffy; Year0 = Year0; GetDate = GetDate; Cmp = Cmp; Yyyymmdd = Yyyymmdd;

diffy = function(f)-- принимает параметры #invoke в виде двух строк-дат

   local d1,m1,y1=DecodeDate(f.args[1]);
   local d0,m0,y0=DecodeDate(f.args[2])
   return Diffy(d1,m1,y1,d0,m0,y0)

end;

monthg=function(f) return monthg[ f.args[1] or f:getParent().args[1] ] end;--realmonth

persdate=function(f)-- Для шаблона Персона/Дата;Komentosarjavirhe: Moduulia ”dates” ei ole.

local frame=f:getParent();
local catpref,mo,d,d2={['Рождения']='Rodivunnuot',['Смерти']='Kuolluot'}, frame.args[1],frame.args[2],frame.args[3]
local cat, j,m,y,oj,om,oy,tail, j2,m2,y2, age = 
if d then
    j,m,y,oj,om,oy,tail=DecodeDate(d:gsub('−','-'));
    if not (j or y) then
        return (frame.args.nocat and d or d..)
    end
end;
if d2 then
    j2,m2,y2 = DecodeDate(d2:gsub('−','-'));
end;
return table.concat{
    FormDate(j,m,y,oj,om,oy,mo),
    ( (frame.args['nopersoncat'] or )~= or (f.args['nocat'] or )~= ) and  or table.concat{
        ,
        j and string.format(,catpref[mo],j,monthg[m]) or ,
        y and string.format(,catpref[mo],y,Year0(y,'году')) or 
    },--/table.concat внутр.
    (function(F)--возраст
        if not F then return  end;
        local n=F();
        return n and string.format(" (%i %s)%s",
            n,
            mw.getLanguage('ru'):plural(n,'год','года','лет'),
            n>150 and  or 
        ) or 
    end)( ({
        ['Рождения']=function()
            local now=os.date('*t');
            if (not d2 or d2==) and j and m and y then
                return Diffy(now.day,now.month,now.year,j,m,y)
            end
        end,
        ['Смерти']=function()
            return j and m and y and j2 and m2 and y2 and Diffy(j,m,y,j2,m2,y2);
        end,
    })[mo] ),--конец вызова функции возраста
    tail or ,
    cat
}--/table.concat внеш.

end;

formdate=function(f) -- Формирует дату по 3--6 параметрам #invoke или шаблона

   --не использовать с пустыми аргументами
   if (f.args[1] or )~= and (f.args[2] or )~= or (f.args[3] or )~= then
       return FormDate(f.args[1],f.args[2],f.args[3],f.args[4],f.args[5],f.args[6],f.args['m'])
   else
       local tf=f:getParent();
       return FormDate(tf.args[1],tf.args[2],tf.args[3],tf.args[4],tf.args[5],tf.args[6],tf.args['m'])
   end

end;

cmp=function(f)--Сравнивает две даты, результат соответственно -1, 0 или 1

   return Cmp(f.args[1],f.args[2])

end;

G2J=function(f)--перевод григорианских дат в юлианские, возврат DD.MM.YYYY --Не знает про 15 октября 1582 года, не работает до нашей эры и после ???99 года --Если есть второй аргумент, преобразует только ДО этой даты включительно --Если есть третий аргумент, результат форматирует под Персона/Дата

   local d,m,y=GetDate(f.args[1])
   if f.args[2] and Cmp(f.args[1],f.args[2])==1 then
       return string.format("%i.%i.%i",d,m,y)
   end
   local shift=math.floor(y/100)-math.floor(y/400)-2
   if d-shift>0 then
       return f.args[3] and string.format("%i.%i.%i (%i)",d,m,y,d-shift)
       or string.format("%i.%i.%i",d-shift,m,y)
   else
       if m==1 then
           return f.args[3]
           and string.format("%i.1.%i (%i.12.%i)",d,y,31+d-shift,y-1)
           or string.format("%i.12.%i",31+d-shift,y-1)
       elseif m==3 then
           return f.args[3] and string.format("%i.3.%i (%i.2)", d,y,
               (y%4==0 and 29 or 28)+d-shift-(y%100==0 and y%400~=0 and 1 or 0)
           )
           or string.format("%i.2.%i",
               (y%4==0 and 29 or 28)+d-shift-(y%100==0 and y%400~=0 and 1 or 0)
           ,y)
       else
           return f.args[3] and string.format(
               "%i.%i.%i (%i.%i)", d,m,y, monthd[m-1]+d-shift,m-1
           )
           or string.format("%i.%i.%i",monthd[m-1]+d-shift,m-1,y)
       end
   end

end;

-- Переводит русскую дату в YYYY-MM-DD. Возвращает входное значение, если дата уже в этом формате yyyymmdd = function(f)

   local date, hourmin = f.args[1]
   if mw.ustring.match(date, "^%s*%d+-%d+-%d+") then
   	return date

end

   hourmin = mw.ustring.match(date, "%s+%d+:%d+$")
   local y, m, d = Yyyymmdd(date)
   if not y then
   	return 'Ошибка: некорректный формат даты.'

end

   return string.format('%4i-%02i-%02i', y, m, d) .. (hourmin or )

end }

function table.val_to_str ( v )

 if "string" == type( v ) then
   v = string.gsub( v, "\n", "\\n" )
   if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
     return "'" .. v .. "'"
   end
   return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
 else
   return "table" == type( v ) and table.tostring( v ) or
     tostring( v )
 end

end

function table.key_to_str ( k )

 if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
   return k
 else
   return "[" .. table.val_to_str( k ) .. "]"
 end

end

function table.tostring( tbl )

 local result, done = {}, {}
 for k, v in ipairs( tbl ) do
   table.insert( result, table.val_to_str( v ) )
   done[ k ] = true
 end
 for k, v in pairs( tbl ) do
   if not done[ k ] then
     table.insert( result,
       table.key_to_str( k ) .. "=" .. table.val_to_str( v ) )
   end
 end
 return "{" .. table.concat( result, "," ) .. "}"

end

local function parseISO8601Date(str) local pattern = "(%-?%d+)%-(%d+)%-(%d+)T" local Y, M, D = mw.ustring.match( str, pattern ) return tonumber(Y), tonumber(M), tonumber(D) end

local function parseISO8601Time(str) local pattern = "T(%d+):(%d+):(%d+)%Z" local H, M, S = mw.ustring.match( str, pattern) return tonumber(H), tonumber(M), tonumber(S) end

local function parseISO8601Offset(str) if str:sub(-1)=="Z" then return 0,0 end -- ends with Z, Zulu time

-- matches ±hh:mm, ±hhmm or ±hh; else returns nils local pattern = "([-+])(%d%d):?(%d?%d?)$" local sign, oh, om = mw.ustring.match( str, pattern) sign, oh, om = sign or "+", oh or "00", om or "00"

return tonumber(sign .. oh), tonumber(sign .. om) end

function p.parseISO8601(str) if 'table'==type(str) then if str.args and str.args[1] then str = .. str.args[1] else return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str ) end end local Y,M,D = parseISO8601Date(str) local h,m,s = parseISO8601Time(str) local oh,om = parseISO8601Offset(str) return tonumber(os.time({year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s})) end

local g2uBoundary1 = p.parseISO8601('1582-10-15T00:00:00Z') local g2uBoundary2 = p.parseISO8601('1700-03-12T00:00:00Z') local g2uBoundary3 = p.parseISO8601('1800-03-13T00:00:00Z') local g2uBoundary4 = p.parseISO8601('1900-03-14T00:00:00Z') local g2uBoundary5 = p.parseISO8601('1918-01-26T00:00:00Z') -- декрет Ленина

-- Передаваемое время обязано быть по Григорианскому календарю (новому стилю) function p.formatWiki( time, infocardClass, categoryNamePrefix ) if 'table'==type( time ) then if time.args and time.args[1] then time = tonumber( time.args[1] ) else return 'unknown argument type: ' .. type( time ) .. ': ' .. table.tostring( time ) end end local t = os.date("*t", time) if time < g2uBoundary1 then -- выводим просто юлианский календарь. Задавать тут григорианский некорректно return p.formatWikiImpl( t, t, infocardClass, categoryNamePrefix ) end

-- Специальные даты if t.year == 1700 and t.month == 3 and t.day == 11 then return p.formatWikiImpl( {year=1700, month=2, day=29}, t, infocardClass, categoryNamePrefix) end if t.year == 1800 and t.month == 3 and t.day == 12 then return p.formatWikiImpl( {year=1800, month=2, day=29}, t, infocardClass, categoryNamePrefix ) end if t.year == 1900 and t.month == 3 and t.day == 13 then return p.formatWikiImpl( {year=1900, month=2, day=29}, t, infocardClass, categoryNamePrefix ) end

if g2uBoundary1 <= time and time < g2uBoundary2 then return p.formatWikiImpl( os.date("*t", time - 10 * 24 * 60 * 60), t, infocardClass, categoryNamePrefix ) end if g2uBoundary2 <= time and time < g2uBoundary3 then return p.formatWikiImpl( os.date("*t", time - 11 * 24 * 60 * 60), t, infocardClass, categoryNamePrefix ) end if g2uBoundary3 <= time and time < g2uBoundary4 then return p.formatWikiImpl( os.date("*t", time - 12 * 24 * 60 * 60), t, infocardClass, categoryNamePrefix ) end if g2uBoundary4 <= time and time < g2uBoundary5 then return p.formatWikiImpl( os.date("*t", time - 13 * 24 * 60 * 60), t, infocardClass, categoryNamePrefix ) end

--только Григорианский календарь return p.formatWikiImpl( t, t, infocardClass, categoryNamePrefix ) end

local function ternary ( cond , T , F )

   if cond then return T else return F end

end

local nominativeMonthes = {'pakkaskuu', 'tuhukuu', 'kevätkuu', 'sulakuu', 'oraskuu', 'kezäkuu',

   'heinykuu', 'elokuu', 'syvyskuu', 'ligakuu', 'kylmykuu', 'talvikuu'}
   

local genitivusMonthes = {'pakkaskuudu', 'tuhukuudu', 'kevätkuudu', 'sulakuudu', 'oraskuudu', 'kezäkuudu',

   'heinykuudu', 'elokuudu', 'syvyskuudu', 'ligakuudu', 'kylmykuudu', 'talvikuudu'}

local function nominativeYear( year, nolinks )

   if ( year >= 0 ) then
       return nolinks and year or '' .. year .. ''
   else
       return nolinks and ( 0 - year ) .. ' eaa.' or '' .. ( 0 - year ) .. ' eaa.'
   end

end

local function inYear( year )

   if ( year >= 0 ) then
       return ' vuvvennu ' .. year .. 
   else
       return ' vuvvennu ' .. ( 0 - year) .. ' eaa.'
   end

end

function p.formatWikiImpl( t1, t2, infocardClass, categoryNamePrefix, leftBracket, rightBracket, nolinks )

   local nd = t2.day;
   local nm = t2.month;
   local ny = t2.year;
   local od = ternary ( t1.day ~= t2.day , t1.day, nil );
   local om = ternary ( t1.month ~= t2.month , t1.month, nil );
   local oy = ternary ( t1.year ~= t2.year , t1.year, nil );

if leftBracket == nil then leftBracket = '(' end if rightBracket == nil then rightBracket = ')' end

local JulianComment = function(s) return tostring(mw.html.create("abbr") :attr("title","по юлианскому календарю") :wikitext(s) :done()) end


   local template =
       (nd ~= nil and "1" or "") .. (nm ~= nil and "2" or "") .. (ny ~= nil and "3" or "") ..
       (od ~= nil and "4" or "") .. (om ~= nil and "5" or "") .. (oy ~= nil and "6" or "")

   local datePart = ''
   if (template == "12") then
       datePart = datePart .. string.format( nolinks and "%d %s" or "%d. %s",
       								nd, genitivusMonthes[nm] )
   elseif (template == "23") then
       datePart = datePart .. string.format( "%s %s",
       								nominativeMonthes[nm], nominativeYear( ny, nolinks ) )
   elseif (template == "3") then
       datePart = datePart .. nominativeYear( ny, nolinks )
   elseif (template == "123") then
       datePart = datePart .. string.format( nolinks and "%d %s %s" or "%d. %s %s",
                                       nd, genitivusMonthes[nm], nominativeYear( ny, nolinks ) )
   elseif (template == "124") then
   	if nolinks then
   		datePart = datePart .. JulianComment(string.format( "%d", od )

).. string.format( " " .. leftBracket .. "%d" .. rightBracket .. " %s",

                                       nd, genitivusMonthes[nm] )
       else
   		datePart = datePart .. JulianComment(string.format( "%d", od )

).. string.format( " " .. leftBracket .. "%d" .. rightBracket .. " %s",

                                       nd, genitivusMonthes[nm], nd, genitivusMonthes[nm] )
       end
   elseif (template == "1234") then
   	if nolinks then
       	datePart = datePart .. JulianComment(string.format( "%d", od )

).. string.format( " |" .. leftBracket .. "%d" .. rightBracket .. " %s %s",

                                       nd, genitivusMonthes[nm], nominativeYear( ny, nolinks ) )
       else
       	datePart = datePart .. JulianComment(string.format( "%d", od )

).. string.format( " " .. leftBracket .. "%d" .. rightBracket .. " %s %s",

                                       nd, genitivusMonthes[nm], nd, genitivusMonthes[nm], nominativeYear( ny, nolinks ) )
       end
   elseif (template == "1245") then
       datePart = datePart .. JulianComment(string.format( "%d. %s", od, genitivusMonthes[om] )

).. string.format(" " .. leftBracket .. (nolinks and "%d %s" or "%d. %s") .. rightBracket .. "", nd, genitivusMonthes[nm] )

   elseif (template == "12345") then
       datePart = datePart .. JulianComment(string.format( "%d. %s", od, genitivusMonthes[om] )

).. string.format(" " .. leftBracket .. (nolinks and "%d %s" or "%d %s") .. rightBracket .. " %s", nd, genitivusMonthes[nm], nominativeYear( ny, nolinks ) )

   elseif (template == "123456") then
       datePart = datePart .. JulianComment(string.format( "%d %s %d", od, genitivusMonthes[om], oy ))

.. ' ' .. string.format(" " .. leftBracket .. (nolinks and "%d %s %s" or "%d %s %s") .. rightBracket , nd, genitivusMonthes[nm], nominativeYear( ny, nolinks ) )

   else
       datePart = datePart .. 'формат неверен'
   end
   datePart = datePart .. ''

   local infocardTemplate =
       (nd ~= nil and "1" or "") .. (nm ~= nil and "2" or "") .. (ny ~= nil and "3" or "")

if infocardClass then if (infocardTemplate == "123") then datePart = datePart .. string.format('(%04d-%02d-%02d)', infocardClass , ny , nm , nd ) elseif (infocardTemplate == "23") then datePart = datePart .. string.format('(%04d-%02d)', infocardClass , ny , nm ) elseif (infocardTemplate == "3") then datePart = datePart .. string.format('(%04d)', infocardClass , ny ) end end

   if categoryNamePrefix then
       if ( nd ~= nil and nm ~= nil) then
           datePart = datePart .. '[[Category:' .. categoryNamePrefix .. ' ' .. nd .. '. ' .. genitivusMonthes[nm] .. ']]'
       end
       if ( ny ~= nil) then
           datePart = datePart .. 
       end
   end

   return datePart

end

return p