Skip to content

Instantly share code, notes, and snippets.

@socantre
Created February 19, 2015 18:13
Show Gist options
  • Save socantre/c9dfcc5bd106a601d395 to your computer and use it in GitHub Desktop.
Save socantre/c9dfcc5bd106a601d395 to your computer and use it in GitHub Desktop.
days_from_civil implemented in Lua
--[[
http://howardhinnant.github.io/date_algorithms.html
Returns number of days since civil 1970-01-01. Negative values indicate
days prior to 1970-01-01.
Preconditions: y-m-d represents a date in the civil (Gregorian) calendar
m is in [1, 12]
d is in [1, last_day_of_month(y, m)]
y is "approximately" in
[numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366]
Exact range of validity is:
[civil_from_days(numeric_limits<Int>::min()),
civil_from_days(numeric_limits<Int>::max()-719468)]
]]
function days_from_civil(y, m, d)
if m <= 2 then -- adjust so that leap days are at the end of leap years
y = y - 1
m = m + 9
else
m = m - 3
end
local era = math.floor(y / 400)
local yoe = y - era * 400 -- [0, 399]
local doy = math.modf((153*m + 2)/5) + d-1 -- [0, 365]
local doe = yoe * 365 + math.modf(yoe/4) - math.modf(yoe/100) + doy -- [0, 146096]
return era * 146097 + doe - 719468
end
local reference_date = {year=2001, month = 1, day = 1}
local date = os.date("*t")
local reference_days = days_from_civil(reference_date.year, reference_date.month, reference_date.day)
local days = days_from_civil(date.year, date.month, date.day)
print(string.format("Today is %d days into the 21st century.",days-reference_days))
-- Returns: true if y is a leap year in the civil calendar, else false
function is_leap(y)
return math.fmod(y, 4) == 0 and (math.fmod(y, 100) ~= 0 or math.fmod(y, 400) == 0)
end
--[[
Preconditions: m is in [1, 12]
Returns: The number of days in the month m of common year
The result is always in the range [28, 31].
]]
function last_day_of_month_common_year(m)
local a = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
return a[m]
end
--[[
Preconditions: m is in [1, 12]
Returns: The number of days in the month m of year y
The result is always in the range [28, 31].
]]
function last_day_of_month(y, m)
return (m ~= 2 or not is_leap(y)) and last_day_of_month_common_year(m) or 29
end
local previous_days = days_from_civil(-1001, 12, 31)
for year = -1000, 3000 do
for month = 1, 12 do
for day = 1, last_day_of_month(year, month) do
local days = days_from_civil(year, month, day)
if previous_days + 1 ~= days then
print("Error", year, month, day)
end
previous_days = days
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment