Module:Sandbox/DixonD/Datetime/Gregorian

local Error = require('Module:Error')

local this = {}; function this.julianDayOfDate(date) -- http://www.tondering.dk/claus/cal/julperiod.php#formula local y = date.year + 4799;                    -- Set epoch in 1 March -4800, but counting months from January as the 10th month of previous year. local m = y * 12 + date.month + 9;             -- Total number of months since epoch (January = 10, December = 21). y = math.floor(m / 12); m = m - y * 12;        -- Split total months into relative months... return math.floor(y * 1461 / 4)                 -- Julian cycle of 1461 days every 4 years. - math.floor(y / 100) + math.floor(y / 400) -- Gregorian adjustement for non-leap years. + math.floor((m + 4) * 153 / 5)            -- Days in relative month (need to substract 122 at end). + date.day                                 -- Additional days (need to substract 1 at end). + (date.hour - 12) / 24                    -- The time part. + date.minute / 1440 + date.second / 86400                      -- (leap seconds ignored, every day assumed to be 86400 seconds exactly). - 32167;                                   -- Final substractive constant 32167 == 32044 + 122 + 1. end;

function this.dateOfJulianDay(JD) -- check parameters if JD == nil then return Error.error{"Parameter 1=Julian day is either missing or has wrong format!"}; end; local date = {}; date.weekday = JD + 0.5 - math.floor((JD + 0.5) / 7) * 7; -- 0 = Monday, 6 = Sunday.

-- See Formules de calcul du calendrier grégorien on Wikipedia for explained details and intermediate values in test cases. JD = JD + 32044.5; local j = math.floor(JD); -- No rounding performed for the precision of time; if you need it, round the JD before calling this method. -- 146097 days per cycle of 400 years local g = math.floor(j / 146097); local dg = j - g * 146097; -- 36524 days per cycle of 100 years (cycle numbers: 0 to 4; but 4 is reduced to 3, making sometimes the last cycle longer by 1 day). -- Note: for c in 0..4: math.min(c, 3) is equal to math.floor((c * 3 + 1) / 4); this avoids conditional branches: --      0 -> 0 = floor(1 / 4); 1 -> 1 = floor(4 / 4); 2 -> 2 = floor(7 / 4); 3 -> 3 = floor(10 / 4); 4 -> 4 = floor(13 / 4). local c = math.floor((math.floor(dg / 36524) * 3 + 1) / 4); local dc = dg - c * 36524; -- 1461 days per cycle of 4 years (cycle numbers: 0 to 24; no adjustment needed). local b = math.floor(dc / 1461); local db = dc - b * 1461; -- 365 days per cycle of 1 year (cycle numbers: 0 to 4; but 4 is reduced to 3, making sometimes the last cycle longer longer by 1 day). -- Note: for a in 0..4: math.min(a, 3) is equal to math.floor((a * 3 + 1) / 4); this avoids conditional branches: local a = math.floor((math.floor(db / 365) * 3 + 1) / 4); local da = db - a * 365; -- Relative date since epoch (1 March -4800) local y = g * 400 + c * 100 + b * 4 + a;   local m = math.floor((da * 5 + 308) / 153) - 2; local d = da - math.floor((m + 4) * 153 / 5) + 122; -- Actual Gregorian date date.year  = y - 4800 + math.floor((m + 2) / 12); date.month = m - math.floor((m + 2) / 12) * 12 + 3; date.day   = d + 1;--//Test:1 -- Actual time of day (leap seconds ignored, every day assumed to be 86400 seconds exactly). j = (JD + 0.5 - math.floor(JD + 0.5)) * 24; date.hour  = math.floor(j); j = (j - date.hour) * 60; date.minute = math.floor(j); date.second = (j - date.minute) * 60; -- No rounding performed; this includes fractions of seconds. return date; end;

function this.julianDay(year, month, day, hour, minute, second) -- Check parameters and set defaults. if year == nil then return Error.error{"Parameter 1=year is either missing or has wrong format!"}; end; if month == nil then month  = 1;  end; if day   == nil then day    = 1;  end; if hour  == nil then hour   = 12; end; if minute == nil then minute = 0; end; if second == nil then second = 0; end; return this.julianDayOfDate({year = year, month = month, day = day, hour = hour, minute = minute, second = second}); end;

-- Basic formatting (for debugging only ?). function this.wikiDateOfJulianDay(JD) -- Check parameters. if JD == nil then return Error.error{"Parameter 1=Julian day is either missing or has wrong format!"}; end;

local date = this.dateOfJulianDay(JD); return       tostring(date.year) .. "|" .. tostring(date.month) .. "|" .. tostring(date.day) .. "|" .. tostring(date.hour) .. "|" .. tostring(date.minute) .. "|" .. tostring(date.second); end;

-- English formatting (for display or tests ? not internationalized). function this.EnglishDateOfJulianDay(JD) -- Check parameters. if JD == nil then return Error.error{"Parameter 1=Julian day is either missing or has wrong format!"}; end;

local date = this.dateOfJulianDay(JD);

local s = ""; if (date >= 0 and date.day < 10) then s = s .. "0" .. tostring(date.day); else s = s .. tostring(date.day); end local months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };   s = s .. " " .. months[date.month - 1] .. " ";   if (date.year < 0) then s = s .. tostring(1 - date.year) .. " BC "; else s = s .. tostring(date.year)    .. " AD "; end if (date.hour >= 0 and date.hour < 10) then s = s .. "0" .. tostring(date.hour) .. ":";   else s = s .. tostring(date.hour) .. ":";   end if (date.minute >= 0 and date.minute < 10) then s = s .. "0" .. tostring(date.minute) .. ":";   else s = s .. tostring(date.minute) .. ":";   end if (date.second >= 0 and date.second < 10) then s = s .. "0" .. tostring(date.second); else s = s .. tostring(date.second); end return s .. " UTC"; end;

function this.yearOfJulianDay(JD) -- Check parameters. if JD == nil then return Error.error{"Parameter 1=Julian day is either missing or has wrong format!"}; end; return this.dateOfJulianDay(JD).year; end;

function this.monthOfJulianDay(JD) -- Check parameters. if JD == nil then return Error.error{"Parameter 1=Julian day is either missing or has wrong format!"}; end; return this.dateOfJulianDay(JD).month; end;

function this.dayOfJulianDay(JD) -- Check parameters. if JD == nil then return Error.error{"Parameter 1=Julian day is either missing or has wrong format!"}; end; return this.dateOfJulianDay(JD).day; end;

function this.weekdayOfJulianDay(JD) -- Check parameters. if JD == nil then return Error.error{"Parameter 1=Julian day is either missing or has wrong format!"}; end; return this.dateOfJulianDay(JD).weekday; end;

function this.hourOfJulianDay(JD) -- Check parameters. if JD == nil then return Error.error{"Parameter 1=Julian day is either missing or has wrong format!"}; end; return this.dateOfJulianDay(JD).hour; end;

function this.minuteOfJulianDay(JD) -- Check parameters. if JD == nil then return Error.error{"Parameter 1=Julian day is either missing or has wrong format!"}; end; return this.dateOfJulianDay(JD).minute; end;

function this.secondOfJulianDay(JD) -- Check parameters. if JD == nil then return Error.error{"Parameter 1=Julian day is either missing or has wrong format!"}; end; return this.dateOfJulianDay(JD).second; end;

return this;