---  
This module is intended for processing of date strings.  
Copied from https://commons.wikimedia.org/w/index.php?title=Module:Date&oldid=224728211
Used by Module:ISOdate, Module:Complex date, Module:WikidataIB  
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 i18n = require('Module:I18n/date') -- get localized translations of date formats
local yesno = require('Module:Yesno')  
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end  
---
Date  
This function can be used to provide an ISOdate template.  
Usage:  
Parameters:
year,month,day,hour,minute,second: broken down date-time component strings
tzhour, tzmin: timezone offset from UTC, hours and minutes
lang: The language to display it in
case: Language format (genitive, etc.) for some languages
class: CSS class for the <time> node, use "" for no metadata at all  
Error Handling:  
function p.Date
local args = .args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = :callParserFunction( "int", "lang" ) -- get user's chosen language
end
return p._Date(
{
args.year or '',
args.month or '',
args.day or '',
args.hour or '',
args.minute or '',
args.second or '',
args.tzhour or '',
args.tzmin or ''
},
args.lang, -- language
args.case or '', -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
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
end  
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the format
for i = #datevec + 1, 8 do
datevec[i] = ''
end
if not case then case = '' end
if not class then end
if not trim_year then trim_year = '100-999' end  
-- if language is not provided than look up users language
-- WARNING: This step should be done by the template as it does not seem to work as well here (cache issues?)
if not lang or not mw.language.isValidCode( lang ) then
lang = 'en'
end  
-- Just in case someone broke the internationalization code than fix the english defaults
if i18n.DateLang['en'] == nil then
i18n.DateLang['en'] = 'en-form'
end
if i18n.DateFormat['en-form'] == nil then
i18n.DateFormat['en-form'] = {YMDHMS='j F Y, H:i:s', YMDHM='j F Y, H:i', YMD='j F Y', YM='F Y', MD='j F', Y='Y'}
end  
-- create datecode based on which variables are provided and check for out of bound values
local maxval = {9999, 12, 31, 23, 59, 60, 23, 59} -- max values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', *, *}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i, v in ipairs( datevec ) do
if v~=nil and v~='' then
datecode = datecode .. c[i]
datenum[i] = tonumber(v)
if datenum[i]==nil and i==2 then
-- month is not a number -> check if it is a month name in English
v = mw.language.new('en'):formatDate( "n", v)
datenum[i] = tonumber(v)
end
if datenum[i]==nil or datenum[i]>maxval[i] then
-- Some numbers are out of range -> abort and return the empty string
return ''
end
end
end  
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 ) -- date not complete -> no html formating or micro-tagging of date string
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] ) -- date not complete -> no html formating or micro-tagging of date string
else
return '' -- format not supported
end  
-- ==========================================================
-- === Create Date String using in chosen language
-- ==========================================================  
-- which form should the date take?
-- Use langSwitch to pick formating for each language
local langDateForm = langSwitch(i18n.DateLang, lang)  
-- special case of French and Gallic dates, which require different date format for the 1st day of the month
if datenum[3]==1 and (langDateForm=='fr-form' or langDateForm=='ga-form') then
langDateForm = langDateForm .. '1' -- ordinal form for the first day of the month
end
-- special case of Basque dates, which require different date format for the 1st, 11th, 21st and 31st day of the month
if langDateForm=='eu-form' then
if (datenum[3]==1 or datenum[3]==21) then
langDateForm = 'eu-form01'
elseif (datenum[3]==11 or datenum[3]==31) then
langDateForm = 'eu-form11'
end
end  
-- Look up country specific format input to function
local dFormat = i18n.DateFormat[langDateForm][datecode]  
-- overwrite default grammatical case of the month (applies mostly to Slavic languages)
if (case=='gen') then
-- CAUTION: at the moment i18n.DateFormat uses "F" only as month name, but this might change and this operation does not check if 'F' is in "" brackets or not, so if some language starts using 'F' in "" than this will not work for that language
dFormat = dFormat:gsub("F", "xg");
end
if (case=='nom') then
-- CAUTION: at the moment i18n.DateFormat uses "xg" only as month name, but this might change and this operation does not check if 'xg' is in "" brackets or not, so if some language starts using 'xg' in "" than this will not work for that language
dFormat = dFormat:gsub("xg", "F");
end
if ((lang=='ru' or lang=='pl' or lang=='cs' or lang=='sl' or lang=='sk') and (case=='loc' or case=='ins')) or
(lang=='fi' and (case=='ptv' or case=='ine'or case=='ela'or case=='ill') ) then
local monthEn = mw.language.new('en'):formatDate( "F", timeStamp) -- month name in English
-- month name using proper case and language. It relies on messages stored in MediaWiki namespace for some cases and languages
-- That is why this IF statement uses "lang" not "langDateForm" variable to decide
local monthMsg = mw.message.new( string.format('%s-%s', monthEn, case ) ):inLanguage( lang )
if not monthMsg:isDisabled then -- make sure it exists
local month=monthMsg:plain
dFormat = dFormat:gsub('F', '"'..month..'"'); -- replace default month with month name we already looked up
dFormat = dFormat:gsub('xg', '"'..month..'"');
end
end
-- Special case related to Quechua and Kichwa languages
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
if (lang=='qu' or lang=='qug') and case=='nom' then
dFormat = dFormat:gsub('F"pi"', 'F');
end  
-- Lua only date formating using parser function (new)
-- prefered call which gives "Lua error: too many language codes requested." on the Module talk:Date/sandbox/testcases page
--local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp)