Module:Calendar

    From Commons

    Documentation for this module may be created at Module:Calendar/doc

    --[[
      __  __           _       _         ____      _                _            
     |  \/  | ___   __| |_   _| | ___ _ / ___|__ _| | ___ _ __   __| | __ _ _ __ 
     | |\/| |/ _ \ / _` | | | | |/ _ (_) |   / _` | |/ _ \ '_ \ / _` |/ _` | '__|
     | |  | | (_) | (_| | |_| | |  __/_| |__| (_| | |  __/ | | | (_| | (_| | |   
     |_|  |_|\___/ \__,_|\__,_|_|\___(_)\____\__,_|_|\___|_| |_|\__,_|\__,_|_|   
                                                                                 
    Maintainers:
    * Jarekt
    ]]
    
    local p = {}
    
    -- Convert "Julian day number" (jdn) to a calendar date
    -- "gregorian" is a 1 for gregorian calendar and 0 for Julian
    -- based on https://en.wikipedia.org/wiki/Julian_day#Converting_Julian_or_Gregorian_calendar_date_to_Julian_day_number
    function p._jdn2date(jdn, gregorian)
      local f, e, g, h, year, month, day
    
    	f = jdn + 1401
    	if gregorian>0 then
    		 f = f + math.floor((math.floor((4*jdn + 274277) / 146097) * 3) / 4) - 38
    	end
    	e = 4*f + 3
    	g = math.floor(math.fmod(e, 1461) / 4)
    	h = 5*g + 2
    	day   = math.floor(math.fmod (h,153) / 5) + 1
    	month = math.fmod (math.floor(h/153) + 2, 12) + 1
    	year  = math.floor(e/1461) - 4716 + math.floor((14 - month) / 12)
    	
    	-- If year is less than 1, subtract one to convert from a zero based date system to the
    	-- common era system in which the year -1 (1 B.C.E) is followed by year 1 (1 C.E.).
    	if year < 1 then
    			year = year - 1
    	end
    	
    	return string.format('%04i-%02i-%02i', year, month, day)
    end
    
    -- Convert calendar date to  "Julian day number" (jdn) 
    -- "gregorian" is a 1 for gregorian calendar and 0 for Julian
    -- based on  https://en.wikipedia.org/wiki/Julian_day#Converting_Julian_or_Gregorian_calendar_date_to_Julian_day_number
    -- explanation based on http://www.cs.utsa.edu/~cs1063/projects/Spring2011/Project1/project1.html
    function p._date2jdn(ISOdate, gregorian)
      
    	local year, month, day = ISOdate:match( "(-?%d%d%d%d)-(%d%d)-(%d%d)" )
    	if not year then
    		return nil
    	elseif tonumber(year) < 0 then
    		-- If year is less than 0, add one to convert from  the common era system in which
    	    -- the year -1 (1 B.C.E) is followed by year 1 (1 C.E.) to a zero based date system
    		year = year + 1
    	end
    	local a, b, c, d, y, m
    	a = math.floor((14-month) / 12) -- will be 1 for January and February, and 0 for other months.
    	y = year + 4800 - a             -- years since year –4800
    	m = month + 12*a - 3            -- month number where 10 for January, 11 for February, 0 for March, 1 for April
    	c = math.floor((153*m + 2)/5)   -- number of days since March 1
    	if gregorian>0 then	
    		b = math.floor(y/4) - math.floor(y/100) + math.floor(y/400) -- number of leap years since y==0 (year –4800)
    		d = 32045                     -- offset so the result will be 0 for January 1, 4713 BCE
    	else
    		b = math.floor(y/4)           -- number of leap years since y==0 (year –4800)
    		d = 32083                     -- offset so the result will be 0 for January 1, 4713 BCE
    	end
    	return day + c + 365*y + b - d
    end
    
    -- Convert a date from Gregorian to Julian calendar
    function p.Gregorian2Julian(frame)
    	local JDN = p._date2jdn(frame.args[1], 1)
    	if JDN then
    		return p._jdn2date(JDN, 0)
    	else
    		return "Error parsing input date: " .. frame.args[1]
    	end
    end
    
    -- Convert a date from Julian to Gregorian calendar
    function p.Julian2Gregorian(frame)
    	local JDN = p._date2jdn(frame.args[1], 0)
    	if JDN then
    		return p._jdn2date(JDN, 1)
    	else
    		return "Error parsing input date: " .. frame.args[1]
    	end
    end
    
    -- Return day of week based on gregorian date. Mon->1, Tue->2, ..., Sun->7
    function p.DayOfWeek(frame)
    	local JDN = p._date2jdn(frame.args[1], 1)
    	local day = math.fmod(JDN, 7) + 1
    	if day then
    		local LUT = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }
    		return LUT[day]
    	else
    		return "Error parsing input date: " .. frame.args[1]
    	end
    end
    
    -- Convert calendar date to  "Julian day number" (jdn)
    function p.date2jdn(frame)
    	return p._date2jdn(frame.args[1] or os.date('%F'), frame.args[2] or 1)
    end
    
    return p