While there is already a much simpler method for calculating the day of the week for any date using Lua’s built in os.date function, that has its limitations such as not working with dates before January 1st 1970, plus it’s not as fun; this led me to replicating the doomsday algorithm (devised in 1973) in order to work out the day of the week for any date regardless of whether it is 1000 years into the future, or 1000 years into the past. All feedback is appreciated and if you want to check out the doomsday algorithm for yourself, James Grime explains it in layman’s terms in this video: Doomsday Algorithm
local date = "9/12/2021" --Day/Month/Year
local function formatDay(day)
if day == 0 then
return "Sunday"
elseif day == 1 then
return "Monday"
elseif day == 2 then
return "Tuesday"
elseif day == 3 then
return "Wednesday"
elseif day == 4 then
return "Thursday"
elseif day == 5 then
return "Friday"
elseif day == 6 then
return "Saturday"
end
end
local function formatDate(date)
local year = date[3]
local month = date[2]
local day = date[1]
if day == "1" then
day = "1st"
elseif day == "2" then
day = "2nd"
elseif day == "3" then
day = "3rd"
elseif day == "21" then
day = "21st"
elseif day == "22" then
day = "22nd"
elseif day == "23" then
day = "23rd"
elseif day == "31" then
day = "31st"
else
day = day.."th"
end
local months = {
[1] = "January",
[2] = "February",
[3] = "March",
[4] = "April",
[5] = "May",
[6] = "June",
[7] = "July",
[8] = "August",
[9] = "September",
[10] = "October",
[11] = "November",
[12] = "December"
}
month = months[tonumber(month)]
return day.." "..month..", "..year
end
local function checkIsLeapYear(year)
if year/4 == math.round(year/4) then
if year/100 == math.round(year/100) then
if year/400 == math.round(year/400) then
return true
else
return false
end
else
return true
end
else
return false
end
end
local function checkDate(date)
local monthsWith30Days = {4,6,9,11}
local day = tonumber(date[1])
local month = tonumber(date[2])
local year = tonumber(date[3])
if day > 0 and day < 32 and month > 0 and month < 13 and year >= 1000 then
if table.find(monthsWith30Days, month) then
if day > 30 then
return false
else
return true
end
elseif month == 2 then
if checkIsLeapYear(year) then
if day > 29 then
return false
else
return true
end
else
if day > 28 then
return false
else
return true
end
end
end
else
return false
end
return true
end
local function calculateCenturyDoomsday(year)
local centuryStartingPoints = {
[1000] = 5,
[1100] = 3,
[1200] = 2,
[1300] = 0,
[1400] = 5,
[1500] = 3,
[1600] = 2,
[1700] = 0,
[1800] = 5,
[1900] = 3,
[2000] = 2,
[2100] = 0,
[2200] = 5,
[2300] = 3,
[2400] = 2,
[2500] = 0,
[2600] = 5,
[2700] = 3,
[2800] = 2,
[2900] = 0,
[3000] = 5
}
year = year:sub(1,2).."00"
year = tonumber(year)
if centuryStartingPoints[year] then
return centuryStartingPoints[year]
else
local yearsDifference = year - 3000
if yearsDifference%400 == 0 then
return 5
else
local remainder = yearsDifference%400
if remainder == 100 then
return 3
elseif remainder == 200 then
return 2
elseif remainder == 300 then
return 0
end
end
end
end
local function calculateYearDoomsday(year, centuryDoomsday)
local yearStartingPoints = {28,56,84}
year = year:sub(3)
year = tonumber(year)
if table.find(yearStartingPoints, year) then
return centuryDoomsday
else
local closestMultiple = math.floor(year/12)
local yearsToAddOn = year - (closestMultiple*12)
return (centuryDoomsday + yearsToAddOn + closestMultiple + math.floor(yearsToAddOn/4))%7
end
end
local function calculateDate(doomsdayForYear, date)
local doomsdays = {
{
Month = 3,
Day = 14
},
{
Month = 4,
Day = 4
},
{
Month = 5,
Day = 9
},
{
Month = 6,
Day = 6
},
{
Month = 7,
Day = 11
},
{
Month = 8,
Day = 8
},
{
Month = 9,
Day = 5
},
{
Month = 10,
Day =10
},
{
Month = 11,
Day = 7
},
{
Month = 12,
Day = 12
},
}
local year = tonumber(date[3])
local month = tonumber(date[2])
local day = tonumber(date[1])
local daysDifference
if checkIsLeapYear(year) then
if month == 1 then
if day > 4 then
daysDifference = day - 4
return (doomsdayForYear + daysDifference)%7
else
daysDifference = 4 - day
local weekdayForDate = 7 - (7 - (doomsdayForYear - daysDifference))
if weekdayForDate < 0 then
local formattedDate
repeat
formattedDate = 7 - math.abs(weekdayForDate)
until formattedDate > 0
return formattedDate
end
return weekdayForDate
end
elseif month == 2 then
daysDifference = 29 - day
return (doomsdayForYear + daysDifference)%7
end
else
if month == 1 then
if day > 3 then
daysDifference = day - 3
return (doomsdayForYear + daysDifference)%7
else
daysDifference = 3 - day
local weekdayForDate = 7 - (7 - (doomsdayForYear - daysDifference))
if weekdayForDate < 0 then
local formattedDate
repeat
formattedDate = 7 - math.abs(weekdayForDate)
until formattedDate > 0
return formattedDate
end
return weekdayForDate
end
elseif month == 2 then
daysDifference = 28 - day
return (doomsdayForYear + daysDifference)%7
else
local doomsdayForMonth
for _, specialDate in pairs(doomsdays) do
if specialDate.Month == month then
doomsdayForMonth = specialDate.Day
break
end
end
if day > doomsdayForMonth then
daysDifference = day - doomsdayForMonth
return (doomsdayForYear + daysDifference)%7
else
daysDifference = doomsdayForMonth - day
local weekdayForDate = 7 - (7 - (doomsdayForYear - daysDifference))
if weekdayForDate < 0 then
local formattedDate
repeat
formattedDate = 7 - math.abs(weekdayForDate)
until formattedDate > 0
return formattedDate
end
return weekdayForDate
end
end
end
end
local function workOutDate()
local splitDate = date:split("/")
if not checkDate(splitDate) then
if tonumber(splitDate[3]) < 1000 then
warn("Cannot compute years under 1000!")
else
warn(date.." is not an arbitrary date!")
end
return
end
local doomsdayForCentury = calculateCenturyDoomsday(splitDate[3])
local doomsdayForYear = calculateYearDoomsday(splitDate[3], doomsdayForCentury)
local date = calculateDate(doomsdayForYear, splitDate)
print(formatDate(splitDate).." is a "..formatDay(date))
end
workOutDate()