<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="nl">
	<id>https://wikitest.nl/index.php?action=history&amp;feed=atom&amp;title=Module%3ADateI18n</id>
	<title>Module:DateI18n - Bewerkingsoverzicht</title>
	<link rel="self" type="application/atom+xml" href="https://wikitest.nl/index.php?action=history&amp;feed=atom&amp;title=Module%3ADateI18n"/>
	<link rel="alternate" type="text/html" href="https://wikitest.nl/index.php?title=Module:DateI18n&amp;action=history"/>
	<updated>2026-04-06T07:06:35Z</updated>
	<subtitle>Bewerkingsoverzicht voor deze pagina op de wiki</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://wikitest.nl/index.php?title=Module:DateI18n&amp;diff=76252&amp;oldid=prev</id>
		<title>Colani: 1 versie geïmporteerd</title>
		<link rel="alternate" type="text/html" href="https://wikitest.nl/index.php?title=Module:DateI18n&amp;diff=76252&amp;oldid=prev"/>
		<updated>2024-03-23T16:39:32Z</updated>

		<summary type="html">&lt;p&gt;1 versie geïmporteerd&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nieuwe pagina&lt;/b&gt;&lt;/p&gt;&lt;div&gt;--[[  &lt;br /&gt;
  __  __           _       _        ____        _       ___ _  ___        &lt;br /&gt;
 |  \/  | ___   __| |_   _| | ___ _|  _ \  __ _| |_ ___|_ _/ |( _ ) _ __  &lt;br /&gt;
 | |\/| |/ _ \ / _` | | | | |/ _ (_) | | |/ _` | __/ _ \| || |/ _ \| &amp;#039;_ \ &lt;br /&gt;
 | |  | | (_) | (_| | |_| | |  __/_| |_| | (_| | ||  __/| || | (_) | | | |&lt;br /&gt;
 |_|  |_|\___/ \__,_|\__,_|_|\___(_)____/ \__,_|\__\___|___|_|\___/|_| |_|&lt;br /&gt;
  &lt;br /&gt;
This module is intended for processing of date strings.&lt;br /&gt;
&lt;br /&gt;
Please do not modify this code without applying the changes first at Module:DateI18n/sandbox&lt;br /&gt;
and testing at Module:DateI18n/sandbox/testcases and Module talk:DateI18n/sandbox/testcases.&lt;br /&gt;
&lt;br /&gt;
Authors and maintainers:&lt;br /&gt;
* User:Parent5446 - original version of the function mimicking Template:ISOdate&lt;br /&gt;
* User:Jarekt - original version of the functions mimicking Template:Date &lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
-- =======================================&lt;br /&gt;
-- === Dependencies ======================&lt;br /&gt;
-- =======================================&lt;br /&gt;
&lt;br /&gt;
require(&amp;#039;strict&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
-- =======================================&lt;br /&gt;
-- === Local Functions ===================&lt;br /&gt;
-- =======================================&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------&lt;br /&gt;
--[[ (copied from Module:Core)&lt;br /&gt;
Function allowing for consistent treatment of boolean-like wikitext input.&lt;br /&gt;
Inputs:&lt;br /&gt;
  1) val - value to be evaluated, outputs as a function of values:&lt;br /&gt;
		true  : true  (boolean), 1 (number), or strings: &amp;quot;yes&amp;quot;, &amp;quot;y&amp;quot;, &amp;quot;true&amp;quot;, &amp;quot;1&amp;quot;&lt;br /&gt;
		false : false (boolean), 0 (number), or strings: &amp;quot;no&amp;quot;, &amp;quot;n&amp;quot;, &amp;quot;false&amp;quot;, &amp;quot;0&amp;quot;&lt;br /&gt;
  2) default - value to return otherwise&lt;br /&gt;
See Also: It works similarly to Module:Yesno&lt;br /&gt;
]]&lt;br /&gt;
local function yesno(val, default)&lt;br /&gt;
	if type(val) == &amp;#039;boolean&amp;#039; then&lt;br /&gt;
		return val&lt;br /&gt;
	elseif type(val) == &amp;#039;number&amp;#039; then&lt;br /&gt;
		val = tostring(val)&lt;br /&gt;
	end&lt;br /&gt;
	if type(val) == &amp;#039;string&amp;#039; then&lt;br /&gt;
		local LUT = {&lt;br /&gt;
			yes=true , y=true , [&amp;#039;true&amp;#039;] =true , t=true , [&amp;#039;1&amp;#039;]=true , on =true,&lt;br /&gt;
			no =false, n=false, [&amp;#039;false&amp;#039;]=false, f=false, [&amp;#039;0&amp;#039;]=false, off=false }&lt;br /&gt;
	    val = LUT[mw.ustring.lower(val)]  -- put in lower case&lt;br /&gt;
	    if (val~=nil) then&lt;br /&gt;
			return val&lt;br /&gt;
		end&lt;br /&gt;
    end&lt;br /&gt;
    return default&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---------------------------------------------------------------------------------------&lt;br /&gt;
-- trim leading zeros in years prior to year 1000&lt;br /&gt;
-- INPUT:&lt;br /&gt;
--  * datestr   - translated date string &lt;br /&gt;
--  * lang      - language of translation&lt;br /&gt;
-- OUTPUT:&lt;br /&gt;
--  * datestr - updated date string &lt;br /&gt;
&lt;br /&gt;
local function trimYear(datestr, year, lang)&lt;br /&gt;
	local yearStr0, yearStr1, yearStr2, zeroStr&lt;br /&gt;
	yearStr0 = string.format(&amp;#039;%04i&amp;#039;, year ) -- 4 digit year in standard form &amp;quot;0123&amp;quot;&lt;br /&gt;
	yearStr1 = mw.language.new(lang):formatDate( &amp;#039;Y&amp;#039;, yearStr0) -- same as calling {{#time}} parser function&lt;br /&gt;
	--yearStr1 = mw.getCurrentFrame():callParserFunction( &amp;quot;#time&amp;quot;, { &amp;#039;Y&amp;#039;, yearStr0, lang } ) -- translate to a language &lt;br /&gt;
	if yearStr0==yearStr1 then -- most of languages use standard form of year &lt;br /&gt;
		yearStr2 = tostring(year)&lt;br /&gt;
	else -- some languages use different characters for numbers&lt;br /&gt;
		yearStr2 = yearStr1&lt;br /&gt;
		zeroStr = mw.ustring.sub(yearStr1,1,1) -- get &amp;quot;0&amp;quot; in whatever language&lt;br /&gt;
		for i=1,3 do -- trim leading zeros&lt;br /&gt;
			if mw.ustring.sub(yearStr2,1,1)==zeroStr then&lt;br /&gt;
				yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)&lt;br /&gt;
			else&lt;br /&gt;
				break&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return string.gsub(datestr, yearStr1, yearStr2 ) -- in datestr replace long year with trimmed one&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---------------------------------------------------------------------------------------&lt;br /&gt;
-- Look up proper format string to be passed to {{#time}} parser function&lt;br /&gt;
-- INPUTS:&lt;br /&gt;
--  * datecode: YMDhms, YMDhm, YMD, YM, Y, MDhms, MDhm, MD, or M&lt;br /&gt;
--  * day     : Number between 1 and 31 (not needed for most languages)&lt;br /&gt;
--  * lang    : language&lt;br /&gt;
-- OUTPUT:&lt;br /&gt;
--  * dFormat : input to {{#time}} function&lt;br /&gt;
local function getDateFormat(datecode, day, lang)&lt;br /&gt;
	local function parseFormat(dFormat, day)&lt;br /&gt;
		if dFormat:find(&amp;#039;default&amp;#039;) and #dFormat&amp;gt;10 then&lt;br /&gt;
			-- Special (and messy) case of dFormat code depending on a day number, where data is a&lt;br /&gt;
			-- JSON-encoded table {”default”:”*”,”dDD”:”*”} including fields for specific 2-digit days.&lt;br /&gt;
			-- Change curly double quotes (possibly used for easier editing in tabular data) in dFormat&lt;br /&gt;
			-- to straight ASCII double quotes (required for parsing of this JSON-encoded table).&lt;br /&gt;
			local D = mw.text.jsonDecode(mw.ustring.gsub(dFormat, &amp;#039;[„“‟”]&amp;#039;, &amp;#039;&amp;quot;&amp;#039;)) --com = mw.dumpObject(D)&lt;br /&gt;
			-- If the desired day is not in that JSON table, then use its &amp;quot;default&amp;quot; case.&lt;br /&gt;
			dFormat = D[string.format(&amp;#039;d%02i&amp;#039;, day)] or D.default&lt;br /&gt;
            -- Change ASCII single quotes to ASCII double quotes used for {{#time}} marking.&lt;br /&gt;
            -- Apostrophes needed in plain-text must not use ASCII single quotes but curly apostrophe&lt;br /&gt;
            -- e.g. { ‟default”: ‟j”, ‟d01”: ‟j’&amp;#039;o&amp;#039;” }, not { ‟default”: ‟j”, ‟d01”: ‟j&amp;#039;&amp;#039;o&amp;#039;” }.&lt;br /&gt;
		end&lt;br /&gt;
		dFormat = dFormat:gsub(&amp;quot;&amp;#039;&amp;quot;, &amp;#039;&amp;quot;&amp;#039;)&lt;br /&gt;
		return dFormat&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local T = {}&lt;br /&gt;
	local tab = mw.ext.data.get(&amp;#039;DateI18n.tab&amp;#039;, lang)&lt;br /&gt;
	for _, row in pairs(tab.data) do -- convert the output into a dictionary table&lt;br /&gt;
		local id, _, msg = unpack(row)&lt;br /&gt;
		T[id] = msg&lt;br /&gt;
	end&lt;br /&gt;
    -- Compatibility of legacy data using &amp;#039;HMS&amp;#039; or &amp;#039;HM&amp;#039;, where &amp;#039;M&amp;#039; is ambiguous&lt;br /&gt;
    T.YMDhms = T.YMDhms or T.YMDHMS&lt;br /&gt;
    T.YMDhm  = T.YMDhm  or T.YMDHM&lt;br /&gt;
    datecode = datecode == &amp;#039;YMDHMS&amp;#039; and &amp;#039;YMDhms&amp;#039; or datecode == &amp;#039;YMDHM&amp;#039; and &amp;#039;YMDhm&amp;#039; or datecode&lt;br /&gt;
&lt;br /&gt;
	local dFormat = T[datecode]&lt;br /&gt;
	if dFormat == &amp;#039;default&amp;#039; and (datecode == &amp;#039;YMDhms&amp;#039; or datecode == &amp;#039;YMDhm&amp;#039;)  then &lt;br /&gt;
		-- For most languages adding hour:minute:second is done by adding &amp;quot;, HH:ii:ss to the &lt;br /&gt;
		-- day precission date, those languages are skipped in DateI18n.tab and default to &lt;br /&gt;
		-- English which stores word &amp;quot;default&amp;quot;&lt;br /&gt;
		dFormat = parseFormat(T[&amp;#039;YMD&amp;#039;], day).. &amp;#039;, H:i&amp;#039;&lt;br /&gt;
		if datecode == &amp;#039;YMDhms&amp;#039; then&lt;br /&gt;
			dFormat = dFormat .. &amp;#039;:s&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		dFormat = parseFormat(dFormat, day)&lt;br /&gt;
	end&lt;br /&gt;
	return dFormat&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---------------------------------------------------------------------------------------&lt;br /&gt;
-- Look up proper format string to be passed to {{#time}} parser function&lt;br /&gt;
-- INPUTS:&lt;br /&gt;
--  * month : month number&lt;br /&gt;
--  * case  : gramatic case abbriviation, like &amp;quot;ins&amp;quot;, &amp;quot;loc&amp;quot;&lt;br /&gt;
--  * lang  : language&lt;br /&gt;
-- OUTPUT:&lt;br /&gt;
--  * dFormat : input to {{#time}} function&lt;br /&gt;
local function MonthCase(month, case, lang)&lt;br /&gt;
	if month == nil or case == nil then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local T = {{},{},{},{},{},{},{},{},{},{},{},{}}&lt;br /&gt;
	local tab = mw.ext.data.get(&amp;#039;I18n/MonthCases.tab&amp;#039;, lang)&lt;br /&gt;
	for _, row in pairs(tab.data) do&lt;br /&gt;
		local mth, cs, msg = unpack(row)&lt;br /&gt;
		T[mth][cs] = msg&lt;br /&gt;
	end&lt;br /&gt;
	return T[month][case]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ==================================================&lt;br /&gt;
-- === External functions ===========================&lt;br /&gt;
-- ==================================================&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- ===========================================================================&lt;br /&gt;
-- === Functions accesible from the outside to allow unit-testing&lt;br /&gt;
-- === Please do not use directly as they could change in the future&lt;br /&gt;
-- ===========================================================================&lt;br /&gt;
&lt;br /&gt;
---------------------------------------------------------------------------------------&lt;br /&gt;
-- Single string replacement that ignores part of the string in &amp;quot;...&amp;quot;&lt;br /&gt;
function p.strReplace(String, old, new)&lt;br /&gt;
	if String:find(&amp;#039;&amp;quot;&amp;#039;) then&lt;br /&gt;
		local T={}&lt;br /&gt;
		for i, str in ipairs(mw.text.split( String, &amp;#039;&amp;quot;&amp;#039;, true )) do&lt;br /&gt;
			if i%2==1 then&lt;br /&gt;
				str = str:gsub(old, new, 1)&lt;br /&gt;
			end&lt;br /&gt;
			table.insert(T, str)&lt;br /&gt;
		end&lt;br /&gt;
		return table.concat(T,&amp;#039;&amp;quot;&amp;#039;)&lt;br /&gt;
	else&lt;br /&gt;
		return String:gsub(old, new, 1)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---------------------------------------------------------------------------------------&lt;br /&gt;
-- process datevec&lt;br /&gt;
-- INPUT:&lt;br /&gt;
--  * datevec - Array of {year,month,day,hour,minute,second, tzhour, tzmin} containing broken &lt;br /&gt;
--    down date-time component strings or numbers&lt;br /&gt;
-- OUTPUT:&lt;br /&gt;
--  * datenum - same array but holding only numbers or nuls&lt;br /&gt;
function p.clean_datevec(datevec)&lt;br /&gt;
	-- create datecode based on which variables are provided and check for out-of-bound values&lt;br /&gt;
	&lt;br /&gt;
	-- check special case of month provided as a name&lt;br /&gt;
	local month = datevec[2]&lt;br /&gt;
	if type(month) == &amp;#039;string&amp;#039; and month ~= &amp;#039;&amp;#039; and not tonumber(month) then&lt;br /&gt;
		-- When the month is not a number, check if it&amp;#039;s a month name in the project&amp;#039;s language.&lt;br /&gt;
		datevec[2] = mw.getContentLanguage():formatDate(&amp;#039;n&amp;#039;, month)&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	-- check bounds&lt;br /&gt;
	local maxval = {  1/0, 12, 31, 23, 59, 59,  23, 59 } -- max values (or  1/0=+inf) for year, month, day, hour, minute, second, tzhour, tzmin&lt;br /&gt;
	local minval = { -1/0, 01, 01, 00, 00, 00, -23, 00 } -- min values (or -1/0=-inf) for year, month, ...&lt;br /&gt;
	local datenum  = {} -- date-time encoded as a vector = [year, month, ... , second, tzhour, tzmin]&lt;br /&gt;
	for i = 1, 8 do&lt;br /&gt;
        local val = tonumber(datevec[i])&lt;br /&gt;
        if val and val &amp;gt;= minval[i] and val &amp;lt;= maxval[i] then -- These tests work with infinite min/max values.&lt;br /&gt;
		    datenum[i] = val&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- leap second&lt;br /&gt;
	if tonumber(datevec[6]) == 60 then -- leap second &amp;#039;60&amp;#039; is valid only at end of 23:59 UTC, on 30 June or 31 December of specific years&lt;br /&gt;
--		datenum[6] = 60 &lt;br /&gt;
		local MDhm = table.concat({unpack(datenum,2,5)}, &amp;#039;,&amp;#039;)&lt;br /&gt;
	    if (MDhm == table.concat({6, 30, 23, 59}, &amp;#039;,&amp;#039;)) or (MDhm == table.concat({12, 31, 23, 59}, &amp;#039;,&amp;#039;)) then&lt;br /&gt;
		   datenum[6] = 60 &lt;br /&gt;
	    end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return datenum&lt;br /&gt;
end&lt;br /&gt;
	&lt;br /&gt;
---------------------------------------------------------------------------------------&lt;br /&gt;
-- process datevec&lt;br /&gt;
-- INPUT:&lt;br /&gt;
--  * datenum - Array of {year,month,day,hour,minute,second, tzhour, tzmin} as numbers or nuls&lt;br /&gt;
-- OUTPUT:&lt;br /&gt;
--  * timeStamp - date string in the format taken by mw.language:formatDate lua function and {{#time}} parser function&lt;br /&gt;
--       https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate&lt;br /&gt;
--       https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time&lt;br /&gt;
--  * datecode - a code specifying content of the array where Y&amp;#039; is year, &amp;#039;M&amp;#039; is month,&lt;br /&gt;
--     &amp;#039;D&amp;#039; is day, &amp;#039;h&amp;#039; is hour, &amp;#039;m&amp;#039; minute, &amp;#039;s&amp;#039; is second.&lt;br /&gt;
--     Output has to be one of YMDhms, YMDhm, YMD, YM, Y, MDhms, MDhm, MD, M.&lt;br /&gt;
function p.getTimestamp(datenum)&lt;br /&gt;
&lt;br /&gt;
	-- create datecode based on datenum&lt;br /&gt;
	local codes  = { &amp;#039;Y&amp;#039;, &amp;#039;M&amp;#039;, &amp;#039;D&amp;#039;, &amp;#039;h&amp;#039;, &amp;#039;m&amp;#039;, &amp;#039;s&amp;#039;} &lt;br /&gt;
	local datecode = &amp;#039;&amp;#039; -- a string signifying which combination of variables was provided&lt;br /&gt;
	for i, c in ipairs(codes) do&lt;br /&gt;
		datecode = datecode .. (datenum[i] and c or &amp;#039;&amp;#039;) -- if datenum[i] than append codes[i] to datecode&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- create timestamp string (for example 2000-02-20 02:20:20) based on which variables were provided&lt;br /&gt;
	local timeStamp&lt;br /&gt;
    -- date starting by a year&lt;br /&gt;
	if datecode == &amp;#039;YMDhms&amp;#039; then&lt;br /&gt;
		timeStamp = string.format(&amp;#039;%04i-%02i-%02i %02i:%02i:%02i&amp;#039;, datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )&lt;br /&gt;
	elseif datecode == &amp;#039;YMDhm&amp;#039; then&lt;br /&gt;
		timeStamp = string.format(&amp;#039;%04i-%02i-%02i %02i:%02i&amp;#039;, datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )&lt;br /&gt;
	elseif datecode:sub(1,3)==&amp;#039;YMD&amp;#039; then&lt;br /&gt;
		timeStamp = string.format(&amp;#039;%04i-%02i-%02i&amp;#039;, datenum[1], datenum[2], datenum[3] )&lt;br /&gt;
		datecode  = &amp;#039;YMD&amp;#039; -- &amp;#039;YMDhms&amp;#039;, &amp;#039;YMDhm&amp;#039; and &amp;#039;YMD&amp;#039; are the only supported format starting with &amp;#039;YMD&amp;#039;; all others will be converted to &amp;#039;YMD&amp;#039;.&lt;br /&gt;
	elseif datecode:sub(1,2) == &amp;#039;YM&amp;#039; then&lt;br /&gt;
		timeStamp = string.format(&amp;#039;%04i-%02i&amp;#039;, datenum[1], datenum[2] )&lt;br /&gt;
		datecode  = &amp;#039;YM&amp;#039; &lt;br /&gt;
	elseif datecode:sub(1,1)==&amp;#039;Y&amp;#039; then&lt;br /&gt;
		timeStamp = string.format(&amp;#039;%04i&amp;#039;, datenum[1] )&lt;br /&gt;
		datecode  = &amp;#039;Y&amp;#039; &lt;br /&gt;
    -- date starting by a month (the implied year is 2000)&lt;br /&gt;
	elseif datecode== &amp;#039;MDhms&amp;#039; then&lt;br /&gt;
		timeStamp = string.format(&amp;#039;%04i-%02i-%02i %02i:%02i:%02i&amp;#039;, 2000, datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )&lt;br /&gt;
	elseif datecode == &amp;#039;MDhm&amp;#039; then&lt;br /&gt;
		timeStamp = string.format(&amp;#039;%04i-%02i-%02i %02i:%02i&amp;#039;, 2000, datenum[2], datenum[3], datenum[4], datenum[5] )&lt;br /&gt;
	elseif datecode:sub(1,2) == &amp;#039;MD&amp;#039; then&lt;br /&gt;
		timeStamp = string.format(&amp;#039;%04i-%02i-%02i&amp;#039;, 2000, datenum[2], datenum[3] )&lt;br /&gt;
		datecode = &amp;#039;MD&amp;#039; -- &amp;#039;MDhms&amp;#039;, &amp;#039;MDhm&amp;#039; and &amp;#039;MD&amp;#039; are the only supported format starting with &amp;#039;MD&amp;#039;; all others will be converted to &amp;#039;MD&amp;#039;&lt;br /&gt;
	elseif datecode:sub(1,1) == &amp;#039;M&amp;#039; then -- Ambiguous: could mean minutes, but here means month (when parsed as a name/abbrev, not as a number).&lt;br /&gt;
		timeStamp = string.format(&amp;#039;%04i-%02i-%02i&amp;#039;, 2000, datenum[2], 1 )&lt;br /&gt;
		datecode  = &amp;#039;M&amp;#039; &lt;br /&gt;
    -- other possible but unrecognized formats (e.g. &amp;#039;DHis&amp;#039;, &amp;#039;DHi&amp;#039;, &amp;#039;D&amp;#039;, &amp;#039;His&amp;#039;, &amp;#039;Hi&amp;#039;);&lt;br /&gt;
    -- note that &amp;#039;Dh&amp;#039;, &amp;#039;D&amp;#039;, &amp;#039;h&amp;#039;, &amp;#039;s&amp;#039; may eventually work, but not &amp;#039;m&amp;#039; for minute only, which is ambiguous with &amp;#039;M&amp;#039; for month only.&lt;br /&gt;
	else&lt;br /&gt;
		timeStamp = nil -- format not supported&lt;br /&gt;
	end&lt;br /&gt;
	return timeStamp, datecode&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isValidLangCode(lang)&lt;br /&gt;
	if not lang then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	lang = mw.text.trim(lang)&lt;br /&gt;
	return lang ~= &amp;#039;&amp;#039; and lang ~= &amp;#039;⧼Lang⧽&amp;#039; and mw.language.isValidCode(lang)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===========================================================================&lt;br /&gt;
-- === Version of the function to be called from other LUA codes&lt;br /&gt;
-- ===========================================================================&lt;br /&gt;
&lt;br /&gt;
--[[ ========================================================================================&lt;br /&gt;
Date&lt;br /&gt;
 &lt;br /&gt;
This function is the core part of the ISOdate template. &lt;br /&gt;
 &lt;br /&gt;
Usage:&lt;br /&gt;
  local Date = require(&amp;#039;Module:DateI18n&amp;#039;)._Date&lt;br /&gt;
  local dateStr = Date({2020, 12, 30, 12, 20, 11}, lang)&lt;br /&gt;
 &lt;br /&gt;
Parameters:&lt;br /&gt;
  * {year,month,day,hour,minute,second, tzhour, tzmin}: broken down date-time component strings or numbers&lt;br /&gt;
		tzhour, tzmin are timezone offsets from UTC, hours and minutes&lt;br /&gt;
  * lang: The language to display it in&lt;br /&gt;
  * case: Language format (genitive, etc.) for some languages&lt;br /&gt;
  * class: CSS class for the &amp;lt;time&amp;gt; node, use &amp;quot;&amp;quot; for no metadata at all&lt;br /&gt;
]]&lt;br /&gt;
function p._Date(datevec, lang, case, class, trim_year)&lt;br /&gt;
	-- make sure inputs are in the right format&lt;br /&gt;
	&lt;br /&gt;
	-- set language&lt;br /&gt;
	if not isValidLangCode(lang) then&lt;br /&gt;
		-- get user&amp;#039;s chosen language&lt;br /&gt;
		-- equivalent to {{int:lang}}&lt;br /&gt;
		lang = mw.getCurrentFrame():callParserFunction(&amp;quot;int&amp;quot;, &amp;quot;lang&amp;quot;)&lt;br /&gt;
		&lt;br /&gt;
		if not isValidLangCode(lang) then&lt;br /&gt;
			-- if that doesn&amp;#039;t work, use the project language&lt;br /&gt;
			-- this is useful on projects which import this module from Commons&lt;br /&gt;
			lang = mw.language.getContentLanguage().code&lt;br /&gt;
			&lt;br /&gt;
			if not isValidLangCode(lang) then&lt;br /&gt;
				-- if that doesn&amp;#039;t work, use English&lt;br /&gt;
				lang = &amp;quot;en&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if lang == &amp;#039;be-tarask&amp;#039; then&lt;br /&gt;
		lang = &amp;#039;be-x-old&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- process datevec and extract timeStamp and datecode strings as well as numeric datenum array&lt;br /&gt;
	local datenum  = p.clean_datevec(datevec)&lt;br /&gt;
	local year, month, day = datenum[1], datenum[2], datenum[3]&lt;br /&gt;
	local timeStamp, datecode = p.getTimestamp(datenum)&lt;br /&gt;
	if not timeStamp then -- something went wrong in parserDatevec&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	-- Commons [[Data:DateI18n.tab]] page stores prefered formats for diferent &lt;br /&gt;
	-- languages and datecodes (specifying year-month-day or just year of month-day, etc)&lt;br /&gt;
	-- Look up country specific format input to {{#time}} function&lt;br /&gt;
	local dFormat = getDateFormat(datecode, day, lang)&lt;br /&gt;
&lt;br /&gt;
	-- By default the gramatical case is not specified (case==&amp;#039;&amp;#039;) allowing the format to be specified &lt;br /&gt;
	-- in [[Data:DateI18n.tab]]. You can overwrite the default grammatical case of the month by &lt;br /&gt;
	-- specifying &amp;quot;case&amp;quot; variable. This is needed mostly by Slavic languages to create more complex &lt;br /&gt;
	-- phrases as it is done in [[c:Module:Complex date]]&lt;br /&gt;
	case = case or &amp;#039;&amp;#039;&lt;br /&gt;
	if (lang==&amp;#039;qu&amp;#039; or lang==&amp;#039;qug&amp;#039;) and case==&amp;#039;nom&amp;#039; then&lt;br /&gt;
		-- Special case related to Quechua and Kichwa languages. The form in the I18n is&lt;br /&gt;
		--  Genitive case with suffix &amp;quot;pi&amp;quot; added to month names provided by {#time}}&lt;br /&gt;
		-- in Nominative case that &amp;quot;pi&amp;quot; should be removed&lt;br /&gt;
		-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014&lt;br /&gt;
		dFormat = dFormat:gsub(&amp;#039;F&amp;quot;pi&amp;quot;&amp;#039;, &amp;#039;F&amp;#039;)&lt;br /&gt;
	elseif case == &amp;#039;gen&amp;#039; then&lt;br /&gt;
		dFormat = p.strReplace(dFormat, &amp;quot;F&amp;quot;, &amp;quot;xg&amp;quot;)&lt;br /&gt;
	elseif case == &amp;#039;nom&amp;#039; then&lt;br /&gt;
		dFormat = p.strReplace(dFormat, &amp;quot;xg&amp;quot;, &amp;quot;F&amp;quot;)&lt;br /&gt;
	elseif case ~= &amp;#039;&amp;#039; and month ~= nil then&lt;br /&gt;
		-- see is page [[Data:I18n/MonthCases.tab]] on Commons have name of the month &lt;br /&gt;
		-- in specific gramatic case in desired language. If we have it than replace &lt;br /&gt;
		-- &amp;quot;F&amp;quot; and xg&amp;quot; in dFormat&lt;br /&gt;
		local monthMsg = MonthCase(month, case, lang)&lt;br /&gt;
		if  monthMsg and monthMsg ~= &amp;#039;&amp;#039; then -- make sure it exists&lt;br /&gt;
			dFormat = p.strReplace(dFormat, &amp;#039;F&amp;#039;,  &amp;#039;&amp;quot;&amp;#039;..monthMsg..&amp;#039;&amp;quot;&amp;#039;) -- replace default month with month name we already looked up&lt;br /&gt;
			dFormat = p.strReplace(dFormat, &amp;#039;xg&amp;#039;, &amp;#039;&amp;quot;&amp;#039;..monthMsg..&amp;#039;&amp;quot;&amp;#039;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
    -- Translate the date using specified format.&lt;br /&gt;
	-- See https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate and &lt;br /&gt;
	-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions##time for explanation of the format&lt;br /&gt;
	local langObj = mw.language.new(lang)&lt;br /&gt;
	local datestr = langObj:formatDate(dFormat, timeStamp) -- same as using {{#time}} parser function&lt;br /&gt;
	&lt;br /&gt;
	-- Special case related to Thai solar calendar: prior to 1940 new-year was at different time of year,&lt;br /&gt;
	-- so just year (datecode == &amp;#039;Y&amp;#039;) is ambiguous and is replaced by &amp;quot;YYYY or YYYY&amp;quot; phrase&lt;br /&gt;
	if lang==&amp;#039;th&amp;#039; and datecode==&amp;#039;Y&amp;#039; and year&amp;lt;=1940 then&lt;br /&gt;
		datestr = string.format(&amp;#039;%04i หรือ %04i&amp;#039;, year+542, year+543 ) &lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- If year &amp;lt; 1000 than either keep the date padded to the length of 4 digits or trim it.&lt;br /&gt;
	-- Decide if the year will stay padded with zeros (for years in 0-999 range).&lt;br /&gt;
	if year and year &amp;lt; 1000 then&lt;br /&gt;
		trim_year = yesno(trim_year, trim_year or &amp;#039;100-999&amp;#039;)&lt;br /&gt;
		if type(trim_year) == &amp;#039;string&amp;#039; then&lt;br /&gt;
			-- If `trim_year` not a simple boolean, then it&amp;#039;s a range of dates.&lt;br /&gt;
			-- For example &amp;#039;100-999&amp;#039; means to pad 1-or-2-digit years to be 4-digit long, while keeping 3-digit years as is.&lt;br /&gt;
			local YMin, YMax = trim_year:match( &amp;#039;(%d+)-(%d+)&amp;#039; )&lt;br /&gt;
			trim_year = YMin and year &amp;gt;= tonumber(YMin) and year &amp;lt;= tonumber(YMax)&lt;br /&gt;
		end&lt;br /&gt;
		if trim_year then&lt;br /&gt;
			datestr = trimYear(datestr, year, lang) -- in datestr replace long year with trimmed one&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Append a timezone if present (after the hour and minute of the day).&lt;br /&gt;
	if datenum[7] and (datecode:sub(1, 5) == &amp;#039;YMDhm&amp;#039; or datecode:sub(1, 4) == &amp;#039;MDhm&amp;#039;) then&lt;br /&gt;
		-- Use {{#time}} parser function to create timezone string, so that we use the correct character set.&lt;br /&gt;
		local sign = (datenum[7]&amp;lt;0) and &amp;#039;−&amp;#039; or &amp;#039;+&amp;#039;&lt;br /&gt;
		timeStamp = string.format(&amp;quot;2000-01-01 %02i:%02i:00&amp;quot;, math.abs(datenum[7]), datenum[8] or 0)&lt;br /&gt;
		local timezone = langObj:formatDate(&amp;#039;H:i&amp;#039;, timeStamp) -- same as using {{#time}} parser function&lt;br /&gt;
		datestr = string.format(&amp;quot;%s %s%s&amp;quot;, datestr, sign, timezone )&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- HTML formating of date string and tagging for microformats (only for absolute dates with a year).&lt;br /&gt;
	if class and class ~= &amp;#039;&amp;#039; and class ~= &amp;#039;-&amp;#039; and datecode:sub(1,1) == &amp;#039;Y&amp;#039; then &lt;br /&gt;
		local pat = &amp;#039;&amp;lt;time class=&amp;quot;%s&amp;quot; datetime=&amp;quot;%s&amp;quot; lang=&amp;quot;%s&amp;quot; dir=&amp;quot;%s&amp;quot; style=&amp;quot;white-space:nowrap&amp;quot;&amp;gt;%s&amp;lt;/time&amp;gt;&amp;#039;&lt;br /&gt;
		datestr = pat:format(class, timeStamp, lang, langObj:getDir(), datestr)&lt;br /&gt;
	end&lt;br /&gt;
	return datestr&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===========================================================================&lt;br /&gt;
-- === Version of the function to be called from template namespace&lt;br /&gt;
-- ===========================================================================&lt;br /&gt;
&lt;br /&gt;
--[[ ========================================================================================&lt;br /&gt;
Date&lt;br /&gt;
 &lt;br /&gt;
This function is the core part of the ISOdate template. &lt;br /&gt;
 &lt;br /&gt;
Usage:&lt;br /&gt;
{{#invoke:DateI18n|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}&lt;br /&gt;
 &lt;br /&gt;
Parameters:&lt;br /&gt;
  * year, month, day, hour, minute, second: broken down date-time component strings&lt;br /&gt;
  * tzhour, tzmin: timezone offset from UTC, hours and minutes&lt;br /&gt;
  * lang: The language to display it in&lt;br /&gt;
  * case: Language format (genitive, etc.) for some languages&lt;br /&gt;
  * class: CSS class for the &amp;lt;time&amp;gt; node, use &amp;quot;&amp;quot; for no metadata at all&lt;br /&gt;
]]&lt;br /&gt;
function p.Date(frame)&lt;br /&gt;
	-- get args&lt;br /&gt;
	local args = {}&lt;br /&gt;
	for key, value in pairs(frame.args) do &lt;br /&gt;
		local trimmed_key = string.gsub(string.lower(mw.text.trim(key)), &amp;#039; &amp;#039;, &amp;#039;_&amp;#039;)&lt;br /&gt;
		local trimmed_value = mw.text.trim(value)&lt;br /&gt;
		if trimmed_key ~= &amp;#039;class&amp;#039; and trimmed_value == &amp;#039;&amp;#039; then&lt;br /&gt;
			trimmed_value = nil&lt;br /&gt;
		end&lt;br /&gt;
		args[trimmed_key] = trimmed_value&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- default values&lt;br /&gt;
	-- Allows to set the html class of the time node where the date is included. This is useful for microformats.&lt;br /&gt;
	args.class = args.class or &amp;#039;-&amp;#039;&lt;br /&gt;
	if args.class == &amp;#039;&amp;#039; then&lt;br /&gt;
		args.class = &amp;#039;dtstart&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	-- By default, pad one- and two-digit years to be 4 digits long, while keeping three-digit years as-is.&lt;br /&gt;
	args.trim_year = args.trim_year or &amp;#039;100-999&amp;#039;&lt;br /&gt;
	&lt;br /&gt;
	return p._Date(	&lt;br /&gt;
		{args.year, args.month, args.day, args.hour, args.minute, args.second, args.tzhour, args.tzmin},&lt;br /&gt;
		args.lang,&lt;br /&gt;
		args.case,	&lt;br /&gt;
		args.class,&lt;br /&gt;
		args.trim_year&lt;br /&gt;
	)	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Colani</name></author>
	</entry>
</feed>