「モジュール:DateI18n」の版間の差分
ナビゲーションに移動
検索に移動
use require('strict') instead of require('Module:No globals')
細 (1版 をインポートしました) |
bsd>WOSlinker (use require('strict') instead of require('Module:No globals')) |
||
15行目: | 15行目: | ||
* User:Jarekt - original version of the functions mimicking template:Date | * User:Jarekt - original version of the functions mimicking template:Date | ||
]] | ]] | ||
-- ================================================== | -- ======================================= | ||
-- === | -- === Dependencies ====================== | ||
-- ================================================== | -- ======================================= | ||
require('strict') | |||
-- ======================================= | |||
-- === Local Functions =================== | |||
-- ======================================= | |||
-- Function allowing for consistent treatment of boolean-like wikitext input. | ------------------------------------------------------------------------------ | ||
-- It works similarly to Module:Yesno | --[[ (copied from Module:Core) | ||
Function allowing for consistent treatment of boolean-like wikitext input. | |||
Inputs: | |||
1) val - value to be evaluated, outputs as a function of values: | |||
true : true (boolean), 1 (number), or strings: "yes", "y", "true", "1" | |||
false : false (boolean), 0 (number), or strings: "no", "n", "false", "0" | |||
2) default - value to return otherwise | |||
See Also: It works similarly to Module:Yesno | |||
]] | |||
local function yesno(val, default) | local function yesno(val, default) | ||
if type(val) == 'boolean' then | if type(val) == 'boolean' then | ||
return val | return val | ||
elseif type(val) == 'number' then | elseif type(val) == 'number' then | ||
if val == | val = tostring(val) | ||
end | |||
if type(val) == 'string' then | |||
return | local LUT = { | ||
yes=true , y=true , ['true'] =true , t=true , ['1']=true , on =true, | |||
no =false, n=false, ['false']=false, f=false, ['0']=false, off=false } | |||
val = LUT[mw.ustring.lower(val)] -- put in lower case | |||
if (val~=nil) then | |||
return val | |||
end | end | ||
end | end | ||
return default | return default | ||
end | end | ||
--------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ||
213行目: | 118行目: | ||
-- Compatibility of legacy data using 'HMS' or 'HM', where 'M' is ambiguous | -- Compatibility of legacy data using 'HMS' or 'HM', where 'M' is ambiguous | ||
T.YMDhms = T.YMDhms or T.YMDHMS | T.YMDhms = T.YMDhms or T.YMDHMS | ||
T.YMDhm = T.YMDhm or T.YMDHM | T.YMDhm = T.YMDhm or T.YMDHM | ||
datecode = datecode == 'YMDHMS' and 'YMDhms' or datecode == 'YMDHM' and 'YMDhm' or datecode | datecode = datecode == 'YMDHMS' and 'YMDhms' or datecode == 'YMDHM' and 'YMDhm' or datecode | ||
240行目: | 145行目: | ||
-- * dFormat : input to {{#time}} function | -- * dFormat : input to {{#time}} function | ||
local function MonthCase(month, case, lang) | local function MonthCase(month, case, lang) | ||
if month == nil or case == nil then | |||
return nil | |||
end | |||
local T = {{},{},{},{},{},{},{},{},{},{},{},{}} | local T = {{},{},{},{},{},{},{},{},{},{},{},{}} | ||
local tab = mw.ext.data.get('I18n/MonthCases.tab', lang) | local tab = mw.ext.data.get('I18n/MonthCases.tab', lang) | ||
253行目: | 161行目: | ||
-- ================================================== | -- ================================================== | ||
local p = {} | local p = {} | ||
-- =========================================================================== | |||
-- === Functions accesible from the outside to allow unit-testing | |||
-- === Please do not use directly as they could change in the future | |||
-- =========================================================================== | |||
--------------------------------------------------------------------------------------- | |||
-- Single string replacement that ignores part of the string in "..." | |||
function p.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, 1) | |||
end | |||
table.insert(T, str) | |||
end | |||
return table.concat(T,'"') | |||
else | |||
return String:gsub(old, new, 1) | |||
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: | |||
-- * datenum - same array but holding only numbers or nuls | |||
function p.clean_datevec(datevec) | |||
-- create datecode based on which variables are provided and check for out-of-bound values | |||
-- check special case of month provided as a name | |||
local month = datevec[2] | |||
if type(month) == 'string' and month ~= '' and not tonumber(month) then | |||
-- When the month is not a number, check if it's a month name in the project's language. | |||
datevec[2] = mw.getContentLanguage():formatDate('n', month) | |||
end | |||
-- check bounds | |||
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 | |||
local minval = { -1/0, 01, 01, 00, 00, 00, -23, 00 } -- min values (or -1/0=-inf) for year, month, ... | |||
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second, tzhour, tzmin] | |||
for i = 1, 8 do | |||
local val = tonumber(datevec[i]) | |||
if val and val >= minval[i] and val <= maxval[i] then -- These tests work with infinite min/max values. | |||
datenum[i] = val | |||
end | |||
end | |||
-- leap second | |||
if tonumber(datevec[6]) == 60 then -- leap second '60' is valid only at end of 23:59 UTC, on 30 June or 31 December of specific years | |||
-- datenum[6] = 60 | |||
local MDhm = table.concat({unpack(datenum,2,5)}, ',') | |||
if (MDhm == table.concat({6, 30, 23, 59}, ',')) or (MDhm == table.concat({12, 31, 23, 59}, ',')) then | |||
datenum[6] = 60 | |||
end | |||
end | |||
return datenum | |||
end | |||
--------------------------------------------------------------------------------------- | |||
-- process datevec | |||
-- INPUT: | |||
-- * datenum - Array of {year,month,day,hour,minute,second, tzhour, tzmin} as numbers or nuls | |||
-- OUTPUT: | |||
-- * timeStamp - date string in the format taken by mw.language:formatDate lua function and {{#time}} parser function | |||
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate | |||
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time | |||
-- * 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, Y, MDhms, MDhm, MD, M. | |||
function p.getTimestamp(datenum) | |||
-- create datecode based on datenum | |||
local codes = { 'Y', 'M', 'D', 'h', 'm', 's'} | |||
local datecode = '' -- a string signifying which combination of variables was provided | |||
for i, c in ipairs(codes) do | |||
datecode = datecode .. (datenum[i] and c or '') -- if datenum[i] than append codes[i] to datecode | |||
end | |||
-- create timestamp string (for example 2000-02-20 02:20:20) based on which variables were provided | |||
local timeStamp | |||
-- date starting by a year | |||
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' -- 'YMDhms', 'YMDhm' and 'YMD' are the only supported format starting with 'YMD'; all others will be converted to 'YMD'. | |||
elseif datecode:sub(1,2) == 'YM' then | |||
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] ) | |||
datecode = 'YM' | |||
elseif datecode:sub(1,1)=='Y' then | |||
timeStamp = string.format('%04i', datenum[1] ) | |||
datecode = 'Y' | |||
-- date starting by a month (the implied year is 2000) | |||
elseif datecode== 'MDhms' then | |||
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', 2000, datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] ) | |||
elseif datecode == 'MDhm' then | |||
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', 2000, datenum[2], datenum[3], datenum[4], datenum[5] ) | |||
elseif datecode:sub(1,2) == 'MD' then | |||
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] ) | |||
datecode = 'MD' -- 'MDhms', 'MDhm' and 'MD' are the only supported format starting with 'MD'; all others will be converted to 'MD' | |||
elseif datecode:sub(1,1) == 'M' then -- Ambiguous: could mean minutes, but here means month (when parsed as a name/abbrev, not as a number). | |||
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 ) | |||
datecode = 'M' | |||
-- other possible but unrecognized formats (e.g. 'DHis', 'DHi', 'D', 'His', 'Hi'); | |||
-- note that 'Dh', 'D', 'h', 's' may eventually work, but not 'm' for minute only, which is ambiguous with 'M' for month only. | |||
else | |||
timeStamp = nil -- format not supported | |||
end | |||
return timeStamp, datecode | |||
end | |||
-- =========================================================================== | |||
-- === Version of the function to be called from other LUA codes | |||
-- =========================================================================== | |||
--[[ ======================================================================================== | --[[ ======================================================================================== | ||
280行目: | 309行目: | ||
-- process datevec and extract timeStamp and datecode strings as well as numeric datenum array | -- process datevec and extract timeStamp and datecode strings as well as numeric datenum array | ||
local | local datenum = p.clean_datevec(datevec) | ||
local year, month, day = datenum[1], datenum[2], datenum[3] | local year, month, day = datenum[1], datenum[2], datenum[3] | ||
local timeStamp, datecode = getTimestamp( | local timeStamp, datecode = p.getTimestamp(datenum) | ||
if not timeStamp then -- something went wrong in parserDatevec | if not timeStamp then -- something went wrong in parserDatevec | ||
return '' | return '' | ||
303行目: | 332行目: | ||
dFormat = dFormat:gsub('F"pi"', 'F') | dFormat = dFormat:gsub('F"pi"', 'F') | ||
elseif case == 'gen' then | elseif case == 'gen' then | ||
dFormat = strReplace(dFormat, "F", "xg") | dFormat = p.strReplace(dFormat, "F", "xg") | ||
elseif case == 'nom' then | elseif case == 'nom' then | ||
dFormat = strReplace(dFormat, "xg", "F") | dFormat = p.strReplace(dFormat, "xg", "F") | ||
elseif case ~= '' then | elseif case ~= '' and month ~= nil then | ||
-- see is page [[Data:I18n/MonthCases.tab]] on Commons have name of the month | -- 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 | -- in specific gramatic case in desired language. If we have it than replace | ||
312行目: | 341行目: | ||
local monthMsg = MonthCase(month, case, lang) | local monthMsg = MonthCase(month, case, lang) | ||
if monthMsg and monthMsg ~= '' then -- make sure it exists | 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 = p.strReplace(dFormat, 'F', '"'..monthMsg..'"') -- replace default month with month name we already looked up | ||
dFormat = strReplace(dFormat, 'xg', '"'..monthMsg..'"') | dFormat = p.strReplace(dFormat, 'xg', '"'..monthMsg..'"') | ||
end | end | ||
end | end | ||
332行目: | 361行目: | ||
-- Decide if the year will stay padded with zeros (for years in 0-999 range). | -- Decide if the year will stay padded with zeros (for years in 0-999 range). | ||
if year and year < 1000 then | if year and year < 1000 then | ||
trim_year = yesno(trim_year, '100-999') | trim_year = yesno(trim_year, trim_year or '100-999') | ||
if type(trim_year) == 'string' then | if type(trim_year) == 'string' then | ||
-- If `trim_year` not a simple boolean, then it's a range of dates. | -- If `trim_year` not a simple boolean, then it's a range of dates. | ||
345行目: | 374行目: | ||
-- Append a timezone if present (after the hour and minute of the day). | -- Append a timezone if present (after the hour and minute of the day). | ||
if datenum[7] and (datecode | if datenum[7] and (datecode:sub(1, 5) == 'YMDhm' or datecode:sub(1, 4) == 'MDhm') then | ||
-- Use {{#time}} parser function to create timezone string, so that we use the correct character set. | -- Use {{#time}} parser function to create timezone string, so that we use the correct character set. | ||
local sign = (datenum[7]<0) and '−' or '+' | local sign = (datenum[7]<0) and '−' or '+' | ||
354行目: | 383行目: | ||
-- HTML formating of date string and tagging for microformats (only for absolute dates with a year). | -- HTML formating of date string and tagging for microformats (only for absolute dates with a year). | ||
if class and class ~= '' and datecode | if class and class ~= '' and class ~= '-' and datecode:sub(1,1) == 'Y' then | ||
local pat = '<time class="%s" datetime="%s" lang="%s" dir="%s" style="white-space:nowrap">%s</time>' | |||
datestr = pat:format(class, timeStamp, lang, langObj:getDir(), datestr) | |||
end | end | ||
return datestr | return datestr | ||
end | end | ||
-- =========================================================================== | |||
-- === Versions of the function to be called from template namespace | |||
-- =========================================================================== | |||
--[[ ======================================================================================== | --[[ ======================================================================================== |