Toggle menu
7
27
38
5.2K
Sanarchive
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

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

require("strict")

local p = {}

local TEMPLATES = {
	birth_date = "Birth date",
	birth_date_and_age = "Birth date and age",
	birth_year = "Birth year",
	birth_year_and_age = "Birth year and age",
	death_date = "Death date",
	death_date_text = "Death date text",
	death_date_and_age = "Death date and age",
	death_year = "Death year",
	death_year_and_age = "Death year and age",
}
local invalid_date_category = ''
local tracking_category = ''
local Date = require('Module:Date')._Date
local getArgs = require('Module:Arguments').getArgs

if mw.title.getCurrentTitle():inNamespaces(0, 828, 829) then
	-- Category only in namespaces: 0=article, 828=module & 829=module talk (last 2 needed for testcases)
	tracking_category = '[[Category:Pages where birth or death is being automatically determined]]'
	invalid_date_category = '[[Category:Pages with invalid birth or death dates]]'
end

local function check_for_invalid_date(str)
	return mw.ustring.match(str, '^%?') or mw.ustring.match(str, '^%d%d%?') or mw.ustring.match(str, '^[Uu]nk')
end

local function expand_template(template, args)
	return mw.getCurrentFrame():expandTemplate{title = template, args = args}
end

local function parse_date(str)
	local date = Date(str)
	if date then
		return tostring(date.year), tostring(date.month), tostring(date.day)
	end
	-- TODO: Date has not been parsed. Need to work out what to do. For now, throw an error.
	-- Example where this can occur: "birth_date = 22 December 1946" at [[Maurice Pasternak]].
	-- There was a nonbreaking space after "22" whidh Date does not accept.
	error('Module:Person_date parse_date invalid date: "' .. tostring(str) .. '"')
end

local function date_format(str)
	if mw.ustring.match (str, '^%d+%s*%a+[%.]*%s*%d%d%d%d$') then
		return 'df'
	elseif mw.ustring.match(str, '^%a+[%.]*%s+%d+,%s*%d%d%d%d$') then
		return 'mf'
	else
		return 'ERROR'
	end

end

local function is_valid_month (str)
	str = string.upper(mw.ustring.sub(str,1,1))..string.lower(mw.ustring.sub(str,2))

	local months = {'Jan','Jan.','January','Feb','Feb.','February','Mar','March','Apr','Apr.','April','May','Jun','Jun.','June','Jul','Jul.','July','Aug','Aug.','August','Sep','Sept','Sep.','Sept.','September','Oct','Oct.','October','Nov','Nov','November','Dec','Dec.','December'}
	for index, value in ipairs(months) do
		if value == str then
			return true
		end
	end
	return false
end

local function is_month_year_only(str)
	local month = mw.ustring.match(str, '^(%a+)[%.]*%s+%d%d%d%d$')
	if month == nil then
		return false
	else
		return is_valid_month(month)
	end
end

local function is_valid_date(str)
	local month = mw.ustring.match (str, '^%d+%s*(%a+)[%.]*%s*%d%d%d%d$') or mw.ustring.match(str, '^(%a+)[%.]*%s+%d+,%s*%d%d%d%d$')
	if month == nil then
		return false
	else
		return is_valid_month(month)
	end
end

local function is_year_only(str)
	return mw.ustring.match(str, '^%d%d%d%d$')
end

local function already_has_template(str)
	str = mw.ustring.gsub(str, '&[Nn][Bb][Ss][Pp];', ' ')
	return mw.ustring.match(str, '<span') or mw.ustring.match(str, '<time') or mw.ustring.match(str,'%(aged%s*%d+') or mw.ustring.match(str,'%(age%s*%d+')
end

local function sanatize_date(str)
	-- Sanatize leading & trailing whitespace (this caused an issue before it was implemented)
	str = mw.ustring.gsub(str,'^%s*','')
	str = mw.ustring.gsub(str,'%s*$','')
	-- Sanatize ordinals
	str = mw.ustring.gsub(str, '(%d+)st([,%s])', '%1%2')
	str = mw.ustring.gsub(str, '(%d+)rd([,%s])', '%1%2')
	str = mw.ustring.gsub(str, '(%d+)th([,%s])', '%1%2')
	str = mw.ustring.gsub(str, '(%d+)nd([,%s])', '%1%2')
	return str
end

