From 12a8febabf1d5fe90d7c623377507d28a0f0fc80 Mon Sep 17 00:00:00 2001
From: daurnimator <quae@daurnimator.com>
Date: Mon, 6 Jan 2014 15:56:55 -0500
Subject: [PATCH] luatz/timetable: Use sakamoto's algorithm instead of doomsday
 algorithm

---
 luatz/timetable.lua     | 33 +++++++++++----------------------
 spec/timetable_spec.lua | 13 +++----------
 2 files changed, 14 insertions(+), 32 deletions(-)

diff --git a/luatz/timetable.lua b/luatz/timetable.lua
index 8c44f2f..e1649a0 100644
--- a/luatz/timetable.lua
+++ b/luatz/timetable.lua
@@ -11,6 +11,8 @@ local months_to_days_cumulative = { 0 }
 for i = 2, 12 do
 	months_to_days_cumulative [ i ] = months_to_days_cumulative [ i-1 ] + mon_lengths [ i-1 ]
 end
+-- For Sakamoto's Algorithm (day of week)
+local sakamoto = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
 
 local function is_leap ( y )
 	return (y % 4) == 0 and (y % 100) ~= 0 or (y % 400) == 0
@@ -32,19 +34,6 @@ local function leap_years_since ( year )
 	return idiv ( year , 4 ) - idiv ( year , 100 ) + idiv ( year , 400 )
 end
 
-local function doomsday ( year )
-	return ( 3 -- Tuesday
-		- 1 + year + leap_years_since ( year ) )
-		% 7 + 1
-end
-local doomsday_cache = setmetatable ( { } , {
-	__index = function ( cache , year )
-		local d = doomsday ( year )
-		cache [ year ] = d
-		return d
-	end ;
-} )
-
 local function day_of_year ( day , month , year )
 	local yday = months_to_days_cumulative [ month ]
 	if month > 2 and is_leap ( year ) then
@@ -53,8 +42,11 @@ local function day_of_year ( day , month , year )
 	return yday + day
 end
 
-local function day_of_week ( yday , year )
-	return ( yday - doomsday_cache [ year ] - 1 ) % 7 + 1
+local function day_of_week ( day , month , year )
+	if month < 3 then
+		year = year - 1
+	end
+	return ( year + leap_years_since ( year ) + sakamoto[month] + day ) % 7 + 1
 end
 
 local function increment ( tens , units , base )
@@ -135,12 +127,8 @@ function timetable_methods:normalise ( )
 	self.day   = day
 	self.month = month
 	self.year  = year
-
-	local yday = day_of_year ( day , month , year )
-	local wday = day_of_week ( yday , year )
-
-	self.yday = yday
-	self.wday = wday
+	self.yday  = day_of_year ( day , month , year )
+	self.wday  = day_of_week ( day , month , year )
 
 	return self
 end
@@ -201,7 +189,8 @@ local function new_from_timestamp ( ts )
 end
 
 return {
-	doomsday  = doomsday ;
+	day_of_year = day_of_year ;
+	day_of_week = day_of_week ;
 	normalise = normalise ;
 	timestamp = timestamp ;
 
diff --git a/spec/timetable_spec.lua b/spec/timetable_spec.lua
index 4cadbbc..c81308e 100644
--- a/spec/timetable_spec.lua
+++ b/spec/timetable_spec.lua
@@ -1,16 +1,6 @@
 describe ( "Time table library" , function ( )
 	local timetable = require "luatz.timetable"
 
-	it ( "Doomsday calculation" , function ( )
-		local doomsday = timetable.doomsday
-
-		-- Doomsday in Gregorian calendar for 2013 is Thursday.
-		assert.are.same ( 5 , doomsday(2013) )
-
-		assert.are.same ( 3 , doomsday(1967) )
-		assert.are.same ( 5 , doomsday(1968) )
-	end )
-
 	local function native_normalise ( year , month , day )
 		return os.date("*t",os.time{
 			year = year ;
@@ -35,6 +25,9 @@ describe ( "Time table library" , function ( )
 		assert_same_wday ( 2013 , 7 , 27 )
 		assert_same_wday ( 2013 , 7 , 28 )
 		assert_same_wday ( 2013 , 7 , 29 )
+		assert_same_wday ( 2014 , 1 , 1 )
+		assert_same_wday ( 2014 , 1 , 6 )
+		assert_same_wday ( 2016 , 2 , 28 )
 	end )
 
 	local function native_timestamp ( year , month , day )
-- 
2.39.5