「モジュール: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  
]]
]]
require('Module:No globals')


-- ==================================================
-- =======================================
-- === Internal functions ===========================
-- === 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 == 1 then  
val = tostring(val)
return true
end
elseif val == 0 then
if type(val) == 'string' then
return false
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
elseif type(val) == 'string' then
    val = string.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
     end
     return default
     return default
end
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, Y, MDhms, MDhm, MD, M
--  * datenum - same array but holding only numbers or nuls
local function parserDatevec(datevec)
-- create datecode based on which variables are provided and check for out-of-bound values
local maxval = {  1/0, 12, 31, 23, 59, 60,  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 codes = { 'Y', 'M', 'D', 'h', 'm', 's', '', '' } -- WARNING: 'M' alone would be ambiguous if it does not follow 'Y' or 'h'
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second, tzhour, tzmin]
for i = 1, 8 do
        local c, val = codes[i], datevec[i]
        if c == 'M' and type(val) == 'string' and val ~= '' and not tonumber(val) then
            -- When the month is not a number, check if it's a month name in the project's language.
            val = mw.getContentLanguage():formatDate('n', val)
        end
        val = tonumber(val)
        if val and val >= minval[i] and val <= maxval[i] then -- These tests work with infinite min/max values.
            if c == 'm' then -- Field for minute accepted only if it follows another valid 'M',  'D' or 'h' field.
              if not string.find('MDh', datecode:sub(-1)) then
                  c = ''; val = nil -- field for minute is invalid
              end
            elseif c == 's' then -- Field for leap second '60' is valid only at end of 23:59 UTC, on 30 June or 31 December of specific years.
              if val == 60 and not( -- Leap second are are added (or dropped) on specific dates planned only some months before.
                  datenum[1] and -- A year is specified (to check it would require constantly maintaining a table of dates).
                  (datenum[2] == 6 and datenum[3] == 30 or datenum[2] == 12 and datenum[3] == 31) and
                  datenum[4] == 23 and datenum[5] == 59
              ) then
                  c = ''; val = nil -- Field for second is invalid in this case, don't add the field.
              end
            end
datecode = datecode .. c
            datenum[i] = val
end
end
return datecode, datenum
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
--  * datecode - a code specifying content of the array where Y' is year, 'M' is month,
--    'D' is day, 'H' is hour, 'i' minute, 's' is second.
--    Output has to be one of YMDhms, YMDhm, YMD, YM, Y, MDhms, MDhm, MD, M.
--  * 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 - with possible corrections
local function getTimestamp(datecode, datenum)
-- 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] )
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 )
    -- 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


---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
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 datecode,  datenum  = parserDatevec(datevec)
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(datecode, datenum)
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.sub(1, 5) == 'YMDhm' or datecode.sub(1, 4) == 'MDhm') then
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.sub(1,1) == 'Y' then --
if class and class ~= '' and class ~= '-' and datecode:sub(1,1) == 'Y' then  
datestr =
local pat = '<time class="%s" datetime="%s" lang="%s" dir="%s" style="white-space:nowrap">%s</time>'
            ('<time class="%s" datetime="%s" lang="%s" dir="%s" style="white-space:nowrap">%s</time>')
datestr = pat:format(class, timeStamp, lang, langObj:getDir(), datestr)
            :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
-- ===========================================================================


--[[ ========================================================================================
--[[ ========================================================================================
匿名利用者