Jump to content
Toggle menu
  • 8 articles
  • 75 files
  • 7 users
  • 37.8K edits
Neodyland Wiki
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:ISOdate: Difference between revisions

From Neodyland Wiki
No edit summary
No edit summary
Line 3: Line 3:
This module is intended for processing of date strings.
This module is intended for processing of date strings.


Please do not modify this code without applying the changes first at Module:Date/sandbox and testing  
Please do not modify this code without applying the changes first at Module:ISOdate/sandbox and testing  
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
at Module:ISOdate/sandbox/testcases and Module talk:ISOdate/sandbox/testcases.


Authors and maintainers:
Authors and maintainers:
Line 26: Line 26:
   
   
Usage:
Usage:
{{#invoke:Date|ISOyear|target_string}}
{{#invoke:ISOdate|ISOyear|target_string}}
   
   
Parameters
Parameters
Line 67: Line 67:
   
   
Usage:
Usage:
{{#invoke:Date|ISOdate|target_string|lang=}}
{{#invoke:ISOdate|ISOdate|target_string|lang=}}
   
   
Parameters:
Parameters:
Line 81: Line 81:
]]
]]
function p.ISOdate(frame)
function p.ISOdate(frame)
local datestr, succeded
datestr, succeded = p._ISOdate(
mw.text.trim(frame.args[1]),
frame.args["lang"],                  -- language
frame.args["case"]  or '',          -- allows to specify grammatical case for the month for languages that use them
frame.args["class"] or 'dtstart',    -- allows to set the html class of the time node where the date is included.
frame.args["trim_year"] or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
return datestr
end
function p._ISOdate(datestr, lang, case, class, trim_year)


-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
Line 86: Line 98:
--  1) Strings starting with "^" and ending with "$" indicate whole string match
--  1) Strings starting with "^" and ending with "$" indicate whole string match
--  2) optional tail part copied as-is and following the main parsed part of the date have to be separated from the date by a whitespace, so "(\s.+)?"
--  2) optional tail part copied as-is and following the main parsed part of the date have to be separated from the date by a whitespace, so "(\s.+)?"
local pattern = {
local patterns = {
-- strings starting with YYYY-MM-DD HH:MM:SS. Year 4 digits (if we know seconds than it was within the last 100 years), the rest 1-2
-- strings starting with YYYY-MM-DD HH:MM:SS. Year 4 digits (if we know seconds than it was within the last 100 years), the rest 1-2
-- date and time can be separated by space or "T" and there could be a "Z" on the end indicating "Zulu" time zone
-- date and time can be separated by space or "T" and there could be a "Z" on the end indicating "Zulu" time zone
Line 92: Line 104:
{dlen=6, tail=0, regexp="^(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?):(%d%d?)Z?$"},  
{dlen=6, tail=0, regexp="^(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?):(%d%d?)Z?$"},  
-- strings starting with YYYY-MM-DD HH:MM. Year 4 digits, the rest 1-2
-- strings starting with YYYY-MM-DD HH:MM. Year 4 digits, the rest 1-2
-- (if one knows hour and minute than it was probably after a year 1000)
{dlen=5, tail=6, regexp="^(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?)(%s.+)"},
{dlen=5, tail=6, regexp="^(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?)(%s.+)"},
{dlen=5, tail=0, regexp="^(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?)$"},
{dlen=5, tail=0, regexp="^(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?)$"},
-- strings starting with YYYY-MM-DD. Year 3-4 digits, the rest 1-2
-- strings starting with YYYY-MM-DD. Year 1-4 digits, the rest 1-2
{dlen=3, tail=4, regexp="^(%d%d?%d?%d?)-(%d%d?)-(%d%d?)(%s.+)"},
{dlen=3, tail=4, regexp="^(%d%d?%d?%d?)-(%d%d?)-(%d%d?)(%s.+)"},
{dlen=3, tail=0, regexp="^(%d%d?%d?%d?)-(%d%d?)-(%d%d?)$"},
{dlen=3, tail=0, regexp="^(%d%d?%d?%d?)-(%d%d?)-(%d%d?)$"},
-- strings starting with YYYY-MM. Year 3-4 digits, month 1-2 digits
-- strings starting with YYYY-MM. Year 3-4 digits, month 2 digits
{dlen=2, tail=3, regexp="^(%d%d?%d?%d?)-(%d%d?)(%s.+)"},  
-- (want to avoit converting to dates strings like 10-5 = 5
{dlen=2, tail=0, regexp="^(%d%d?%d?%d?)-(%d%d)$"},  
{dlen=2, tail=3, regexp="^(%d%d%d%d?)-(%d%d)(%s.+)"},  
-- if whole string is in YYYY-MM form: If Year 1-4 digits, month 1-2 digits
{dlen=2, tail=0, regexp="^(%d%d?%d?%d?)-(%d%d?)$"},  
-- string starts with a number -> it has to be 3 or 4 digit long to be a year
-- string starts with a number -> it has to be 3 or 4 digit long to be a year
{dlen=1, tail=2, regexp="^(%d%d%d%d?)(%s.+)"},
{dlen=1, tail=2, regexp="^(%d%d%d%d?)(%s.+)"},
Line 107: Line 122:
-- create datevec based on which variables are provided
-- create datevec based on which variables are provided
local input = mw.text.trim( frame.args[1] )
local datevec, tail, formatNum
datevec, tail, formatNum = p.test_date_formats(datestr, patterns)
if datevec[1]=='' or datevec[1]==nil then
-- quickly return if datestr does not look like date (it could be a template)
return datestr, false
end
 
-- call p._Date function to format date string
local succeded, datestr2
succeded, datestr2 = pcall( D._Date, datevec, lang, case, class, trim_year)
if succeded and datestr2~='' then
return mw.text.trim( datestr2 .. tail), true
else -- in case of errors return the original string
return datestr, false
end
end
 
function p.ISOdate_extended(frame)
-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
-- regexp hints:
--  1) Strings starting with "^" and ending with "$" indicate whole string match
--  2) optional tail part copied as-is and following the main parsed part of the date have to be separated from the date by a whitespace, so "(\s.+)?"
 
local datestr, succeded
datestr, succeded = p._ISOdate(
mw.text.trim(frame.args[1]),
frame.args["lang"],                  -- language
frame.args["case"]  or '',          -- allows to specify grammatical case for the month for languages that use them
frame.args["class"] or 'dtstart',    -- allows to set the html class of the time node where the date is included.
frame.args["trim_year"] or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
if succeded then
return datestr
end
 
local patterns = {
-- Exended set of recognized formats: like MM/DD/YYYY
{dlen=3, tail=4, regexp="^(%d%d?)[-./](%d%d?)[-./](%d%d%d%d)(%s.+)"},
{dlen=3, tail=0, regexp="^(%d%d?)[-./](%d%d?)[-./](%d%d%d%d)$"},
{dlen=3, tail=0, regexp="^(%d%d?)%s(%w+)%s(%d%d%d%d)$"},
{dlen=3, tail=0, regexp="^(%w+)%s(%d%d?),%s(%d%d%d%d)$"},
}
local datevec, tail, formatNum, category = ''
datevec, tail, formatNum = p.test_date_formats(frame.args[1], patterns)
if formatNum==1 or formatNum==2 then
vec = datevec;
if tonumber(datevec[1])>12 then
frame.args[1] = string.format('%04i-%02i-%02i', datevec[3], datevec[2], datevec[1] )
category = '[[Category:Date in DD/MM/YYYY format]]'
return mw.text.trim( p.ISOdate(frame) .. tail);
elseif tonumber(datevec[2])>12 then
frame.args[1] = string.format('%04i-%02i-%02i', datevec[3], datevec[1], datevec[2] )
category = '[[Category:Date in MM/DD/YYYY format]]'
return mw.text.trim( p.ISOdate(frame) .. tail);
end
elseif (formatNum==3 or formatNum==4) and (datevec[3]=='' or datevec[3]~=nil) then
local str = mw.getCurrentFrame():callParserFunction( "#time", { 'Y-m-d', datestr} )
local vec = {str:match( "^(%d%d?%d?%d?)-(%d%d?)-(%d%d?)$" )}
if vec and vec[1]~=nil then
frame.args[1] = string.format('%04i-%02i-%02i', vec[1], vec[2], vec[3] )
category = '[[Category:Date in word format]]'
return p.ISOdate(frame);
end
end
return datestr
end
 
function p.test_date_formats(datestr, patterns)
-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
 
local datevec = {'','','','','',''}
local datevec = {'','','','','',''}
local tail = ''
local tail = ''
local vec, pat
local vec, pat
local debugStr = ''
local formatNum = 0
for i, pat in ipairs( pattern ) do
for i, pat in ipairs( patterns ) do
vec = {input:match( pat.regexp )}
vec = {datestr:match( pat.regexp )}
if vec and vec[1]~=nil then
if vec and vec[1]~=nil then
for j=1,pat.dlen do
for j=1,pat.dlen do
Line 119: Line 204:
end
end
if pat.tail>0 and vec[pat.tail]~=nil then
if pat.tail>0 and vec[pat.tail]~=nil then
tail = vec[pat.tail]
tail = mw.ustring.gsub(' ' .. vec[pat.tail], ' +', ' ')
end
end
debugStr = string.format(' (%i)', i)
formatNum = i
break
break
end
end
end
if datevec[1]=='' or datevec[1]==nil then
-- quickly return if input does not look like date (it could be a template)
return input
end
end
 
return datevec, tail, formatNum
-- call p._Date function to format date string
local succeded, datestr
succeded, datestr = pcall(
D._Date, -- call p._Date function with following arguments:
datevec, -- {year, month, day, hour, minute, second} strings packed in a vector
frame.args["lang"], -- language
frame.args["case"]  or '', -- allows to specify grammatical case for the month for languages that use them
frame.args["class"] or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
frame.args["trim_year"] or '100-999'-- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
if succeded and datestr~='' then
tail = mw.ustring.gsub(' ' .. tail, ' +', ' ')
return mw.text.trim( datestr .. tail)
else
-- in case of errors return the original string
return input
end
end
end


return p
return p

Revision as of 04:31, 30 December 2014

Module documentation[ create · purge ]
This documentation is transcluded from Module:ISOdate/doc.
--[[  
 
This module is intended for processing of date strings.

Please do not modify this code without applying the changes first at Module:ISOdate/sandbox and testing 
at Module:ISOdate/sandbox/testcases and Module talk:ISOdate/sandbox/testcases.

Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date and template:ISOyear

]]

 
local p = {}

-- =======================================
-- === Dependencies ======================
-- =======================================
local D = require('Module:Date')

--[[
ISOyear
 
This function returns year part of date string.
 
Usage:
{{#invoke:ISOdate|ISOyear|target_string}}
 
Parameters
    1: The date string 
 
Error Handling:
   If the string does not look like it contain the year than the function will not return anything.
   That is the preferred treatment for the template:Creator which is the main (only?) template calling it.
]]
function p.ISOyear( frame )
	local input = frame.args[1]
	if not input then
		input = frame.args["s"]
	end
	input = mw.text.trim( input )
    
	-- if empty string then return it
	if input == "" then
		return input
	end
    
	-- if number then return it
	if tonumber( input ) then
		return mw.ustring.format( '%04i', input )
	end
    
	-- otherwise use regular expression match
	input = mw.ustring.match( input, '^(-?%d%d?%d?%d?)-' )
	if input then
		return mw.ustring.format( '%04i', input )
	else
		return ''
	end
end

--[[
ISOdate
 
This function is the core part of the ISOdate template. 
 
Usage:
{{#invoke:ISOdate|ISOdate|target_string|lang=}}
 
Parameters:
     1: The date string 
  lang: The language to display it in
  form: Language format (genitive, etc.) for some languages
 class: CSS class for the <time> node

 Error Handling:
   If the string does not look like it contain the proper ISO date than the function will return the original string.
   
   That is the preferred treatment for the template:Information (and similar templates) which calling it.
]]
function p.ISOdate(frame)
	local datestr, succeded
	datestr, succeded = p._ISOdate(
		mw.text.trim(frame.args[1]),
		frame.args["lang"],                  -- language
		frame.args["case"]  or '',           -- allows to specify grammatical case for the month for languages that use them
		frame.args["class"] or 'dtstart',    -- allows to set the html class of the time node where the date is included. 
		frame.args["trim_year"] or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is	
	)
	return datestr
end

function p._ISOdate(datestr, lang, case, class, trim_year)

	-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
	-- regexp hints:
	--  1) Strings starting with "^" and ending with "$" indicate whole string match
	--  2) optional tail part copied as-is and following the main parsed part of the date have to be separated from the date by a whitespace, so "(\s.+)?"
	local patterns = {
		-- strings starting with YYYY-MM-DD HH:MM:SS. Year 4 digits (if we know seconds than it was within the last 100 years), the rest 1-2
		-- date and time can be separated by space or "T" and there could be a "Z" on the end indicating "Zulu" time zone
		{dlen=6, tail=7, regexp="^(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?):(%d%d?)Z?(%s.*)"}, 
		{dlen=6, tail=0, regexp="^(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?):(%d%d?)Z?$"}, 
		-- strings starting with YYYY-MM-DD HH:MM. Year 4 digits, the rest 1-2
		-- (if one knows hour and minute than it was probably after a year 1000)
		{dlen=5, tail=6, regexp="^(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?)(%s.+)"},
		{dlen=5, tail=0, regexp="^(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?)$"},
		-- strings starting with YYYY-MM-DD. Year 1-4 digits, the rest 1-2
		{dlen=3, tail=4, regexp="^(%d%d?%d?%d?)-(%d%d?)-(%d%d?)(%s.+)"},
		{dlen=3, tail=0, regexp="^(%d%d?%d?%d?)-(%d%d?)-(%d%d?)$"},
		-- strings starting with YYYY-MM. Year 3-4 digits, month 2 digits
		-- (want to avoit converting to dates strings like 10-5 = 5
		{dlen=2, tail=3, regexp="^(%d%d%d%d?)-(%d%d)(%s.+)"}, 
		-- if whole string is in YYYY-MM form: If Year 1-4 digits, month 1-2 digits
		{dlen=2, tail=0, regexp="^(%d%d?%d?%d?)-(%d%d?)$"}, 
		-- string starts with a number -> it has to be 3 or 4 digit long to be a year
		{dlen=1, tail=2, regexp="^(%d%d%d%d?)(%s.+)"},	
		 -- if whole string is a number (1-4 digit long) than it will be interpreted as a year
		{dlen=1, tail=0, regexp="^(%d%d?%d?%d?)$"},
	}
	
	-- create datevec based on which variables are provided
	local datevec, tail, formatNum
	datevec, tail, formatNum = p.test_date_formats(datestr, patterns)
	if datevec[1]=='' or datevec[1]==nil then
		-- quickly return if datestr does not look like date (it could be a template)
		return datestr, false
	end

	-- call p._Date function to format date string
	local succeded, datestr2
	succeded, datestr2 = pcall( D._Date, datevec, lang, case, class, trim_year)
	if succeded and datestr2~='' then
		return mw.text.trim( datestr2 .. tail), true
	else -- in case of errors return the original string
		return datestr, false
	end	
end

function p.ISOdate_extended(frame)
	-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
	-- regexp hints:
	--  1) Strings starting with "^" and ending with "$" indicate whole string match
	--  2) optional tail part copied as-is and following the main parsed part of the date have to be separated from the date by a whitespace, so "(\s.+)?"

	local datestr, succeded
	datestr, succeded = p._ISOdate(
		mw.text.trim(frame.args[1]),
		frame.args["lang"],                  -- language
		frame.args["case"]  or '',           -- allows to specify grammatical case for the month for languages that use them
		frame.args["class"] or 'dtstart',    -- allows to set the html class of the time node where the date is included. 
		frame.args["trim_year"] or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is	
	)
	if succeded then
		return datestr
	end

	local patterns = {
		-- Exended set of recognized formats: like MM/DD/YYYY
		{dlen=3, tail=4, regexp="^(%d%d?)[-./](%d%d?)[-./](%d%d%d%d)(%s.+)"},
		{dlen=3, tail=0, regexp="^(%d%d?)[-./](%d%d?)[-./](%d%d%d%d)$"},
		{dlen=3, tail=0, regexp="^(%d%d?)%s(%w+)%s(%d%d%d%d)$"},
		{dlen=3, tail=0, regexp="^(%w+)%s(%d%d?),%s(%d%d%d%d)$"},
	}
	
	local datevec, tail, formatNum, category = ''
	datevec, tail, formatNum = p.test_date_formats(frame.args[1], patterns)
	if formatNum==1 or formatNum==2 then
		vec = datevec;
		if tonumber(datevec[1])>12 then
			frame.args[1] = string.format('%04i-%02i-%02i', datevec[3], datevec[2], datevec[1] )
			category = '[[Category:Date in DD/MM/YYYY format]]'
			return mw.text.trim( p.ISOdate(frame) .. tail);
		elseif tonumber(datevec[2])>12 then
			frame.args[1] = string.format('%04i-%02i-%02i', datevec[3], datevec[1], datevec[2] )
			category = '[[Category:Date in MM/DD/YYYY format]]'
			return mw.text.trim( p.ISOdate(frame) .. tail);
		end
	elseif (formatNum==3 or formatNum==4) and (datevec[3]=='' or datevec[3]~=nil) then
		local str = mw.getCurrentFrame():callParserFunction( "#time", { 'Y-m-d', datestr} )
		local vec = {str:match( "^(%d%d?%d?%d?)-(%d%d?)-(%d%d?)$" )}
		if vec and vec[1]~=nil then
			frame.args[1] = string.format('%04i-%02i-%02i', vec[1], vec[2], vec[3] )
			category = '[[Category:Date in word format]]'
			return p.ISOdate(frame);
		end
	end	
	return datestr
end

function p.test_date_formats(datestr, patterns)
	-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any

	local datevec = {'','','','','',''}
	local tail = ''
	local vec, pat
	local formatNum = 0
	for i, pat in ipairs( patterns ) do
		vec = {datestr:match( pat.regexp )}
		if vec and vec[1]~=nil then
			for j=1,pat.dlen do
				datevec[j] = vec[j]
			end
			if pat.tail>0 and vec[pat.tail]~=nil then
				tail = mw.ustring.gsub(' ' .. vec[pat.tail], ' +', ' ')
			end
			formatNum = i
			break
		end
	end
	return datevec, tail, formatNum
end

return p
Cookies help us deliver our services. By using our services, you agree to our use of cookies.