Mine sisu juurde

Modul':Пасха

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

local m = {}

local EasterData = {

   defaultMethod = 2,        -- метод пасхалии по умолчанию, если не указан тип
   defaultFormat = "Y-m-d",  -- формат вывода даты по умолчанию
   noFormat      = "none",   -- предотвратить форматирование окончательной даты
   defaultOffset = 0,        -- дата Пасхи
   minimumOffset = -71,      -- Неделя о мытаре и фарисее
   maximumOffset = 70,       -- Неделя 3-я по Пятидесятнице

   -- API
   apiEaster            = "Calculate", -- открытое функционное имя
   argEasterYear        = 1,           -- индекс или имя аргумента с годом
   argEasterMethod      = "method",    -- индекс или имя аргумента с методом пасхалии
   argEasterOffset      = "day",       -- индекс или имя аргумента со смещением в днях относительно расчитанного пасхального воскресенья
   argEasterFormat      = "format",    -- индекс или имя аргумента с форматом вывода даты (#time style)

   -- errors
   errorMissingYear     = "Отсутствует обязательный аргумент с годом",
   errorInvalidYear     = "Неверный аргумент со значением года '%s'",
   errorInvalidOffset   = "Неверный аргумент 'day': '%s'",
   errorInvalidMethod   = "Неверный аргумент 'method': '%s'",
   errorYearOutOfRange  = "Пасхальные даты доступны между 326 и 4099 годами; год: %d",
   errorIncorrectMethod = "Западная или православная Пасха существует с 1583 года; год: %d",
   errorUnknownMethod   = "Неизвестный метод пасхалии: %d",

   methods = {
       ["Julian"]      = 1,
       ["Старый стиль"]= 1, -- синоним для Julian
       ["Eastern"]     = 2,
       ["Orthodox"]    = 2, -- синоним для Eastern
       ["Восточная традиция"]  = 2, -- синоним для Eastern
       ["Western"]     = 3,
       ["Roman"]       = 3, -- синоним для Roman
       ["Западная традиция"]  = 3, -- синоним для Roman
   },

   relativeDates = {
       ["Семидесятница"]            = -63,
       ["Septuagesima"]             = -63,
       ["Sexagesima"]               = -56,
       ["Fat Thursday"]             = -52,
       ["Прощёное воскресенье"]     = -49,
       ["Quinquagesima"]            = -49,
       ["Чистый понедельник"]       = -48,
       ["Пепельная среда"]          = -46,
       ["Ash Wednesday"]            = -46,
       ["Вербное воскресенье"]      =  -7,
       ["Palm Sunday"]              =  -7,
       ["Великий четверг"]          =  -3,
       ["Maundy Thursday"]          =  -3,
       ["Великая пятница"]          =  -2,
       ["Good Friday"]              =  -2,
       ["Великая суббота"]          =  -1, 
       ["Holy Saturday"]            =  -1, 
       ["Пасха"]                    =   0,
       ["Easter"]                   =   0,
       ["Вознесение"]               =  39,
       ["Ascension Thursday"]       =  39,
       ["Троица"]                   =  49,
       ["Pentecost"]                =  49,
       ["Corpus Christi"]           =  60,
       ["Праздник Тела и Крови Христовых"]=60,
   },

}

local function formatEasterError(message, ...)

   if select('#', ... ) > 0 then
       message = string.format(message, ...)
   end
   return "" .. message .. ""

end

local function loadEasterYear(year)

   if not year then
       return false, formatEasterError(EasterData.errorMissingYear)
   end
   local result = tonumber(year)
   if not result or math.floor(result) ~= result then
       return false, formatEasterError(EasterData.errorInvalidYear, year)
   end

   return true, result

end

local function loadEasterMethod(method, year)

   local result = EasterData.defaultMethod
   if method then
       result = EasterData.methods[method]
       if not result then
           return false, formatEasterError(EasterData.errorInvalidMethod, method)
       end
   end

   if year < 1583 then
       result = 1
   end

   return true, result

end

local function loadEasterOffset(day)

   if not day then
       return true, ""
   end

   local data = EasterData.relativeDates
   local offset = tonumber(day)
   if not offset then
       offset = data[day]
   end
   if not offset or offset ~= math.floor(offset) or offset < EasterData.minimumOffset or offset > EasterData.maximumOffset then
       return false, formatEasterError(EasterData.errorInvalidOffset, day)
   end

   if offset < -1 then
       return true, string.format(" %d days", offset)
   elseif offset == -1 then
       return true, " -1 day"
   elseif offset == 0 then
       return true, ""
   elseif offset == 1 then
       return true, " +1 day"
   else -- if offset > 1 then
       return true, string.format(" +%d days", offset)
   end

end

local function loadEasterFormat(fmt)

   if fmt == EasterData.noFormat then
       return true, nil
   elseif not fmt then
       return true, EasterData.defaultFormat
   else
       return true, fmt
   end

end

--[[

PURPOSE:     This function returns Easter Sunday day and month
             for a specified year and method.

INPUTS:      Year   - Any year between 326 and 4099.
             Method - 1 = the original calculation based on the
                          Julian calendar
                      2 = the original calculation, with the
                          Julian date converted to the
                          equivalent Gregorian calendar
                      3 = the revised calculation based on the
                          Gregorian calendar

OUTPUTS:     None.

RETURNS:     0, error message - Error; invalid arguments
             month, day       - month and day of the Sunday

NOTES:
             The code is translated from DN OSP 6.4.0 sources.
             The roots of the code might be found in
             http://www.gmarts.org/index.php?go=415

ORIGINAL NOTES:

             This algorithm is an arithmetic interpretation
             of the 3 step Easter Dating Method developed
             by Ron Mallen 1985, as a vast improvement on
             the method described in the Common Prayer Book

             Published Australian Almanac 1988
             Refer to this publication, or the Canberra Library
             for a clear understanding of the method used

             Because this algorithm is a direct translation of
             the official tables, it can be easily proved to be
             100% correct

             It's free! Please do not modify code or comments!

]] local function calculateEasterDate(year, method)

   if year < 326 or year > 4099 then
       -- Easter dates are valid for years between 326 and 4099
       return 0, formatEasterError(EasterData.errorYearOutOfRange, year)
   end
   if year < 1583 and method ~= 1 then
       -- Western or Orthodox Easter is valid since 1583
       return 0, formatEasterError(EasterData.errorIncorrectMethod, year)
   end

   -- intermediate result
   local firstDig = math.floor(year / 100)
   local remain19 = year % 19
   local temp = 0
   -- table A to E results
   local tA = 0
   local tB = 0
   local tC = 0
   local tD = 0
   local tE = 0
    -- Easter Sunday day
   local d = 0

   if method == 1 or method == 2 then
       -- calculate PFM date
       tA   = ((225 - 11 * remain19) % 30) + 21
       -- find the next Sunday
       tB   = (tA - 19) % 7
       tC   = (40 - firstDig) % 7
       temp = year % 100
       tD   = (temp + math.floor(temp / 4)) % 7
       tE   = ((20 - tB - tC - tD) % 7) + 1
       d    = tA + tE
       if method == 2 then
           -- convert Julian to Gregorian date
           -- 10 days were skipped in the Gregorian calendar from 5-14 Oct 1582
           temp = 10
           -- only 1 in every 4 century years are leap years in the Gregorian
           -- calendar (every century is a leap year in the Julian calendar)
           if year > 1600 then
               temp = temp + firstDig - 16 - math.floor((firstDig - 16) / 4)
           end
           d = d + temp
       end
   elseif method == 3 then
       -- calculate PFM date
       temp = math.floor((firstDig - 15) / 2)  + 202 - 11 * remain19
       if firstDig > 26 then
           temp = temp - 1
       end
       if firstDig > 38 then
           temp = temp - 1
       end
       if firstDig == 21 or firstDig == 24 or firstDig == 25 or firstDig == 33 or firstDig == 36 or firstDig == 37 then
           temp = temp - 1
       end
       temp = temp % 30
       tA   = temp + 21
       if temp == 29 then
           tA = tA - 1
       end
       if temp == 28 and remain19 > 10 then
           tA = tA - 1
       end
       -- find the next Sunday
       tB   = (tA - 19) % 7
       tC   = (40 - firstDig) % 4
       if tC == 3 then
           tC = tC + 1
       end
       if tC > 1 then
           tC = tC + 1
       end
       temp = year % 100
       tD   = (temp + math.floor(temp / 4)) % 7
       tE   = ((20 - tB - tC - tD) % 7) + 1
       d    = tA + tE
   else
       -- Unknown method
       return 0, formatEeasteError(EasterData.errorUnknownMethod, method)
   end
   if d > 61 then
       -- when the original calculation is converted to the Gregorian
       -- calendar, Easter Sunday can occur in May
       return 5, d - 61
   elseif d > 31 then
       return 4, d - 31
   else
       return 3, d
   end

end

local function Easter(args)

   local ok
   local year
   ok, year = loadEasterYear(args[EasterData.argEasterYear])
   if not ok then
       return year
   end

   local method
   ok, method = loadEasterMethod(args[EasterData.argEasterMethod], year)
   if not ok then
       return method
   end

   local offset
   ok, offset = loadEasterOffset(args[EasterData.argEasterOffset])
   if not ok then
       return offset
   end

   local format
   ok, format = loadEasterFormat(args[EasterData.argEasterFormat])
   if not ok then
       return format
   end

   local month, day = calculateEasterDate(year, method)
   if month == 0 then
       return day
   end

   local result = string.format("%04d-%02d-%02d%s", year, month, day, offset)
   if format then
       result = mw.language.getContentLanguage():formatDate(format, result)
   end

   return result

end

m[EasterData.apiEaster] = function (frame)

   return Easter(frame.args)

end

return m