Edge Rewrite
// HTMLRewriter · presentation

This page was redesigned at the edge.

Cloudflare fetched the original article and streamed it through HTMLRewriter to apply an entirely new visual system without rebuilding the source page.

Jump to content

Module:Sandbox/RexxS/CalcDate

From Wikipedia, the free encyclopedia
--[[ CalcDate
Modified from chunks of Module:Time
Main function: getYearMonthDay
takes a date string like "second Friday in June" as parameter |date=
and a year as parameter |year= (uses current year if omitted)
and returns the date in YYYY-MM-DD format, e.g. 2018-06-08
Other formats are possible.
]]

require ('strict')


--[[ is_set(var)
Whether variable is set or not.  A variable is set when it is not nil and not empty.
]]
local function is_set(var)
	return var and var ~= ''
end

--[[ current_year()
returns the current year
]]
local function current_year()
	return os.date('%Y', os.time())
end


--[[ decode_date_event(date_event_string)
extract ordinal, day-name, and month from daylight saving start/end definition string as digits:
	Second Sunday in March
returns
	2 0 3
Case doesn't matter but the form of the string does:
	<ordinal> <day> <any single word> <month> – all are separated by spaces
]]
local function decode_date_event(date_event_string)
	local ordinals = {
		['1st'] = 1, ['first'] = 1, ['2nd'] = 2, ['second'] = 2, ['3rd'] = 3, ['third'] = 3,
		['4th'] = 4, ['fourth'] = 4, ['5th'] = 5, ['fifth'] = 5, ['last'] = -1
	}
	local days = {
		['sunday'] = 0, ['monday'] = 1, ['tuesday'] = 2, ['wednesday'] = 3, ['thursday'] = 4, ['friday'] = 5, ['saturday'] = 6
	}
	local months = {
		['january'] = 1, ['february'] = 2, ['march'] = 3, ['april'] = 4, ['may'] = 5,
		['june'] = 6, ['july'] = 7, ['august'] = 8, ['september'] = 9, ['october'] = 10,
		['november'] = 11, ['december'] = 12
	}

	date_event_string = date_event_string:lower()
	local ord, day, month = date_event_string:match ('([%a%d]+)%s+(%a+)%s+%a+%s+(%a+)')

	if is_set(ord) and is_set(day) and is_set(month) then
		return ordinals[ord], days[day], months[month]
	else
		-- if one or more of these not set, then pattern didn't match
		return nil
	end
end


--[[ get_days_in_month(year, month)
Returns the number of days in the month where month is a number 1–12
and year is four-digit Gregorian calendar.
Accounts for leap year. Throws an error if month and year are not numbers.
]]
local function get_days_in_month(year, month)
	local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
	year = tonumber (year)
	month = tonumber (month)
	if month == 2 and year%4 == 0 and (year%100 ~= 0 or year%400 == 0) then
		return 29
	end
	return days_in_month [month]
end


--[[ get_date_month_day(date_event_string, year)
Return the date for the day that is the ordinal (nth) day-name in month of the given year
e.g. "second Friday in June", 2018 -> 2018-06-08
]]
local function get_date_month_day(date_event_string, year)
	local ord, weekday_num, month = decode_date_event(date_event_string)
	if not (is_set (ord) and is_set (weekday_num) and is_set (month)) then
		return nil
	end
	if ord == -1 then
		-- event occurs on the last day-name of the month
		-- j = t + 7×(n + 1) - (wt - w) mod 7
		local days_in_month = get_days_in_month (year, month)
		local ostime = os.time ({['year']=year, ['month']=month, ['day']=days_in_month})
		local last_day_of_month = os.date('%w', ostime)
		return month, days_in_month + 7 * (ord + 1) - ((last_day_of_month - weekday_num) % 7)
	else
		-- j = 7×n - 6 + (w - w1) mod 7
		local ostime = os.time({['year']=year, ['month']=month, ['day']=1})
		local first_day_of_month = os.date('%w', ostime)
		return month, 7 * ord - 6 + (weekday_num - first_day_of_month) % 7
	end
end


-- set up public functions for debugging
local p = {}
p.currentYear = current_year
p.getDateMonth = function(frame)
	local m, d = get_date_month_day(frame.args.date, frame.args.year)
	return m .. "-" .. d
end
--[[ getYearMonthDay
Takes a date string like "second Friday in June" as parameter |date=
and a year as parameter |year=
and returns the date in YYYY-MM-DD format, e.g. 2018-06-08
Other formats are easy to implement.
]]
p.getYearMonthDay = function(frame)
	local args = frame.args or frame:getParent().args
	local date_event = args.date or mw.text.trim(args[1])
	local year = args.year or args[2] or current_year()
	local month, day = get_date_month_day(date_event, year)
	month = (month<10 and "0" or "") .. month
	day = (day<10 and "0" or "") .. day
	return year .. "-" .. month .. "-" .. day
end
return p