local module = {}
local getArgs = require('Module:Arguments').getArgs
--本模块用于解析任意时间文本,并选择任意的格式化形式。
--要注意,时间文本并非一种精确的数值,而是包含着模糊性、连续性的描述。
--初步将模糊理解为单位层级。
--要将时间文本的模糊记录下来。如果没有格式化要求,那么应当保留这种模糊性。
--格式化的要求允许指定某主要单位下显示/不显示,指定主要单位的首选、必选显示单位,指定次序。允许更改时区、进行计算。
--正常的格式化要求下不应该改变信息含量
--一个合法的时间必须有确定的c模糊度,确切的说应当是一个连续无中断信息。
--支持ISO 8601
--辅助处理
function string.findAny(text,patterns,index)
index=index or 1
for _,pattern in ipairs(patterns) do
local b,e=text:find(pattern,index)
if b then
return b,e
end
end
return false
end
local FormatString = {}
function string.buildTrie(formats)
root = {}
root.next = {}
for _, format in ipairs(formats) do
for i, item in ipairs(format) do
if item.format then
local cur = root
for i = 1, mw.ustring.len(item.format), 1 do
local c = mw.ustring.sub(item.format, i, i)
if cur.next[c] == nil then
cur.next[c] = {} -- new node
cur.next[c].next = {}
cur.next[c].father = cur
cur.next[c].char = c
end
cur = cur.next[c]
end
cur.item = item
cur.len = mw.ustring.len(item.format)
end
end
end
root.fail = {}
local q = require("Module:queue")
q:push(root)
while not(q:empty()) do
local cur = q:front()
q:pop()
for i, t in pairs(cur.next or {}) do
q:push(t)
end
if (cur ~= root) and (cur.father ~= root) then
cur2 = cur.father
while cur2.fail.next[cur.char] ~= nil and cur2 ~= root do
cur2 = cur2.fail
end
cur.fail = cur2.fail.next[cur.char] or root
else
cur.fail = root
end
end
return root
end
function string.replaceTrie(str, trie, extras)--用数据格式化文本
local cur_state = trie
local i = 1
--local matched_times = 0
while i <= mw.ustring.len(str) do
local c = mw.ustring.sub(str, i, i)
while cur_state.next[c] == nil and cur_state ~= trie do
cur_state = cur_state.fail or trie
end
cur_state = cur_state.next[c] or trie
if cur_state.item ~= nil then
--matched_times = matched_times + 1matched_times,
local newStr = cur_state.item:callback(extras)
-- mw.log(mw.ustring.sub(str, i - cur_state.len + 1, i)..' -> '..newStr)
str = mw.ustring.sub(str, 1, i - cur_state.len)..newStr..mw.ustring.sub(str, i + 1, mw.ustring.len(str))
i = i + mw.ustring.len(newStr) - cur_state.len
cur_state = trie
end
i = i + 1
end
return str
end
function string.matchTrie(str, trie,text,extras)--用文本获取数据
local cur_state = trie
local index=1
local i = 1
--local matched_times = 0
while i <= mw.ustring.len(str) do
local c = mw.ustring.sub(str, i, i)
while cur_state.next[c] == nil and cur_state ~= trie do
cur_state = cur_state.fail or trie
end
cur_state = cur_state.next[c] or trie
if cur_state.item ~= nil then
--matched_times = matched_times + 1
index = cur_state.item:matchback(extras,text,index)
if index>#text then
return index
end
-- mw.log(mw.ustring.sub(str, i - cur_state.len + 1, i)..' -> '..newStr)
cur_state = trie
end
i = i + 1
end
return index
end
--时间单位。
--时间单位将指示时间段限定的范畴。
--时间单位皆有name,range属性,analysis、format函数
--时间单位需要能够从时间文本、时间段中提取值,也能赋值到这两者之中。
--时间单位有着占用组别,它们有着优先度,占用同一数值。如上旬与三月占用同一组。每一组只能有一个单位适用。
--解析器是固定的,但格式化是可变的。
local timeUnit={name="nil",_min=1,_max=math.huge,_priority=0,_begin=0}
--构建函数--
function timeUnit.base(tag,priority)--基础单位。
local unit={
tag=tag,
_priority=priority,
}
return setmetatable(unit,timeUnit)
end
timeUnit.__index = timeUnit
function timeUnit:setName(text)--设置标签文本,仅用于调试
self.name=text
return self
end
function timeUnit:pattern(patternAnalysis,patternFormat)--设置模式字符串
self.patternAnalysis=patternAnalysis or self.patternAnalysis
self.patternFormat=patternFormat or self.patternFormat
return self
end
function timeUnit:fill(number)--指示格式输出数字的必须位数
self.patternFormat="%0"..number.."d"
return self
end
function timeUnit:patternFixed(pattern)--纯粹形式
self.patternAnalysis=pattern
self.patternFormat=pattern
return self
end
function timeUnit:patternPrefix(name)--前缀形式
self.patternAnalysis=name.."^(%d+)"
self.patternFormat=name.."%d"
return self
end
function timeUnit:patternSuffix(name)--后缀形式
self.patternAnalysis="^(%d+)"..name
self.patternFormat="%d"..name
return self
end--可以考虑中文数字支持
function timeUnit:priority(number)--设置优先度
self._priority=number
return self
end
function timeUnit:__lt(other)--排序使用
return self._priority<other._priority
end
function timeUnit:b(number)--设置最小精度开始于,即对于区块开始于
self._begin=number
return self
end
function timeUnit:_convertTo(value)--到time时的值转换
return value
end
function timeUnit:_convertFrom(value)--到字面时的值转换
return value
end
function timeUnit:ratio(number)--设置倍率
local convertTo=self._convertTo
self._convertTo=function(value)
return convertTo(value)*number
end
local convertFrom=self._convertFrom
self._convertFrom=function(value)
return convertFrom(value/number)
end
return self
end
function timeUnit:move(number)--设置值位移,即特殊情况下单位与实际值的差异(用于世纪)
local convertTo=self._convertTo
self._convertTo=function(value)
return convertTo(value)+number
end
local convertFrom=self._convertFrom
self._convertFrom=function(value)
return convertFrom(value-number)
end
return self
end
function timeUnit:accuracy(min,max)--设置精度
self._min=min
self._max=max
return self
end
function timeUnit:des(text)
self.description=text
return self
end
function timeUnit:formatFuc(fuc)--设置格式化函数
self.formatText=fuc
return self
end
function timeUnit:na()--指示不用于分析
self._na=true
return self
end
function timeUnit:f(pattern)--指示使用本unit格式化的指示符
self.format=pattern
return self
end
--延展构建--基于本单位的单位
function timeUnit:append()
self.__index = self
return setmetatable({},self)
end
function timeUnit:appendNumber(number)
local unit=self:append()
unit.patternAnalysis="^(%d+)"
unit.patternFormat="%d"
return unit
end
function timeUnit:appendPrefix(text)
local unit=self:append()
unit:patternPrefix(text)
return unit
end
function timeUnit:appendSuffix(text)
local unit=self:append()
unit:patternSuffix(text)
return unit
end
--功能函数
function timeUnit:available()--指示不用于分析
return not self._na
end
function timeUnit:priorityCompare(other)--判断优先低于
return self._priority<other._priority
end
function timeUnit:analysis(time,text,index)--从文字解析
local value=text:match(self.patternAnalysis,index)
if value then
--assert(type(value)=="number",self.patternAnalysis..";"..text)
_,index=text:find(self.patternAnalysis,index)--可以优化?
index=index+1
--time.units:push({self,value})
return value,index
end
return nil,index
end
function timeUnit._convertTo(value)--到time时的值转换
return value
end
function timeUnit._convertFrom(value)--到字面时的值转换
return value
end
function timeUnit:supplyTime(time,value)--为time赋值
time:setValue(self.tag,self._convertTo(value),self._min,self._max,self._begin)
end
function timeUnit:matchback(time,text,index) --分析用
local value,index=self:analysis(time,text,index)
if value then
self:supplyTime(time,value)
end
return index
end
function timeUnit:getValue(time)--从time获取值
local value=time:getValue(self.tag,self._min,self._max,self._begin,self._min)
return value and self._convertFrom(value)
end
function timeUnit:formatText(value)--返回格式化文本
return string.format(self.patternFormat,value)
end
function timeUnit:callback( time) --格式化用
local value= self:getValue(time)
if value then
return self:formatText(value)
end
return ""
end
timeUnit.Multi={}--多标签识别
function timeUnit.Multi:analysis(time,text,index)--从文字解析
local value={text:match(self.patternAnalysis,index)}
if value[1] then
_,index=text:find(self.patternAnalysis,index)--可以优化?
--time.units:push({self,value})
return value,index+1
end
return nil,index
end
function timeUnit.Multi:supplyTime(time,value)--为time赋值
for index,unit in ipairs(self.units) do
unit:supplyTime(time,value[index])
end
end
function timeUnit.Multi:formatText(value)--返回格式化文本
return string.format(self.patternFormat,unpack(value))
end
function timeUnit.Multi:getValue(time)
local t={}
for index,unit in ipairs(self.units) do
table.insert(t,unit:getValue(time))
end
return #t==#self.units and t
end
function timeUnit.multi(units)
local unit=timeUnit.base()
unit.units=units
unit._priority=units[#units]._priority
unit.analysis=timeUnit.Multi.analysis
unit.supplyTime=timeUnit.Multi.supplyTime
unit.formatText=timeUnit.Multi.formatText
unit.getValue=timeUnit.Multi.getValue
return unit
end
--[[--识别多个单位的连接
--这样就有点像语言识别系统了..说不定就该用语言识别系统做基础
function timeMultiUnit:analysis(time,text,index)
local i=index
local vt={}
for _,unit in ipairs(self.units) do
local value,i=unit:analysis(time,text,i)
if not value then
return nil,index
end
table.insert(vt,value)
end
return vt,i
end
function timeMultiUnit:supplyTime(time,value)--为time赋值
for index,unit in ipairs(self.units) do
unit:supplyTime(time,value[index])
end
end
function timeMultiUnit:callBack(_, time) --格式化用
local t={}
for index,unit in ipairs(self.units) do
table.insert(t,unit:callBack(_,time))
end
return table.concat(t)
end]]
timeUnit.List={}--文本列表单位,对应正当格式但不用阿拉伯数字描述的单位
function timeUnit.List:analysis(time,text,index)--从文字解析,匹配为time记录
for value,name in pairs(self.names) do
if text:sub(index,index+#name-1)==name then
--mw.log(self.tag,value,index+#name,index)
return value,index+#name
end
end
return nil,index
end
function timeUnit.List:formatText(value)--返回格式化文本
--mw.log(value,self.names[value])
return self.names[value] or ""
end
function timeUnit:appendList(names)--扩展数组形式的单位表
local unit=self:append()
unit.names=names
unit.analysis=timeUnit.List.analysis
unit.formatText=timeUnit.List.formatText
return unit
end
timeUnit.Group={}--文本列表单位,对应正当格式但不用阿拉伯数字描述的单位,并且允许使用不同的区间值
function timeUnit.Group.createIndex(t)
local a = {}
for n in pairs(t) do
a[#a+1] = n
end
table.sort(a)
return a
end
timeUnit.Group.analysis=timeUnit.List.analysis
function timeUnit.Group:formatText(value)--返回格式化文本)
for index,v in ipairs(self.index) do
if v>value then
if self.index[index-1] then
return self.names[self.index[index-1]]
else
return ""
end
end
end
return self.names[self.index[#self.index]] or ""
end
function timeUnit:appendGroup(args)
local unit=self:append()
unit.names=args
unit.index=timeUnit.Group.createIndex(args)
unit.analysis=timeUnit.Group.analysis
unit.formatText=timeUnit.Group.formatText
return unit
end
--时间精度
--精度有两方面,未知的前缀与未知的后缀。
local timeAccuracy={
max=1,--最前精度
maxA=1,
min=7,--最后精度
minA=math.huge,
}
timeAccuracy.baseData={["year"]=7,["month"]=6,["week"]=5,["yday"]=4,["day"]=4,["wday"]=4,["hour"]=3,["min"]=2,["sec"]=1}
timeAccuracy.indexToTag={"sec","min","hour","day","week","month","year"}
function timeAccuracy.create(args)
return setmetatable(args or {},timeAccuracy)
end
timeAccuracy.__index=timeAccuracy
timeAccuracy.clear=timeAccuracy.create{max=1,maxA=math.huge,min=7,minA=1}
function timeAccuracy.lt(l,la,r,ra)--小于
return l<r or (l==r and la<ra)
end
function timeAccuracy.le(l,la,r,ra)--小于等于
return l<r or (l==r and la<=ra)
end
function timeAccuracy:setMin(min,minA)
self.min=min
self.minA=minA
end
function timeAccuracy:setMax(max,maxA)
self.max=max
self.maxA=maxA
end
function timeAccuracy:isNull()
return timeAccuracy.le(self.max,self.maxA,self.min,self.minA)
end
function timeAccuracy:limit(tag,min,max)--限制、精确范围
local index=timeAccuracy.baseData[tag]
max=max or min
if timeAccuracy.lt(self.max,self.maxA,index,max) then
self:setMax(index,max)
end
if timeAccuracy.lt(index,min,self.min,self.minA) then
self:setMin(index,min)
end--两者间的空间为已知精确
end
function timeAccuracy:_cover(index,value,direct)--遮盖、模糊范围
if direct then--true为遮蔽右侧
if timeAccuracy.lt(self.min,self.minA,index,value) then
if value==math.huge then--进位
self:setMin(index+1,1)
else
self:setMin(index,value)
end
end
else
if timeAccuracy.lt(index,value,self.max,self.maxA) then
if value==1 then--进位
self:setMax(index-1,math.huge)
else
self:setMax(index,value)
end
end
end
end
function timeAccuracy:cover(tag,value,direct)--遮盖、模糊范围
local index=timeAccuracy.baseData[tag]
self:_cover(index,value,direct)
end
function timeAccuracy:coverBy(other,direct)--遮盖、模糊范围
if direct then--true为遮蔽右侧
self:_cover(other.max,other.maxA,direct)
else
self:_cover(other.min,other.minA,direct)
end
end
function timeAccuracy:loverThan(tag,min,max)--是否比指定标签下的精度更低--即数据是否没有涵盖该精度
local index=timeAccuracy.baseData[tag]
max=max or min
return timeAccuracy.lt(self.max,self.maxA,index,max) or timeAccuracy.lt(index,min,self.min,self.minA)
end
function timeAccuracy:getTagAccuracy(tag)--获取tag下精度,两个数。必须是范围包含的标签
local index=timeAccuracy.baseData[tag]
if self.max<index or index<self.min then
return
end
local first=self.max==index and self.maxA or math.huge
local last=self.min==index and self.minA or 1
return first,last
end
function timeAccuracy:contains(tag,accuracy)--包含精度
return not self:loverThan(tag,accuracy)
end
function timeAccuracy:getMax()--获取最前精度
return timeAccuracy.indexToTag[self.max],self.maxA
end
function timeAccuracy:getMin()--获取最后精度
return timeAccuracy.indexToTag[self.min],self.minA,self.begin
end
function timeAccuracy.getTags_step(self,index)
index=index-1
if (index<self.min) then--超过界限
return
end
if index==self.min then
local min=self.minA
end
if index==self.max then
local max=self.maxA
end
return index,timeAccuracy.indexToTag[index],min or 1,max or math.huge
end
function timeAccuracy:getTags()--遍历标签精度,从大到小
return timeAccuracy.getTags_step,self,self.max+1
end
function timeAccuracy.getSupplyPreTags_step(tab,index)
index=index+1
if index>tab[2].max then--超过界限
return
end
if index==tab[2].max then
if index==tab[1].max and tab[1].maxA>=tab[2].maxA then
return
end
return index,timeAccuracy.indexToTag[index],tab[2].maxA
end
return index,timeAccuracy.indexToTag[index],math.huge
end
function timeAccuracy:getSupplyPre(other)--获取other的更高精度的标签、精度 迭代器
return timeAccuracy.getSupplyPreTags_step,{self,other},math.max(self.max,other.min)-1
end
function timeAccuracy.getSupplyAfterTags_step(tab,index)
index=index-1
if (index<tab[2].min) then--超过界限
return
end
if index==tab[2].min then
if index==tab[1].min and tab[1].minA<=tab[2].minA then
return
end
return index,timeAccuracy.indexToTag[index],tab[2].minA
end
return index,timeAccuracy.indexToTag[index],1
end
function timeAccuracy:getSupplyAfter(other)--获取other的更高精度的标签、精度 迭代器
return timeAccuracy.getSupplyAfterTags_step,{self,other},math.min(self.min,other.max)+1
end
--time 时间类
local time={year=1,month=1,week=1,day=1,wday=1,hour=0,min=0,sec=0}
time.zero=os.time(time)
function time.create(args)--设置值
args=args or {}
args.accuracy=timeAccuracy.create()
return setmetatable(args,time)
end
time.__index=time
function time:setEnd(time)--结束时间点
time:supply(self,true,false)
time.accuracy=self.accuracy
self._end=time
end
function time:getAccuracyEnd()--模糊度的范围结束时间点
local tag,range=self.accuracy:getMin()
--mw.log(tag,range)
local e=mw.clone(self)
e[tag]=e[tag]+range
e:update()
return e
end
function time:getEnd()--结束范围并结束模糊度的范围时间点
if not self._accuracy_end then
self._accuracy_end=(self._end or self):getAccuracyEnd()
end
return self._accuracy_end
end
function time.initializeUnit(lang_from,lang_to,connect)--初始化
local base={--单位有优先度--占用同一标签的单位按照优先度予以保留--优先度从低到高都可运算ymd,新的ymd覆盖旧的。
--进行time创建时将优先度从高到低依次调用构建,单位的会因精确已达到而跳过赋值
century =timeUnit.base("year",0) :accuracy(100,math.huge) :b(0):ratio(100):move(-199),
years =timeUnit.base("year",1) :accuracy(10,math.huge) :b(0),
year =timeUnit.base("year",2) :accuracy(1,math.huge) :b(1),
month =timeUnit.base("month",3) :accuracy(1,math.huge) :b(1),
week =timeUnit.base("week",4) :accuracy(1,math.huge) :b(1),
wday =timeUnit.base("wday",5) :accuracy(1,math.huge) :b(1),
yday =timeUnit.base("yday",5.5) :accuracy(1,math.huge):na() :b(1),
day =timeUnit.base("day",6) :accuracy(1,math.huge) :b(1),
mer =timeUnit.base("hour",7) :accuracy(12,math.huge) :b(0):ratio(12),
hour =timeUnit.base("hour",8) :accuracy(1,math.huge) :b(0),
min =timeUnit.base("min",9) :accuracy(1,math.huge) :b(0),
sec =timeUnit.base("sec",10) :accuracy(1,math.huge) :b(0),
--timezone=timeUnit.base("timezone",11),--暂时无意义
}
local standard={--数字-符号格式。应最后分析。
--数字识别。4位将识别为年,3位为日
timeUnit .multi{base.year,base.month,base.day}--ymd
:pattern("^(%d+)\-(%d+)\-(%d+)","%04d\-%02d\-%02d")--正规模式
:des("日期-年月日 (ISO 8601格式)(不支持省略)")
:f("%E"),
timeUnit .multi{base.year,base.month,base.day}--ymd
:pattern("^(%d+)%.(%d+)%.(%d+)","%04d\.%02d\.%02d")--泛用模式
:des("日期-年月日 (ISO 8601格式)(不支持省略)")
:f("%(Eo)"),
timeUnit .multi{base.year,base.month}--ym
:pattern("^(%d%d%d%d+)\-(%d+)"),
timeUnit .multi{base.month,base.day}--md
:pattern("^(%d%d-)\-(%d+)"),
timeUnit .multi{base.year,base.month,base.day}--ymd2
:pattern("^(%d%d%d%d)\-?(%d%d)\-?(%d%d)"),--数字模式
timeUnit .multi{base.year,base.week,base.wday}--ywd
:pattern("^(%d+)\-W(%d+)\-(%d+)","%04d\-W%02d\-%d")--正规模式
:des("日期-年周星期 (ISO 8601格式)(不支持省略)")
:f("%(Ew)"),
timeUnit .multi{base.year,base.week}--yw
:pattern("^(%d%d%d%d+)\-?W(%d%d-)"),
timeUnit .multi{base.week,base.wday}--wd
:pattern("^W(%d%d-)\-?(%d)"),
timeUnit .multi{base.year,base.week,base.wday}--ywd2
:pattern("^(%d%d%d%d)\-?W(%d%d)\-?(%d)"),--数字模式
timeUnit .multi{base.hour,base.min,base.sec}--his
:pattern("^(%d+)\:(%d+)\:(%d+)","%02d\:%02d\:%02d")--正规模式
:des("时间-时分秒 (ISO 8601格式)(不支持省略)")
:f("%e"),
timeUnit .multi{base.hour,base.min}:setName("--hi")
:pattern("^(%d+)\:(%d+)"),--可能需要扩展
timeUnit .multi{base.hour,base.min,base.sec}--his2
:pattern("^T(%d%d)\:?(%d%d)\:?(%d%d)"),
base.year :appendNumber()--四位纯数字--yearF
:pattern("^(%d%d%d%d)"),
base.mer :appendList{[0]="am","pm"}--mer
:des("根据时间是上午还是下午,输出am或pm")
:f("%a"),
base.mer :appendList{[0]="AM","PM"}--meridiem
:des("根据时间是上午还是下午,输出AM或PM")
:f("%(au)"),
timeUnit .multi{base.hour,base.min}--timezone
:priority(11)
:pattern("^([-%+]%d+):?(%d+)")
:des("时区"),--:f("%(au)")
}
local expend={--只用于格式化
base.century:appendNumber()--centuryF
:pattern("^(%d%d)")
:des("日期-世纪,2位数字,补足前导0")
:fill(2)
:f("%(cf)"),
base.years :appendNumber()--yearsF
:pattern("^(%d%d)")
:des("日期-年代,2位数字,补足前导0")
:fill(2)
:accuracy(10,100)
:f("%(tf)"),
base.year :appendNumber()--四位纯数字--yearF
:pattern("^(%d%d%d%d)")
:fill(4)
:des("日期-年份,4位数字,补足前导0")
:f("%(yf)"),
base.month :appendNumber()--monthF
:pattern("^(%d%d)")
:des("日期-月,2位数字,补足前导0")
:fill(2)
:f("%(mf)"),
base.day :appendNumber()--dayF
:pattern("^(%d%d)")
:des("日期-日,2位数字,补足前导0")
:fill(2)
:f("%(df)"),
base.week :appendNumber()--获得的W开头两位数字识别为周--weekF
:pattern("^W(%d%d)")
:des("日期-年内第几周,2位数字,补足前导0")
:fill(2)
:f("%(wf)"),
base.hour :appendNumber()--hourF
:pattern("^(%d%d)")
:des("时间-小时,2位数字,补足前导0")
:fill(2)
:f("%(hf)"),
base.min :appendNumber()--minF
:pattern("^(%d%d)")
:des("时间-分钟,2位数字,补足前导0")
:fill(2)
:f("%(if)"),
base.sec :appendNumber()--secF
:pattern("^(%d%d)")
:des("时间-秒,2位数字,补足前导0")
:fill(2)
:f("%(sf)"),
base.century:appendNumber()--century
:des("日期-世纪")
:f("%c"),
base.years :appendNumber()--years
:des("日期-年代")
:f("%t"),
base.year :appendNumber()--year
:des("日期-年份")
:f("%y"),
base.month :appendNumber()--month
:des("日期-月")
:f("%m"),
base.week :appendNumber()--week
:des("日期-年内第几周")
:f("%w"),
base.day :appendNumber()--day
:des("日期-日")
:f("%d"),
base.yday :appendNumber()--yday
:des("日期-日 年内第几日")
:f("%(dy)"),
base.wday :appendNumber()--已有周,获得的一位数字识别为星期--wday
:pattern("^(%d)")
:des("日期-星期,1位数字")
:f("%l"),
base.hour :appendNumber()--hour
:des("时间-小时")
:f("%h"),
base.hour :appendNumber()--hour
:des("时间-小时 十二小时制")
:accuracy(1,12)
:b(0)
:f("%(hy)"),
base.min :appendNumber()--min
:des("时间-分钟")
:f("%i"),
base.sec :appendNumber()--sec
:des("时间-秒")
:f("%s"),
timeUnit .multi{base.year,base.month,base.day,base.hour,base.min,base.sec}--ios
:pattern("^%d%d%d%d-?%d%d-%d%d T%%d%d:?%d%d:?%d%d","%04d-%02d-%02d T%02d:%02d:%02d")
:des("年月日时分秒 (ISO 8601格式)(不支持省略)")
:f("%X"),
timeUnit .multi{base.year,base.week,base.day,base.hour,base.min,base.sec}--ios2
:pattern("%d%d%d%d-?W%d%d-?%d%d T%%d%d:?%d%d:?%d%d","%04dW%02d-%d T%02d:%02d:%02d")
:des("年周日时分秒 (ISO 8601格式)(不支持省略)")
:f("%x"),
}
local lang={}
lang.en={--英语格式
base.years :appendSuffix("s")--years
:accuracy(10,math.huge)
:f("%T"),
base.month :appendList{"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}--monthShort
:f("%(Ms)"),
base.month :appendList{"January","February","March","April","May","June","July","August","September","October","November","December"}
:f("%M"),--month =
base.wday :appendList{"Mon","Tue","Wed","Thu","Fri","Sat","Sun"}--wdayShort
:f("%(Ls)"),
base.wday :appendList{"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"}--wday
:f("%L"),
base.day :appendSuffix("nd")--day
:formatFuc(
function(self,value)
return value==1 and value.."st" or value.."nd"
end)
:f("%D"),
base.day :appendSuffix("st"),--day2
}
lang.zh={--中文格式
base.century:appendSuffix("世纪")--century
:des("日期-世纪 单位“世纪”")
:f("%C"),
base.years :appendSuffix("年代")--yearsF
:pattern("^%d%d年代")
:des("日期-年代 单位“年代”,只取后两位")
:accuracy(10,100)
:f("%(Tf)"),
base.years :appendSuffix("年代")--years
:des("日期-年代 单位“年代”")
:accuracy(10,math.huge)
:f("%T"),
base.year :appendSuffix("年")--year
:des("日期-年 单位“年”")
:f("%Y"),
base.month :appendSuffix("月")--month
:des("日期-月 单位“月”")
:f("%M"),
base.month :appendList{"一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"}
:des("日期-月 中文文字")--monthC
:f("%(Mc)"),
base.week :appendSuffix("周")--weekN =
:pattern("^第d%+周","第d%周"),
base.week :appendSuffix("周")--week
:des("日期-周 单位“周”")
:f("%W"),
base.day :appendSuffix("日")--day
:des("日期-日 单位“日”")
:f("%D"),
base.yday :appendSuffix("日")--yday
:des("日期-日 年内第几日 单位“日”")
:na()
:f("%(Dy)"),
base.day :appendSuffix("号")--dayO
:des("日期-日 单位“号”")
:f("%(Do)"),
base.wday :appendList{"星期一","星期二","星期三","星期四","星期五","星期六","星期日"}--wday
:des("日期-星期 中文文字“星期”")
:f("%L"),
base.wday :appendList{"周一","周二","周三","周四","周五","周六","周日"}--wdaySS
:des("日期-星期 中文文字大写“周”")
:f("%(Lss)"),
base.wday :appendList{"一","二","三","四","五","六","日"}--wdayS
:des("日期-星期 只输出一个中文文字")
:f("%(Ls)"),
base.hour :appendSuffix("时")--hour
:des("时间-时 单位“时”")
:f("%H"),
base.hour :appendSuffix("时")--hour
:des("时间-时 单位“时” 十二小时制")
:accuracy(1,12)
:na()
:f("%(Hy)"),
base.hour :appendSuffix("点")--hourO
:des("时间-时 单位“点”")
:f("%(Ho)"),
base.min :appendSuffix("分")--min
:des("时间-分 单位“分”")
:f("%I"),
base.sec :appendSuffix("秒")--sec
:des("时间-秒 单位“秒”")
:f("%S"),
--wday =base.week :appendSuffix("星期","^星期d%+","星期d%"),
--shortMonth =base.month:appendList("一","二","三","四","五","六","七","八","九","十","十一","十二"),
base.day :appendGroup{
[1]="上旬",[11]="中旬",[21]="下旬"
} :priority(3.5)--tendays
:accuracy(10,math.huge)
:b(1)
:des("日期-月内描述“旬”,十日为一旬")
:f("%(Mi)"),
base.mer :appendList{[0]="上午","下午"}--mer
:des("时间-根据时间输出上午或下午")
:f("%A"),
base.hour :appendGroup{
[0]="凌晨",[7]="上午",[11]="中午",[13]="下午",[17]="傍晚",[19]="晚上",[22]="深夜"--有争议
} :priority(7.5)--inday =
:des("时间-日内详细描述")
:f("%(Di)"),
}
local filter=function(tab)
local t={}
for _,unit in ipairs(tab) do
if unit:available() then
table.insert(t,unit)
end
end
return t
end
time.analysisTable={}--分析表--本来是可以写成语言,树状分析,但是暂且看来并没有必要。为了兼容更多模糊写法。
if lang_from=="all" then
for _,l in pairs(lang) do
table.insert(time.analysisTable,filter(l))
end
else--lang=="zh"
table.insert(time.analysisTable,filter(lang[lang_from or "zh"]))
end
table.insert(time.analysisTable,filter(standard))
time.analysisConnect={--时间连接符,间隔两时间点
"^到","^至","^\-","^\b","^\\","^、"
}
time.analysisIgnore={--忽略符、间隔符。
"^%s","^ ","^,","^—"
}
time.formatTable={lang[lang_to or "zh"],expend,standard}--格式化表
if connect then
time.formatConnect=connect
else
if lang_to=="en" then
time.formatConnect="-"
else
time.formatConnect="到"
end
end
--追加词,暂未实现--追加在单位后的后缀,可以不断追加。后缀与原单位相关。
local about={
--about =base.hour :appendFixed("左右"),
--about2 =base.hour :appendFixed("前后"),
--about2 =base.hour :appendFixed("约"),
--about2 =base.hour :appendFixed("大概"),
--about2 =base.hour :appendFixed("之间"),
--about2 =base.hour :appendFixed("开始"),
--about2 =base.hour :appendFixed("附近"),
}
time.formatTrie = string.buildTrie(time.formatTable)
return standard,expend,lang
end
--功能函数
function time:isEmpty()--是否为空
return self.accuracy:isNull()
end
function time:_setValue(tag,value,min,max,begin)
--mw.log("set",tag,value,min,max,begin,self:_getValue(tag))
local f,l=self.accuracy:getTagAccuracy(tag)
value=value-begin
--value=math.mod(value,max)-math.mod(value,min)
value=value-math.mod(value,min)
if f then
local valueTag=self[tag]-begin
value=value-math.mod(value,f)+math.mod(value,l)+math.mod(valueTag,f)-math.mod(valueTag,l)--保留值-保留信息已有精度部分内容
--mw.log(tag,min,max,begin,value,value,math.mod(value,f),math.mod(value,l),math.mod(valueTag,f),math.mod(valueTag,l))
end
value=value+begin
self[tag]=value--+math.mod(begin,min)
--mw.log("set","result",tag,self[tag])
end
function time:forceSetValue(tag,value,min,max,begin)--强制设置值--内部函数
self:_setValue(tag,value,min,max,begin)
self.accuracy:limit(tag,min,max)
end
function time:setValue(tag,value,min,max,begin)--设置值--内部函数
if self.accuracy:loverThan(tag,min,max) then
self:forceSetValue(tag,value,min,max,begin or 0)
end
end
function time:_getValue(tag)--实际记录值
local f,l=self.accuracy:getTagAccuracy(tag)
if f then
return self[tag]
end
return time[tag]
end
function time:forceGetValue(tag,min,max,begin)--强制获取值
local value=self[tag]-begin
local f,l=self.accuracy:getTagAccuracy(tag)
if f then
max=math.min(f,max)
min=math.max(l,min)
end
--mw.log("-->",tag,self[tag],max,min,math.mod(value,max)-math.mod(value,min))
return math.mod(value,max)-math.mod(value,min)+begin--+self.accuracy:getTagBegin(tag)
end
function time:getValue(tag,min,max,begin)--获取值
min=min or 1
max=max or math.huge
if self.accuracy:contains(tag,min,max) then
return self:forceGetValue(tag,min,max,begin)
end
end
function time:getValueIgnore(tag,min,max,begin)--获取值--忽略粗略精度版本
min=min or 1
max=max or math.huge
local f,l=self.accuracy:getTagAccuracy(tag)
if f~=max or l~=min then
return nil
end
return self:forceGetValue(tag,min,max,begin)
end
function time:update()--指示需要更新
self.value=nil
self._accuracy_end=nil
self._sorted=false
end
function time:_getNumber()
if not self.value then--改动后清空
local t={}
for _,tag in ipairs(time.keyData) do
t[tag]=self:_getValue(tag) or time[tag]
end
if not rawget(self,"month") and not rawget(self,"day") then--处理周问题
local week=self:_getValue("week")
if week then
local wday=os.date("*t",os.time({year=self.year,month=1,day=1})).wday-1--年内第一天的星期 减去1是因为lua标准中1代表星期日
if wday==0 then wday=7 end--ios标准
t.day= week*7+(wday>4 and 0 or -7)+self.wday-wday+1
end
end
self.value=os.time(t)
end
return self.value
end
function time:getNumber()--获取计算值
return self:_getNumber(),self:getEnd():_getNumber()
end
time.compare={--时间比较结果常数
before=1,--完全在之前
equal=2,--相同
after=3,--完全在之后
include=4,--包含
inside=5,--被包含
conflict=6,--冲突
}
time.compare=setmetatable(time.compare,{--时间比较,理论上只允许发生在同精确度的时间上。
__call=function(self,time1,time2)
local b1,e1=time1:getNumber()
local b2,e2=time2:getNumber()
--mw.log(tostring(time1),tostring(time2))
--mw.log(b1,e1,b2,e2)
if e1<=b2 then return self.before end
if e2<=b1 then return self.after end
if b1==b2 and e1==e2 then return self.equal end
if b1<=b2 then
if e2<=e1 then return self.include end
end
if b2<=b1 then
if e1<=e2 then return self.inside end
end
return self.conflict
end
})
function time:__eq(other)
return self:compare(other)==time.compare.equal
end
function time:__lt(other)
return self:compare(other)==time.compare.before
end
time.keyData={"year","month","day","hour","min","sec"}
time.baseData={"year","month","week","wday","yday","day","wday","hour","min","sec"}
function time:supply(other,pre,after)--利用另一个时间对此时间精确度补足
pre=pre==nil and true or pre
after=after==nil and true or after
if pre then
for _,tag,accuracy in self.accuracy:getSupplyPre(other.accuracy) do
self:_setValue(tag,other:_getValue(tag),1,accuracy,0)
end
if self._end then
for _,tag,accuracy in self.accuracy:getSupplyPre(other.accuracy) do
self._end:_setValue(tag,other:_getValue(tag),1,accuracy,0)
end
end
self.accuracy:limit(other.accuracy:getMax())
end
if after then
for _,tag,accuracy in self.accuracy:getSupplyAfter(other.accuracy) do
self:_setValue(tag,other:_getValue(tag),accuracy,math.huge,0)
end
if self._end then
for _,tag,accuracy in self.accuracy:getSupplyAfter(other.accuracy) do
self._end:_setValue(tag,other:_getValue(tag),accuracy,math.huge,0)
end
end
local tag,min=other.accuracy:getMin()
self.accuracy:limit(tag,min,min)
end
self:update()
return self
end
function time:cover(other,direct)--利用另一个时间对此时间精确度减损遮盖
--direct指示遮蔽方向,true遮蔽后部精度,false遮蔽前部精度
self.accuracy:coverBy(other.accuracy,direct)
self:update()
return self
end
function time:coverRange(other)--遮盖other中到_end前后一致部分单位
local tag,min
for _,t,i in other.accuracy:getTags() do
if other._end and other._end[t]~=other[t] then
break
end
tag=t
min=i
end
if tag then
self.accuracy:cover(tag,min,false)
else
tag,min=other.accuracy:getMax()
self.accuracy:cover(tag,min,false)
end
--mw.log("tag",tag,min)
self:update()
return self
end
function time:coverUnit(unitTag,direct)--利用unit格式索引unit并以此进行遮盖 也可以认为是截取信息,截取从unit开始的信息--种种问题暂且只支持主要单位
local unit
for _,group in ipairs(self.formatTable) do
for _,u in ipairs(group) do
if u.format==unitTag then
unit=u
break
end
end
end
if unit then
--mw.log(self.accuracy.min,self.accuracy.minA,self.accuracy.max,self.accuracy.maxA)
self.accuracy:cover(unit.tag,direct and unit._min or unit._max,direct)--有点不规范
--mw.log(self.accuracy.min,self.accuracy.minA,self.accuracy.max,self.accuracy.maxA)
self:update()
end
return self
end
function time:_add(tag,value)
t[tag]=t[tag]+value
if self._end then
self._end[tag]=self._end[tag]+value
end
end
function time:add(tag,value)--时间位移--暂且只有在具有该精确值时才生效
self._add(tag,value)
self:update()
end
function time:getMin()--获取最小精确标签
return self.accuracy:getMin()
end
function time:forceAccuracy()--强制模糊度
self.accuracy=timeAccuracy.clear
end
function time:__connect(other)--连接时间,取前者到后者产生区间。模糊处使用前者补足后者--关于模糊前
local b=mw.clone(self)
local e=mw.clone(other)
b:setEnd(e)
b:update()
return b
end
function time:__add(other)
local b=mw.clone(self)
for _,tag in ipairs(time.baseData) do
local value=other:getValue(tag)
if value then
local v=self:getValue(tag)
b:_add(tag,v)
end
end
b:update()
return b
end
function time:__sub(other)
local b=mw.clone(self)
for _,tag in ipairs(time.baseData) do
local value=other:getValue(tag)
if value then
local v=self:getValue(tag)
b:_add(tag,-v)
end
end
b:update()
return b
end
function time.currentTimeZone()
if not time._currentTimeZone then
local a = os.date('!*t',os.time())--中时区的时间
local b = os.date('*t',os.time())
time._currentTimeZone= os.difftime(os.time(b), os.time(a))
end
return time._currentTimeZone
end
function time.normalizeZone(timezone)
if timezone == nil or #timezone == 0 then
return currentTimeZone() --用户当前时区
else
local hour, min = mw.ustring.match(mw.text.trim(timezone), "^([-%+]?%d+):?(%d*)$")
if hour == nil then
error("时区参数格式不正确。")
end
return (tonumber(hour)*60+tonumber(min))*60
end
end
function time:convert(o_timezone,c_timezone)--改变时区
local ot=time.normalizeZone(o_timezone)
local ct=time.normalizeZone(c_timezone or mw.getCurrentFrame():callParserFunction( "#var","timezone"))
self:add("sec",ct-ot)
end
function time:setData(t)--设置数据
for _,tag in ipairs(time.baseData) do
self[tag]=t[tag]
end
end
function time:sort()--规范数据
if self._sorted then
return self
end
self.value=self:_getNumber()
local t=os.date("*t",self.value)
t.wday=t.wday-1
if t.wday==0 then t.wday=7 end--ios标准
self:setData(t)
local wday=math.mod(self.wday-self.yday,7)
wday=wday>0 and wday or 7+wday--不包括0
self.week=math.floor((self.yday+1-self.wday)/7)+(wday>4 and -1 or 0)+2
if self._end then
self._end:sort()
end
self._sorted=true
return self
end
--文本转换
function time.sortUnits(uv1,uv2)
return uv1.unit:priorityCompare(uv2.unit)
end
function time.analysis_step(text,index)
local value
for n1,group in ipairs(time.analysisTable) do
for n2,unit in ipairs(group) do
--mw.log("name",n1,n2,text:sub(index),unit.patternAnalysis)
value,index=unit:analysis(time,text,index)
if value then--!需要追加模糊词的处理
--mw.log("analysis!")
return unit,value,index
end
end
end
return nil,nil,index
end
function time.analysisSp(analysis_pattern)
local value
local trie=time.formatTrie
local function fuc(text,index)
local time1=time.create()
local ni=string.matchTrie(analysis_pattern,trie,text:sub(index),time1)
return time1,index+ni-1
end
return fuc
end
function time.analysis_simple(text,index)--处理不含连接符的时间文本
local unitvalue={}
local unit,value
repeat
repeat--trim
local b,e=string.findAny(text,time.analysisIgnore,index)--连接符处理,可能需要多段支持
if b then
index=e+1
end
until(not b)
--mw.log("analysis!",text:sub(index))
unit,value,index= time.analysis_step(text,index)
if unit then
table.insert(unitvalue,{unit=unit,value=value})
end
until(unit==nil or index>#text)
table.sort(unitvalue,time.sortUnits)--必须保证信息是连成段的,不允许空隙
local time1=time.create()
for _,uv in ipairs(unitvalue) do
uv.unit:supplyTime(time1,uv.value)
end
return time1,index
end
function time.analysis(text,analysis_pattern)--分析文本
local analysis=analysis_pattern and time.analysisSp(analysis_pattern) or time.analysis_simple
local time1,index=analysis(text,1,analysis_pattern)
local time2
repeat
local b,e=string.findAny(text,time.analysisConnect,index)--连接符处理,可能需要多段支持
if b then
time2,index=analysis(text,e+1,analysis_pattern)
end
until(not b)
if time2 then
--error(table.concat({time2.year,time2.month,time2.day},";"))
time1:setEnd(time2)
--error(table.concat({time1._end.year,time1._end.month,time1._end.day},";"))
end
return time1,index
end
function time:format(pattern,ignore)--格式化--使用匹配符,指示忽略粗略描述
self:sort()
if self._end then
self._end:sort()
end
local getvalue,accuracy
if ignore then
getvalue=time.getValue
time.getValue=time.getValueIgnore
elseif ignore==false then
getvalue=time.getValue
time.getValue=time.forceGetValue
end
local text=string.replaceTrie(pattern,time.formatTrie,self)
if self._end then
if self._end:_getNumber()~=self:_getNumber() then
text=text..self.formatConnect..string.replaceTrie(pattern,time.formatTrie,self._end)
end
end
if getvalue then
time.getValue=getvalue
end
return text--range问题
end
function time:__tostring()
return self:format("%Y%M%D%H%I%S")
end
--主函数
module.time=time--用于继承等
function module.formatList(frame)--参数表
local standard,expend,lang=module.initialize()
local var_array = require("Module:Var-array")
local formatNames = {}
local formatList = {}
table.insert(formatNames,"")
local list = {}
for _, unit in pairs(standard) do
if unit.format then
table.insert(list, { unit.format, unit.description })
end
end
for _, unit in pairs(expend) do
if unit.format then
table.insert(list, { unit.format, unit.description })
end
end
table.insert(formatList,list)
for name, l in pairs(lang) do
table.insert(formatNames, name)
local list = {}
for _, unit in pairs(l) do
if unit.format then
table.insert(list, { unit.format, unit.description })
end
end
table.insert(formatList,list)
end
var_array.new("time.convert.formatlist.name", formatNames)
var_array.new("time.convert.formatlist", formatList)
end
function module.toTime(text,analysis_pattern)--获取时间类
return time.analysis(text or "",analysis_pattern)
end
function module.initialize(from,to,connect)--设置参数初始化
return time.initializeUnit(from,to,connect)
end
function module.format(text,pattern,ignore,o_timezone,c_timezone,analysis_pattern)--格式化文本
assert(text,"text cannot be nil")
assert(pattern,"pattern cannot be nil")
local time=time.analysis(text,analysis_pattern)
if o_timezone then
time=time:convert(o_timezone,c_timezone)
end
return time:format(pattern,ignore)
end
function module._main(args)--主函数
module.initialize(args.from or args["原语言"],args.to or args["目标语言"],args.connect or args["连接符"])
local text=args[1] or args.text or args["时间文本"]
local pattern=args[2] or args.pattern or args["格式化字符串"]
local ignore=args[3] or args.force or args["强制精确度"]
if ignore=="true" then
ignore=true
elseif ignore=="false" then
ignore=false
else
ignore=nil
end
local o_timezone=args[4] or args.o_timezone or args.o_tz or args["原时区"]
local c_timezone=args.c_timezone or args.c_tz or args["现时区"]
if not pattern then return "" end
local analysis_pattern=args[5] or args.analysis_pattern or args.a_pattern or args["分析字符串"]
return module.format(text,pattern,ignore,o_timezone,c_timezone,analysis_pattern)
end
function module.main(frame)--主函数
local args = getArgs(frame)
return module._main(args)
end
return module