Mô đun:Easter

local m = {}

local EasterData = {
defaultMethod = 3, -- default method of Easter date calculation when Easter type is not given
defaultFormat = "Y-m-d", -- default date output format
noFormat = "none", -- prevent from final date formatting
defaultOffset = 0, -- the Easter date
minimumOffset = -63, -- Septuagesima
maximumOffset = 68, -- Feast of the Sacred Heart

-- API
apiEaster = "Calculate", -- public function name
argEasterYear = 1, -- index or name of the argument with year
argEasterMethod = "method", -- index or name of the argument with calculation method
argEasterOffset = "day", -- index or name of the argument with offset in days relative to the calculated Easter Sunday
argEasterFormat = "format", -- index or name of the argument with date output format (time style)

-- errors
errorMissingYear = "Thiếu đối số bắt buộc có năm",
errorInvalidYear = "Đối số có giá trị năm không chính xác: “%s”",
errorInvalidOffset = "Đối số “day” không chính xác: “%s”",
errorInvalidMethod = "Đối số “method” không chính xác: “%s”",
errorYearOutOfRange = "Các ngày tháng Lễ Phục Sinh có sẵn từ 326 đến 4099; năm: %d",
errorIncorrectMethod = "Lễ Phục sinh phương Tây hoặc Chính Thống giáo có sẵn từ 1583; năm: %d",
errorUnknownMethod = "Phương pháp không rõ: %d",

methods = {
["Julian"] = 1,
["Julius"] = 1,
["Juliêng"] = 1,
["Eastern"] = 2,
["Đông phương"] = 2,
["Đông Phương"] = 2,
["phương Đông"] = 2,
["Phương Đông"] = 2,
["Orthodox"] = 2, -- alias for Eastern
["Chính Thống giáo"] = 2,
["Western"] = 3,
["Tây phương"] = 3,
["Tây Phương"] = 3,
["phương Tây"] = 3,
["Roman"] = 3, -- alias for Roman
["La Mã"] = 3,
["Rôma"] = 3,
["Roma"] = 3,
},

relativeDates = {
["Septuagesima"] = -63,
["Sexagesima"] = -56,
["Fat Thursday"] = -52,
["Quinquagesima"] = -49,
["Ash Wednesday"] = -46,
["Thứ tư Lễ Tro"] = -46,
["Thứ 4 Lễ Tro"] = -46,
["Lễ Tro"] = -46,
["Palm Sunday"] = -7,
["Chúa nhật Lễ Lá"] = -7,
["Chủ nhật Lễ Lá"] = -7,
["Lễ Lá"] = -7,
["Maundy Thursday"] = -3,
["Thứ năm Tuần Thánh"] = -3,
["Thứ 5 Tuần Thánh"] = -3,
["Good Friday"] = -2,
["Thứ sáu Tuần Thánh"] = -2,
["Thứ 6 Tuần Thánh"] = -2,
["Thứ sáu Tốt lành"] = -2,
["Thứ 6 Tốt lành"] = -2,
["Holy Saturday"] = -1,
["Thứ bảy Tuần Thánh"] = -1,
["Thứ 7 Tuần Thánh"] = -1,
["Canh thức Phục Sinh"] = -1,
["Easter"] = 0,
["Lễ Phục Sinh"] = 0,
["Easter Monday"] = 1,
["Ascension Thursday"] = 39,
["Lễ Thăng Thiên"] = 39,
["Pentecost"] = 49,
["Lễ Chúa Thánh Thần Hiện Xuống"] = 49,
["Lễ Hiện xuống"] = 49,
["Lễ Giáng xuống"] = 49,
["Lễ Hạ trần"] = 49,
["Corpus Christi"] = 60,
["Lễ Mình Máu Thánh Chúa"] = 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 EasterData.maximumOffset then
return false, formatEasterError(EasterData.errorInvalidOffset, day)
end

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 4099 then
-- Easter dates are valid for years between 326 and 4099
return 0, formatEasterError(EasterData.errorYearOutOfRange, year)
end
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 oryginal calculation os 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