local function parse_birth(args)
	local birth_date = args['birth_date'] or ''
	local death_date = args['death_date'] or ''
	local disap_date = args['disappeared_date'] or ''
	local original = birth_date
	
	birth_date = sanatize_date(birth_date)
	death_date = sanatize_date(death_date)
	disap_date = sanatize_date(disap_date)

	-- Check for bad inputs. This used to be covered by a switch statement in infoboxes
	if check_for_invalid_date(birth_date) then
		return original..invalid_date_category
	end

	if already_has_template(birth_date) then
		return original
	end

	if is_valid_date(birth_date) then
		local location = mw.ustring.find(birth_date, '%d%d%d%d')
		local extra = mw.ustring.sub(birth_date, location+4) .. tracking_category
		local date = mw.ustring.sub(birth_date, 1,location+3)
		local year, month, day = parse_date(date)
		local format = date_format(date)
		if death_date == '' and disap_date == '' then
			return expand_template(TEMPLATES.birth_date_and_age, {year, month, day, [format] = 'yes'}) .. extra
		elseif is_year_only(death_date) or is_valid_date(death_date) or is_month_year_only(death_date) then
			return expand_template(TEMPLATES.birth_date, {year, month, day, [format] = 'yes'}) .. extra
		else
			-- death_date is not a valid string (example: 'unknown')
			return original
		end
	end
	if is_month_year_only(birth_date) then

		local year = Date('1 '..birth_date):text('%Y')
		local month = Date('1 '..birth_date):text('%-m')
		local location = mw.ustring.find(birth_date, '%d%d%d%d')
		local date = mw.ustring.sub(birth_date, 1,location+3)
		local extra = mw.ustring.sub(birth_date, location+4) .. tracking_category

		if death_date == '' and disap_date == '' then
			return expand_template(TEMPLATES.birth_year_and_age, {year, month}) .. extra
		elseif is_year_only(death_date) or is_valid_date(death_date) or is_month_year_only(death_date) then
			return expand_template(TEMPLATES.birth_year, {date}) .. extra
		else
			-- death_date is not a valid string (example: 'unknown')
			return original
		end

	end
	if is_year_only(birth_date) then
		local date = mw.ustring.sub(birth_date, 1, 5)
		local extra = mw.ustring.sub(birth_date, 5) .. tracking_category
		if death_date == '' and disap_date == '' then
			return expand_template(TEMPLATES.birth_year_and_age, {date}) .. extra
		elseif is_year_only(death_date) or is_valid_date(death_date) then
			return expand_template(TEMPLATES.birth_year, {date}) .. extra
		else
			-- death_date is not a valid string (example: 'unknown')
			return original
		end
	end

	return original
end

local function parse_death(args)
	local birth_date = args['birth_date'] or ''
	local death_date = args['death_date'] or ''
	local original = death_date

	birth_date = sanatize_date(birth_date)
	death_date = sanatize_date(death_date)

	-- Check for bad inputs. This used to be covered by a switch statement in infoboxes
	if check_for_invalid_date(death_date) then
		return original..invalid_date_category
	end

	if already_has_template(death_date) then
		return original
	end

	if is_valid_date(death_date) or is_month_year_only(death_date) then
		local location = mw.ustring.find(death_date, '%d%d%d%d')
		local date = mw.ustring.sub(death_date, 1,location+3)
		local extra = mw.ustring.sub(death_date, location+4) .. tracking_category
		local format = date_format(date)

		if birth_date == '' then
			if is_month_year_only(death_date) then
				return expand_template(TEMPLATES.death_date_text, {date}) .. extra
			end
			local year, month, day = parse_date(date)
			return expand_template(TEMPLATES.death_date, {year, month, day, [format] = 'yes'}) .. extra
		else
			if is_year_only(birth_date) then
				location =  mw.ustring.find(birth_date, '%d%d%d%d')
				local bd = mw.ustring.sub(birth_date, 1,location+3)
				if is_month_year_only(death_date) then
					return expand_template(TEMPLATES.death_date_and_age, {date, bd}) .. extra
				end

				return expand_template(TEMPLATES.death_date_and_age, {date, bd, [format] = 'yes'}) .. extra
			elseif is_valid_date(birth_date) or is_month_year_only(birth_date) then
				location =  mw.ustring.find(birth_date, '%d%d%d%d')
				local bd = mw.ustring.sub(birth_date, 1,location+3)

				if (is_month_year_only(death_date)) then
					return expand_template(TEMPLATES.death_date_and_age, {date, bd}) .. extra
				end

				return expand_template(TEMPLATES.death_date_and_age, {date, bd, [format] = 'yes'}) .. extra
			end
		end
	end

	if is_year_only(death_date) then
		if birth_date == '' then
			return  expand_template(TEMPLATES.death_year, {mw.ustring.sub(death_date, 1, 5)}) .. mw.ustring.sub(death_date, 5) .. tracking_category
		else
			if is_year_only(birth_date) then
				return  expand_template(TEMPLATES.death_year_and_age, {mw.ustring.sub(death_date, 1, 5), mw.ustring.sub(birth_date, 1, 5)}) .. mw.ustring.sub(death_date, 5) .. tracking_category
			else
				if is_valid_date(birth_date) then
					local location = mw.ustring.find(death_date, '%d%d%d%d')
					local date = mw.ustring.sub(death_date, 1,location+3)
					local extra = mw.ustring.sub(death_date, location+4) .. tracking_category

					location =  mw.ustring.find(birth_date, '%d%d%d%d')
					local bd = mw.ustring.sub(birth_date, 1,location+3)

					return  expand_template(TEMPLATES.death_date_and_age, {date, bd}) .. extra
				end
			end
		end
	end

	return original
end

function p.birth(frame)
	return parse_birth(getArgs(frame, {parentFirst = true}))
end

function p.death(frame)
	return parse_death(getArgs(frame, {parentFirst = true}))
end

return p