<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://stockhub.co/index.php?action=history&amp;feed=atom&amp;title=Module%3ASandbox%2FHarry_noob%2FDates</id>
	<title>Module:Sandbox/Harry noob/Dates - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://stockhub.co/index.php?action=history&amp;feed=atom&amp;title=Module%3ASandbox%2FHarry_noob%2FDates"/>
	<link rel="alternate" type="text/html" href="https://stockhub.co/index.php?title=Module:Sandbox/Harry_noob/Dates&amp;action=history"/>
	<updated>2026-05-27T22:27:35Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://stockhub.co/index.php?title=Module:Sandbox/Harry_noob/Dates&amp;diff=145506&amp;oldid=prev</id>
		<title>imported&gt;Harry noob: add formatting and prefix and suffix</title>
		<link rel="alternate" type="text/html" href="https://stockhub.co/index.php?title=Module:Sandbox/Harry_noob/Dates&amp;diff=145506&amp;oldid=prev"/>
		<updated>2020-01-21T12:22:44Z</updated>

		<summary type="html">&lt;p&gt;add formatting and prefix and suffix&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;--Harry noob Google Code-in 2019, Lua Task 9&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
date format&lt;br /&gt;
M= word month&lt;br /&gt;
m= number month&lt;br /&gt;
&lt;br /&gt;
d/m/y&lt;br /&gt;
y/m/d&lt;br /&gt;
m/d/y&lt;br /&gt;
d M y&lt;br /&gt;
M d y&lt;br /&gt;
y M&lt;br /&gt;
M y&lt;br /&gt;
y&lt;br /&gt;
]]--&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
TODO:&lt;br /&gt;
Q. how to know it is invalid day or it just doesnt match the current pattern?&lt;br /&gt;
A. save when it matches some pattern&lt;br /&gt;
]]--&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local log = mw.log&lt;br /&gt;
&lt;br /&gt;
function p.canonicalDate(frame)&lt;br /&gt;
	local fmt = frame.args.format&lt;br /&gt;
	local text = frame.args.text&lt;br /&gt;
	local date, defaultFmt = p.parseDate(text)&lt;br /&gt;
	if not date then&lt;br /&gt;
		return &amp;quot;Invalid entry&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	local formattedDate = p.formatDate(date, fmt, defaultFmt)&lt;br /&gt;
	local result = addPreSuffix(text, formattedDate)&lt;br /&gt;
	return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.parseDate(text)&lt;br /&gt;
	local d, m, y, a, b, c, mName&lt;br /&gt;
	local mayMatch&lt;br /&gt;
	&lt;br /&gt;
	log(&amp;quot;try to match y/m/d or d/m/y or m/d/y&amp;quot;)&lt;br /&gt;
	a, b, c = string.match(text, &amp;quot;(%d+)[/%- ](%d+)[/%- ](%d+)&amp;quot;) -- y/m/d or d/m/y or m/d/y&lt;br /&gt;
	log(a, b, c)&lt;br /&gt;
	mayMatch = a and b and c&lt;br /&gt;
	local possibleArrangement = {{a, b, c}, {c, b, a}, {c, a, b}}&lt;br /&gt;
	mw.logObject(possibleArrangement, &amp;quot;possibleArrangement: &amp;quot;)&lt;br /&gt;
	for _, arrange in ipairs(possibleArrangement) do&lt;br /&gt;
		y, m, d = unpack(arrange) -- this is not python :(, need to add unpack, destructing assignment doesnt works with table&lt;br /&gt;
		if isValidNumDate(y, m, d) then&lt;br /&gt;
			log(&amp;quot;isValidNumDate passed 1&amp;quot;)&lt;br /&gt;
			return mapToNum({y, m, d}), &amp;quot;iso&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if mayMatch then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local monthShortNameToNum = {jan = 1, feb = 2, mar = 3, apr = 4,&lt;br /&gt;
		jun = 6, jul = 7, aug = 8, sept = 9,&lt;br /&gt;
		oct = 10, nov = 11, dec = 12}&lt;br /&gt;
	local monthLongNameToNum = {january = 1, february = 2, march = 3,&lt;br /&gt;
		april = 4, may = 5, june = 6, july = 7, august = 8, september = 9,&lt;br /&gt;
		october = 10, november = 11, december = 12}&lt;br /&gt;
	&lt;br /&gt;
	-- match long name first, and then short name&lt;br /&gt;
	local monthFound = false&lt;br /&gt;
	local start, stop&lt;br /&gt;
	for monthName, monthNum in pairs(monthLongNameToNum) do&lt;br /&gt;
		start, stop = text:lower():find(monthName)&lt;br /&gt;
		log(&amp;quot;month for loop 1:&amp;quot;, monthName, monthNum, start, stop)&lt;br /&gt;
		if start and stop then&lt;br /&gt;
			monthFound = true&lt;br /&gt;
			mName = text:sub(start, stop) -- ~= monthName, == orginal month name&lt;br /&gt;
			log(&amp;quot;mName:&amp;quot;, mName)&lt;br /&gt;
			m = monthNum&lt;br /&gt;
			break&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if not monthFound then&lt;br /&gt;
		for monthName, monthNum in pairs(monthShortNameToNum) do&lt;br /&gt;
			if monthName == &amp;quot;sept&amp;quot; then&lt;br /&gt;
				start, stop = text:lower():find(&amp;quot;sept?&amp;quot;)&lt;br /&gt;
			else&lt;br /&gt;
				start, stop = text:lower():find(monthName)&lt;br /&gt;
			end&lt;br /&gt;
			if start and stop then&lt;br /&gt;
				monthFound = true&lt;br /&gt;
				mName = text:sub(start, stop) -- ~= monthName, == orginal month name&lt;br /&gt;
				log(&amp;quot;mName:&amp;quot;, mName)&lt;br /&gt;
				m = monthNum&lt;br /&gt;
				break&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if monthFound then&lt;br /&gt;
		log(&amp;quot;try to match d M y or y M d&amp;quot;)&lt;br /&gt;
		d, y = text:match(&amp;quot;(%d+)%D-&amp;quot; .. mName .. &amp;quot;%D-(%d+)&amp;quot;) -- d M y&lt;br /&gt;
		if isValidNumDate(y, m, d) then&lt;br /&gt;
			log(&amp;quot;isValidNumDate passed 2&amp;quot;)&lt;br /&gt;
			return mapToNum({y, m, d}), &amp;quot;dmy&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		if d and y then&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		log(&amp;quot;try to match M d y&amp;quot;)&lt;br /&gt;
		d, y = text:match(mName .. &amp;quot;%D-(%d+)%D-(%d+)&amp;quot;) -- M d y&lt;br /&gt;
		if isValidNumDate(y, m, d) then&lt;br /&gt;
			log(&amp;quot;isValidNumDate passed 4&amp;quot;)&lt;br /&gt;
			return mapToNum({y, m, d}), &amp;quot;mdy&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		if d and y then&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		&lt;br /&gt;
		log(&amp;quot;try to match M y&amp;quot;)&lt;br /&gt;
		y = text:match(mName .. &amp;quot;%D-(%d+)&amp;quot;) -- M y&lt;br /&gt;
		log(&amp;quot;y =&amp;quot;, y)&lt;br /&gt;
		if y then&lt;br /&gt;
			return mapToNum({y, m}), &amp;quot;my&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		log(&amp;quot;try to match y M&amp;quot;)&lt;br /&gt;
		y = text:match(&amp;quot;(%d+)%D-&amp;quot; .. mName) -- y M&lt;br /&gt;
		log(&amp;quot;y =&amp;quot;, y)&lt;br /&gt;
		if y then&lt;br /&gt;
			return mapToNum({y, m}), &amp;quot;ym&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	log(&amp;quot;try to match y&amp;quot;)&lt;br /&gt;
	y = text:match(&amp;quot;(%d+)%D-$&amp;quot;) -- y&lt;br /&gt;
	if y then&lt;br /&gt;
		return {tonumber(y)}, &amp;quot;year&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	log(&amp;quot;no match&amp;quot;)&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- y, m, d are string number&lt;br /&gt;
function isValidNumDate(y, m, d)&lt;br /&gt;
	log(&amp;quot;isValidNumDate receive:&amp;quot;, y, m, d)&lt;br /&gt;
	y, m, d = tonumber(y), tonumber(m), tonumber(d)&lt;br /&gt;
	local NOfDays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}&lt;br /&gt;
	local result = y and m and d and 1 &amp;lt;= m and m &amp;lt;= 12 and (&lt;br /&gt;
		isLeap(y) and m == 2 and 1 &amp;lt;= d and       -- if is leap year and February&lt;br /&gt;
		(d &amp;lt;= 29) or                        -- then test if it &amp;lt;= 29&lt;br /&gt;
		(d &amp;lt;= NOfDays[m]))                  -- else follow general case (use assert and pcall instead of returning 2 result can type less code)&lt;br /&gt;
	log(&amp;quot;isValidNumDate result:&amp;quot; .. tostring(result))&lt;br /&gt;
	return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function isLeap(y)&lt;br /&gt;
	return y%4 == 0 and (y%100 ~= 0 or y%400 == 0)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function mapToNum(arr)&lt;br /&gt;
	mw.logObject(arr, &amp;quot;mapToNum receive arr:&amp;quot;)&lt;br /&gt;
	for i, v in pairs(arr) do&lt;br /&gt;
		arr[i] = tonumber(v)&lt;br /&gt;
	end&lt;br /&gt;
	mw.logObject(arr, &amp;quot;mapToNum result arr:&amp;quot;)&lt;br /&gt;
	return arr&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.formatDate(date, fmt, defaultFmt)&lt;br /&gt;
	local monthNames = {&amp;quot;January&amp;quot;, &amp;quot;February&amp;quot;, &amp;quot;March&amp;quot;,&lt;br /&gt;
		&amp;quot;April&amp;quot;, &amp;quot;May&amp;quot;, &amp;quot;June&amp;quot;, &amp;quot;July&amp;quot;, &amp;quot;August&amp;quot;, &amp;quot;September&amp;quot;,&lt;br /&gt;
		&amp;quot;October&amp;quot;, &amp;quot;November&amp;quot;, &amp;quot;December&amp;quot;}&lt;br /&gt;
	local y, m, d = unpack(date)&lt;br /&gt;
	&lt;br /&gt;
	if (fmt or defaultFmt) == &amp;quot;iso&amp;quot; then&lt;br /&gt;
		return (&amp;quot;%d-%02d-%02d&amp;quot;):format(y, m, d)&lt;br /&gt;
	elseif (fmt or defaultFmt) == &amp;quot;dmy&amp;quot; then&lt;br /&gt;
		return (&amp;quot;%d %s %d&amp;quot;):format(d, monthNames[m], y)&lt;br /&gt;
	elseif (fmt or defaultFmt) == &amp;quot;mdy&amp;quot; then&lt;br /&gt;
		return (&amp;quot;%s %d, %d&amp;quot;):format(monthNames[m], d, y)&lt;br /&gt;
	elseif (fmt or defaultFmt) == &amp;quot;my&amp;quot; then&lt;br /&gt;
		return (&amp;quot;%s %d&amp;quot;):format(monthNames[m], y)&lt;br /&gt;
	elseif (fmt or defaultFmt) == &amp;quot;ym&amp;quot; then&lt;br /&gt;
		return (&amp;quot;%d %s&amp;quot;):format(y, monthNames[m])	&lt;br /&gt;
	else&lt;br /&gt;
		return tostring(y)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function addPreSuffix(text, dateStr)&lt;br /&gt;
	local aboutList = {&amp;quot;about&amp;quot;, &amp;quot;around&amp;quot;, &amp;quot;uncertain&amp;quot;, &amp;quot;roughly&amp;quot;,&lt;br /&gt;
		&amp;quot;approximate&amp;quot;, &amp;quot;close to&amp;quot;, &amp;quot;near &amp;quot;}&lt;br /&gt;
	local suffixList = {&amp;quot;BCE&amp;quot;, &amp;quot;CE&amp;quot;, &amp;quot;BC&amp;quot;, &amp;quot;AD&amp;quot;}&lt;br /&gt;
	&lt;br /&gt;
	for i, word in ipairs(aboutList) do&lt;br /&gt;
		if text:lower():find(word) then&lt;br /&gt;
			dateStr = &amp;quot;circa &amp;quot; .. dateStr&lt;br /&gt;
			break&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	for i, word in ipairs(suffixList) do&lt;br /&gt;
		if text:find(&amp;quot; &amp;quot; .. word) then&lt;br /&gt;
			dateStr = dateStr .. &amp;quot; &amp;quot; .. word&lt;br /&gt;
			break&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return dateStr&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>imported&gt;Harry noob</name></author>
	</entry>
</feed>