Module:HebrewDate
Appearance
local p = {}
-- ==========================================================
-- 1. CORE CALENDAR MATH
-- ==========================================================
local hebrew_epoch = -1373427
local function div(a, b) return math.floor(a / b) end
local function mod(a, b) return a - math.floor(a / b) * b end
local function elapsed_days(year)
local months = div(235 * year - 234, 19)
local parts = 12084 + 13753 * months
local day = 29 * months + div(parts, 25920)
-- Dehuiyot: adjust if molad falls on Sunday, Wednesday, or Friday
if mod(3 * (day + 1), 7) < 3 then
return day + 1
end
return day
end
local function new_year_delay(year)
local n0 = elapsed_days(year - 1)
local n1 = elapsed_days(year)
local n2 = elapsed_days(year + 1)
if (n2 - n1) == 356 then return 2 end
if (n1 - n0) == 382 then return 1 end
return 0
end
local function hebrew_new_year(year)
return hebrew_epoch + elapsed_days(year) + new_year_delay(year)
end
local function is_greg_leap(y)
return (mod(y, 4) == 0 and mod(y, 100) ~= 0) or (mod(y, 400) == 0)
end
local function gregorian_to_fixed(y, m, d)
local days = 365 * (y - 1) + div(y - 1, 4) - div(y - 1, 100) + div(y - 1, 400)
local month_days = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}
days = days + month_days[m]
if m > 2 and is_greg_leap(y) then days = days + 1 end
return days + d
end
local function get_month_length(month, is_leap, year_len)
if month == 13 then return is_leap and 29 or 0 end
if month == 12 then return is_leap and 30 or 29 end
if month == 2 or month == 4 or month == 6 or month == 10 then return 29 end
if month == 8 then return (year_len == 355 or year_len == 385) and 30 or 29 end
if month == 9 then return (year_len == 353 or year_len == 383) and 29 or 30 end
return 30
end
-- ==========================================================
-- 2. MAIN EXECUTION
-- ==========================================================
function p.main(frame)
-- Get current UTC time
local current_time = os.time()
local t = os.date("!*t", current_time)
-- If it is 6:00 PM (18:00) UTC or later, roll forward to the next day
if t.hour >= 18 then
current_time = current_time + 86400
t = os.date("!*t", current_time)
end
-- Convert Gregorian date to Rata Die (Fixed days since 1 CE)
local rd = gregorian_to_fixed(t.year, t.month, t.day)
-- Find the corresponding Hebrew Year
local appx = 1 + div((rd - hebrew_epoch) * 98496, 35975351)
local Y = appx - 1
while hebrew_new_year(Y + 1) <= rd do
Y = Y + 1
end
-- Determine year characteristics
local is_leap = mod(1 + 7 * Y, 19) < 7
local year_len = hebrew_new_year(Y + 1) - hebrew_new_year(Y)
-- Iterate through the months to find the current month and day
local seq = {7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6}
local days_remaining = rd - hebrew_new_year(Y)
local H_month = 7
for _, m in ipairs(seq) do
local m_len = get_month_length(m, is_leap, year_len)
if m_len > 0 then
if days_remaining < m_len then
H_month = m
break
else
days_remaining = days_remaining - m_len
end
end
end
local H_day = days_remaining + 1
-- Map month number to text
local month_names = {
[1] = "Nisan", [2] = "Iyyar", [3] = "Sivan", [4] = "Tammuz",
[5] = "Av", [6] = "Elul", [7] = "Tishri", [8] = "Marheshvan",
[9] = "Kislev", [10] = "Tevet", [11] = "Shevat", [12] = "Adar", [13] = "Adar II"
}
local date_no_year = H_day .. " " .. month_names[H_month]
local date_with_year = date_no_year .. " " .. Y
return "[[" .. date_no_year .. "|" .. date_with_year .. "]]"
end
return p