모듈:DateI18n

--

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 
at Module:Date/sandbox/testcases and Module talk:Date/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 
require('strict')
-- ==============================================**
-- === Internal functions =======================**
-- ==============================================**
-- Function allowing for consistent treatment of boolean-like wikitext input.
-- It works similarly to Module:Yesno
local function yesno(val, default)
	if type(val) == 'boolean' then
		return val
	elseif type(val) == 'number' then
		if val==1 then 
			return true
		elseif val==0 then
			return false
		end
	elseif type(val) == 'string' then
	    val = mw.ustring.lower(val)  -- put in lower case
	    if val == 'no'  or val == 'n' or val == 'false' or tonumber(val) == 0 then
	        return false
	    elseif val == 'yes' or val == 'y' or val == 'true'  or tonumber(val) == 1 then
	        return true
	    end
end
return default
end
---------------------------------------------------------------------------------------
-- String replacement that ignores part of the string in "..."
local function strReplace(String, old, new)
	if String:find('"') then
		local T={}
		for i, str in ipairs(mw.text.split( String, '"', true )) do
			if i%2==1 then
				str = str:gsub(old, new)
			end
			table.insert(T, str)
		end
		return table.concat(T,'"')
	else
		return String:gsub(old, new)
	end
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
--  * datevec - Array of {year,month,day,hour,minute,second, tzhour, tzmin} containing broken 
--    down date-time component strings or numbers
-- OUTPUT:
--  * datecode - a code specifying content of the array where Y' is year, 'M' is month, 
--     'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
--  * datenum - same array but holding only numbers or nuls
local function parserDatevec(datevec)
	-- if month is not a number than check if it is a month name in project's language
	local month = datevec2
	if month and month~= and not tonumber(month) then
		datevec2 = mw.getContentLanguage():formatDate( "n", month)
	end
	-- create datecode based on which variables are provided and check for out-of-bound values
	local maxval = {nil, 12, 31, 23, 59, 59,  23, 59} -- max values for year, month, ...
	local minval = {nil,  1,  1,  0,  0,  0, -23,  0} -- min 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 = 1,8 do
		datenumi = tonumber(dateveci)
		if datenumi and (i==1 or (datenumi>=minvali and datenumi10 then
			-- special (and messy) case of dFormat code depending on a day number
			-- then json contains a string with more json containing "default" field and 2 digit day keys
			-- if desired day is not in that json than use "default" case
			dFormat = dFormat:gsub('”','"') -- change fancy double quote to a straight one, used for json marking
			local D = mw.text.jsonDecode( dFormat )		--com = mw.dumpObject(D)
			day = string.format('d%02i',day) -- create day key
			dFormat = Dday or D.default
			dFormat = dFormat:gsub("'", '"') -- change single quote to a double quote, used for  marking
		end
		return dFormat
	end
	
	local T = {}
	local tab = mw.ext.data.get('DateI18n.tab', lang)
	for _, row in pairs(tab.data) do -- convert the output into a dictionary table
		local id, _, msg = unpack(row)
		Tid = msg
	end
	local dFormat = Tdatecode
	if dFormat=='default' and (datecode=='YMDHMS' or datecode=='YMDHM')  then 
		-- for most languages adding hour:minute:second is done by adding ", HH:MM:SS to the 
		-- day precission date, those languages are skipped in DateI18n.tab and default to 
		-- English which stores word "default"
		dFormat = parseFormat(T'YMD', day).. ', H:i'
		if datecode=='YMDHMS' then
			dFormat = dFormat .. ':s'
		end
	else
		dFormat = parseFormat(dFormat, day)
	end
	return dFormat
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to  parser function
-- INPUTS:
--  * month : month number
--  * case  : gramatic case abbriviation, like "ins", "loc"
--  * lang  : language
-- OUTPUT:
--  * dFormat : input to  function
local function MonthCase(month, case, lang)
	local T = 
	local tab = mw.ext.data.get('I18n/MonthCases.tab', lang)
	for _, row in pairs(tab.data) do
		local mth, cs, msg = unpack(row)
		Tmthcs = msg
	end
	return Tmonthcase
end
-- ==============================================**
-- === External functions =======================**
-- ==============================================**
local p = {}
-- ====================================================================================**
Date

This function is the core part of the ISOdate template. 

Usage:
local Date = require('Module:DateI18n')._Date
local dateStr = Date({2020, 12, 30, 12, 20, 11}, lang)

Parameters:- {year,month,day,hour,minute,second, tzhour, tzmin}: broken down date-time component strings or numbers
		tzhour, tzmin are timezone offsets 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  node, use "" for no metadata at all
function p._Date(datevec, lang, case, class, trim_year)	
	-- make sure inputs are in the right format
	if not lang or not mw.language.isValidCode( lang ) then
		lang = mw.getCurrentFrame():callParserFunction( "int", "lang" ) -- get user's chosen language
	end
	if lang == 'be-tarsk' then
		lang = 'be-x-old'
	end
	
	-- process datevec and extract timeStamp and datecode strings as well as numeric datenum array
	local datecode,  datenum  = parserDatevec(datevec)
	local year, month, day = datenum1, datenum2, datenum3
	local timeStamp, datecode = getTimestamp(datecode, datenum)
	if not timeStamp then -- something went wrong in parserDatevec
		return 
	end
	-- Commons Data:DateI18n.tab page stores prefered formats for diferent 
	-- languages and datecodes (specifying year-month-day or just year of month-day, etc)
	-- Look up country specific format input to  function
	local dFormat = getDateFormat(datecode, day, lang)
	-- By default the gramatical case is not specified (case==) allowing the format to be specified 
	-- in Data:DateI18n.tab. You can overwrite the default grammatical case of the month by 
	-- specifying "case" variable. This is needed mostly by Slavic languages to create more complex 
	-- phrases as it is done in c:Module:Complex date
	case = case or 
	if (lang=='qu' or lang=='qug') and (case=='nom') then
		-- Special case related to Quechua and Kichwa languages. The form in the I18n is
		--  Genitive case with suffix "pi" added to month names provided by {#time}}
		-- in Nominative case that "pi" should be removed
		-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
		dFormat = dFormat:gsub('F"pi"', 'F')
	elseif (case=='gen') then
		dFormat = strReplace(dFormat, "F", "xg")
	elseif (case=='nom') then
		dFormat = strReplace(dFormat, "xg", "F")
	elseif (case ~= ) then
		-- see is page Data:I18n/MonthCases.tab on Commons have name of the month 
		-- in specific gramatic case in desired language. If we have it than replace 
		-- "F" and xg" in dFormat
		local monthMsg = MonthCase(month, case, lang)
		if  monthMsg and monthMsg ~=  then -- make sure it exists
			dFormat = strReplace(dFormat, 'F',  '"'..monthMsg..'"') -- replace default month with month name we already looked up
			dFormat = strReplace(dFormat, 'xg', '"'..monthMsg..'"')
		end
	end
-- Translate the date using specified format
	-- See https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate and 
	-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions##time for explanation of the format
	local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp) -- same as using  parser function
	
	-- Special case related to Thai solar calendar: prior to 1940 new-year was at different 
	-- time of year, so just year (datecode=='Y') is ambiguous and is replaced by "YYYY or YYYY" phrase
	if lang=='th' and datecode=='Y' and year=tonumber(YMin) and year%s'
		datestr = DateHtmlTags:format(class, timeStamp, datestr)
	end
	return datestr
end
-- ====================================================================================**
Date

This function is the core part of the 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  node, use "" for no metadata at all
function p.Date(frame)
	local args = {}
	for name, value in pairs( frame.args ) do 
		name = string.gsub( string.lower(name), ' ', '_')
		argsname = value
	end
	return p._Date(	
		{ args.year, args.month, args.day, args.hour, args.minute, args.second, args.tzhour, args.tzmin },
		args.lang,                  -- language
		args.case,                  -- 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
return p