Module:模糊时间
模糊时间模块用于匹配获取一个时间文本,进而,可以进行时间计算、时间比较、时区调整、依据给定的格式进行格式化。 与其他时间函数不同之处在于,本模块支持不规范的、模糊描述以及时间段描述的文本,并允许在格式化时省略精确度不足的内容。
函数
initialize(from,to,connect)
初始化设置。
from
为文本语言,可选zh
/en
/all
,默认为zh
。to
为格式化目标语言,可选zh
/en
,默认为zh
。- connect为时间连接符,指格式化时使用时间段概念时连接两时间点的文本。默认为
到
。
toTime(text,analysis_pattern)
将文本解析转换为time表
。time表
具有os.time
可以识别的结构。
- 能识别ISO 8601格式的时间,如
2024-11-21T21:25:09+00:00
。但暂不识别追加的时区文本。 - 能识别带有单位的文本,如
2012年
。支持乱序。 - 能识别特定的时间描述,如
星期五
凌晨
。 - 字符
到
至
-
\
被视为时间段的描述关键字。 - 字符
%s
中文空格,
被忽略。
基本可以认为,所有该函数可以输出的格式化格式,都可以被其识别。
如果指定analysis_pattern
,则将使用固定的格式进行匹配。对于固定格式的文本,将大大节省解析时间。其中格式匹配符与#占位符标准相同。没有指定该参数的场合,函数将试图使用所有可能的格式进行分析匹配。
time表的属性
year
,month
,day
,week
,wday
,yday
,hour
,minute
,second
。
time表的函数
time表可以执行以下函数:
运算符
time:__connect(other)
即..
。连接两时间,获得时间段。
time:__add(other)
即+
。时间加法。
time:__sub(other)
即-
。时间减法。
其他函数
time:add(tag,value)
按标签计算。标签可以为属性其一。暂且唯有原time中具有该精确度的标签才有效。
time:convert(o_timezone,c_timezone)
时区转换。
c_timezone
默认为当前时区。o_timezone
默认为{{TimeZone}}所定义的时区。
time:supply(other,pre,after)
补足精确度。使用另一时间对其补足缺失的精确度以及值。已有部分则忽略。
pre
为true则补足前部缺失值(如年、月),after
设置为true则补足后部缺失值(如秒、分)。两参数默认为true。
time:getNumber()
获取时间段前后的对应os.time
数值。
time:sort()
规范数据,计算后如果需要从属性获得准确值需调用。在格式化前会自动调用。
time:format(pattern,ignore)
格式化为文本。
ignore
如果为true
,则在非必要时忽略区域性描述(诸如旬)(如果精确度只达到该描述,则不省略)。如果为false
,则强制要求所有精确度都输出值。ignore
默认为nil
,此时正常输出,忽略不具备的精确度。
其中pattern
的占位符标准详见#占位符。
format(text,pattern,ignore,o_timezone,c_timezone,a_pattern)
格式化输出。这个函数是功能的集合版本。
text
为时间文本ignore
同上。pattern
的占位符标准详见#格式化。o_timezone
原时区,即文本使用时区c_timezone
现时区,即显示时区。默认采用{{timezone}}模板定义的时区。a_pattern
,分析用字符串。
格式化
格式化使用到
作为时间段描述方式(或许可以考虑增加到占位符中)。
格式化使用的替换用占位符如下。
待扩展中文数字表达。
示例时间:2024年11月21日21时25分9秒
类型 | 格式化参数 | 说明 | 输出
|
---|---|---|---|
%E | 日期-年月日 (ISO 8601格式)(不支持省略) | 2024-11-21
| |
%(Eo) | 日期-年月日 (ISO 8601格式)(不支持省略) | 2024.11.21 | |
%(Ew) | 日期-年周星期 (ISO 8601格式)(不支持省略) | 2024-W47-4 | |
%e | 时间-时分秒 (ISO 8601格式)(不支持省略) | 21:25:09 | |
%a | 根据时间是上午还是下午,输出am或pm | pm | |
%(au) | 根据时间是上午还是下午,输出AM或PM | PM | |
%(cf) | 日期-世纪,2位数字,补足前导0 | 21 | |
%(tf) | 日期-年代,2位数字,补足前导0 | 20 | |
%(yf) | 日期-年份,4位数字,补足前导0 | 2024 | |
%(mf) | 日期-月,2位数字,补足前导0 | 11 | |
%(df) | 日期-日,2位数字,补足前导0 | 21 | |
%(wf) | 日期-年内第几周,2位数字,补足前导0 | 47 | |
%(hf) | 时间-小时,2位数字,补足前导0 | 21 | |
%(if) | 时间-分钟,2位数字,补足前导0 | 25 | |
%(sf) | 时间-秒,2位数字,补足前导0 | 09 | |
%c | 日期-世纪 | 21 | |
%t | 日期-年代 | 2020 | |
%y | 日期-年份 | 2024 | |
%m | 日期-月 | 11 | |
%w | 日期-年内第几周 | 47 | |
%d | 日期-日 | 21 | |
%(dy) | 日期-日 年内第几日 | 326 | |
%l | 日期-星期,1位数字 | 4 | |
%h | 时间-小时 | 21 | |
%(hy) | 时间-小时 十二小时制 | 9 | |
%i | 时间-分钟 | 25 | |
%s | 时间-秒 | 9 | |
%X | 年月日时分秒 (ISO 8601格式)(不支持省略) | 2024-11-21 T21:25:09 | |
%x | 年周日时分秒 (ISO 8601格式)(不支持省略) | 2024W47-21 T21:25:09 | |
en | %T | 2020s
| |
%(Ms) | Nov | ||
%M | November | ||
%(Ls) | Thu | ||
%L | Thursday | ||
%D | 21nd | ||
zh | %C | 日期-世纪 单位“世纪” | 21世纪
|
%(Tf) | 日期-年代 单位“年代”,只取后两位 | 20年代 | |
%T | 日期-年代 单位“年代” | 2020年代 | |
%Y | 日期-年 单位“年” | 2024年 | |
%M | 日期-月 单位“月” | 11月 | |
%(Mc) | 日期-月 中文文字 | 十一月 | |
%W | 日期-周 单位“周” | 47周 | |
%D | 日期-日 单位“日” | 21日 | |
%(Dy) | 日期-日 年内第几日 单位“日” | 326日 | |
%(Do) | 日期-日 单位“号” | 21号 | |
%L | 日期-星期 中文文字“星期” | 星期四 | |
%(Lss) | 日期-星期 中文文字大写“周” | 周四 | |
%(Ls) | 日期-星期 只输出一个中文文字 | 四 | |
%H | 时间-时 单位“时” | 21时 | |
%(Hy) | 时间-时 单位“时” 十二小时制 | 9时 | |
%(Ho) | 时间-时 单位“点” | 21点 | |
%I | 时间-分 单位“分” | 25分 | |
%S | 时间-秒 单位“秒” | 9秒 | |
%(Mi) | 日期-月内描述“旬”,十日为一旬 | 下旬 | |
%A | 时间-根据时间输出上午或下午 | 下午 | |
%(Di) | 时间-日内详细描述 | 晚上 |
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