模組:RailRouteTimer
调用
本模块支持调用下列函数来获取数据(或 MediaWiki 内容)。
调用名称 | 参数 | 说明 |
---|---|---|
trainTime |
{{#invoke:RailRouteTimer | trainTime |
通过线路编号、运营方向(预定义)、首/末班及始发站到达本站的时间获得车站首班/末班车时间。 |
trainDirectionTime |
{{#invoke:RailRouteTimer | trainDirectionTime |
通过线路编号、运营方向(预定义)、首/末班及始发站到达本站的时间获得车站首班/末班车终点站名链接及时间组成的 WikiText。 |
local p = {
DEFAULT_TRAIN_TIME_TEMPLATE = '往{station_link}方向:{train_time|H:i|次日}',
DEFAULT_STATION_TRAIN_TIME_TEMPLATE = '{start_time|H:i|次日}-{end_time|H:i|次日}',
}
local function _convBool(val, default)
if val == nil then
return default
elseif type(val) == 'boolean' then
return val
elseif mw.text.trim(val) == '' then
return default
end
return (mw.text.trim(val) == '1')
end
local function literalize(str)
return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", function(c) return "%" .. c end)
end
local function plainReplace(src, substr, repl)
substr = literalize(substr)
local rstr, _ = src:gsub(substr, repl)
return rstr
end
local function _parseTime(time_str)
local base_parts = mw.text.split(time_str, ':')
local parts_size = table.getn(base_parts)
local hour = 0
local minute = 0
local sec = 0
if parts_size == 1 then
minute = tonumber(mw.text.trim(base_parts[1]))
elseif parts_size == 2 then
hour = tonumber(mw.text.trim(base_parts[1]))
minute = tonumber(mw.text.trim(base_parts[2]))
elseif parts_size == 3 then
hour = tonumber(mw.text.trim(base_parts[1]))
minute = tonumber(mw.text.trim(base_parts[2]))
sec = tonumber(mw.text.trim(base_parts[3]))
else
error('非法的时间输入')
end
return hour, minute, sec
end
local function _extractSeconds(totalsec)
local hour = math.floor(totalsec / 3600)
local minute = math.floor(totalsec % 3600 / 60)
local sec = totalsec % 60
return hour, minute, sec
end
local function _loadSystemData(system, raises)
local system_data = nil
local state
if raises == nil then
raises = true
end
if system ~= nil then
state, system_data = pcall(mw.loadData, "Module:Adjacent stations/" .. system)
if not state then
if raises then
error(string.format('铁道系统“%s”的数据模块不存在', system))
else
system_data = nil
end
end
end
return system_data
end
local function getLineData(system_data, line_code)
if system_data.aliases then
line_code = system_data.aliases[mw.ustring.lower(line_code)] or line_code
end
return system_data.lines[line_code]
end
function p._internalTrainTime(line_code, dir, type_, delta, system)
local system_data = _loadSystemData(system)
local dir_info = getLineData(system_data, line_code).routes[dir]
local bhour, bmin, bsec = _parseTime(dir_info[type_])
local dhour, dmin, dsec = _parseTime(delta)
local totalsec = (bhour * 60 + bmin) * 60 + bsec + (dhour * 60 + dmin) * 60 + dsec
local hour, minute, sec = _extractSeconds(totalsec)
return dir_info["service end"], hour, minute, sec
end
function p._formatTemplateTime(frame, sparts, hour, minute, second)
local tomorrow_prefix = ''
if table.getn(sparts) >= 3 then
if hour >= 24 then
tomorrow_prefix = mw.text.trim(sparts[3])
if tomorrow_prefix == '' then tomorrow_prefix = '次日' end
hour = hour - 24
end
end
local time_str = string.format('%d:%d:%d', hour, minute, second)
return tomorrow_prefix .. frame:callParserFunction{ name = '#time', args = { sparts[2], time_str } }
end
function p.trainTime(frame)
local a = frame.args
if a['type'] == 'f' or a['type'] == 'F' then a['type'] = 'first train' end
if a['type'] == 'l' or a['type'] == 'L' then a['type'] = 'last train' end
local terminal, hour, minute, sec = p._internalTrainTime(a.name, a.dir, a['type'], a.delta, a.system)
return string.format('%02d:%02d', hour, minute)
end
function p.trainDirectionTime(frame)
local str = frame.template
local adj_result
if str == nil then str = p.DEFAULT_TRAIN_TIME_TEMPLATE end
local a = frame.args
if a['type'] == 'f' or a['type'] == 'F' then a['type'] = 'first train' end
if a['type'] == 'l' or a['type'] == 'L' then a['type'] = 'last train' end
local terminal, hour, minute, sec = p._internalTrainTime(a.name, a.dir, a['type'], a.delta, a.system)
local l = 0
while true do
local l, r, c = string.find(str, "%{([^%}]+)%}", l + 1)
if l == nil then break end
local sparts = mw.text.split(c, '|')
if sparts[1] == 'station_name' then
str = plainReplace(str, '{' .. c .. '}', terminal)
elseif sparts[1] == 'station_link' then
adj_result = require('Module:Adjacent stations')._station({
system = a.system,
station = terminal,
line = nil,
type = nil,
}, frame)
str = plainReplace(str, '{' .. c .. '}', adj_result)
elseif sparts[1] == 'train_time' then
str = plainReplace(str, '{' .. c .. '}', p._formatTemplateTime(frame, sparts, hour, minute, sec))
end
end
return str
end
function p.stationTrainTime(frame)
local a = frame.args
local dir_reprs = {}
local system_data = _loadSystemData(a.system)
local min_time_data = 999999
local max_time_data = -999999
local line_names = mw.text.split(a.name, ',')
for k, v in pairs(a) do
if type(k) == 'number' then
dir_reprs[k] = v
end
end
for k, line_name in ipairs(line_names) do
for i = 1, table.getn(dir_reprs), 2 do
local dir = dir_reprs[i]
local diff = dir_reprs[i + 1]
local dir_times = getLineData(system_data, line_name).routes[dir]
if dir_times ~= nil then
local dh, dm, ds = _parseTime(diff)
for _, dir_time in ipairs({dir_times["first train"], dir_times["last train"]}) do
if dir_time ~= nil then
local h, m, s = _parseTime(dir_time)
local t = (h * 60 + m) * 60 + s + (dh * 60 + dm) * 60 + ds
min_time_data = math.min(min_time_data, t)
max_time_data = math.max(max_time_data, t)
end
end
end
end
end
local min_h, min_m, min_s = _extractSeconds(min_time_data)
local max_h, max_m, max_s = _extractSeconds(max_time_data)
local str = frame.template
if str == nil then str = p.DEFAULT_STATION_TRAIN_TIME_TEMPLATE end
local l = 0
while true do
local l, r, c = string.find(str, '%{([^%}]+)%}', l + 1)
if l == nil then break end
local sparts = mw.text.split(c, '|')
if sparts[1] == 'start_time' then
str = plainReplace(str, '{' .. c .. '}', p._formatTemplateTime(frame, sparts, min_h, min_m, min_s))
elseif sparts[1] == 'end_time' then
str = plainReplace(str, '{' .. c .. '}', p._formatTemplateTime(frame, sparts, max_h, max_m, max_s))
end
end
return str
end
function p.testCases()
local frame = mw.getCurrentFrame()
local function mkfp(func_name)
local fun = p[func_name]
local function call_frame(frame)
return fun(mw.getCurrentFrame():newChild(frame))
end
return call_frame
end
local fp = {
trainDirectionTime = mkfp('trainDirectionTime'),
stationTrainTime = mkfp('stationTrainTime'),
}
-- trainTime test
if p.trainTime{args={name='1', dir='霞高', ['type']='L', delta='0:3', system='宁波轨道交通'}} ~= '22:03' then
error('trainTime test failed: dir=霞高, type=L, delta=0:3.')
end
if p.trainTime{args={name='1', dir='高霞', ['type']='F', delta='0:4', system='宁波轨道交通'}} ~= '05:59' then
error('trainTime test failed: dir=高霞, type=F, delta=0:4.')
end
-- trainDirectionTime test
if fp.trainDirectionTime{args={name='1', dir='霞高', ['type']='L', delta='0:3', system='宁波轨道交通'}} ~= "往[[高桥西站 (宁波市)|高桥西]]方向:22:03" then
error('trainDirectionTime test failed: dir=霞高, type=L, delta=0:3.')
end
if fp.trainDirectionTime{args={name='1', dir='高霞', ['type']='F', delta='0:4', system='宁波轨道交通'}} ~= "往[[霞浦站 (宁波市)|霞浦]]方向:05:59" then
error('trainDirectionTime test failed: dir=高霞, type=F, delta=0:4.')
end
-- stationTrainTime test
if fp.stationTrainTime{args={'高霞', '0:3', name='1', system='宁波轨道交通'}} ~= "05:58-22:36" then
error('stationTrainTime test failed: 高霞, 0:3, name=1.')
end
if fp.stationTrainTime{args={'高霞', '0:3', '栎清', '0:1', name='1,2', system='宁波轨道交通'}} ~= "05:58-22:36" then
error('stationTrainTime test failed: 高霞, 0:3, name=1,2.')
end
end
return p