Vorlagenprogrammierung | Diskussionen | Lua | Unterseiten | |
Modul | Deutsch
|
Modul: | Dokumentation |
Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus
--[=[ Wikidata/Time 2022-10-28
Module for processing wikidata time information
Author: Vollbracht
== for use in other modules ==
* service object representing a wikidata time property
fields: year, month, day, hour, min, sec,
precision, timezone, calendarmodel
constructor:
Time:new(value) processes snack.datavalue.value e.g.
method:
Time:format(fmtStr) string representation for time object
Time:qualified(snak) snak and qualification state
Time:isLessthan(time) comparator method
Time:equals(time) comparator method
* service.LT(a, b) comparator function for table.sort e.g.
* service.equals(a, b) comparator function
* service.timify(values) table of time objects by table of values
== for use in templates ==
{{#invoke:Wikidata/Time|now|<format string>}} current system time
(mainly for testing purposes;
format string optional: defaults to wikidata time string format)
]=]
--Module globals
local p = { service={
-- more selective variant to '(.)(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)Z':
MATCHING = '(.)(%d+)%-([0-1]%d)%-([0-3]%d)T([0-2]%d):([0-6]%d):([0-6]%d)Z',
PRECISIONLEVEL = {
-- unused:
'E8', 'E7', 'E6', 'E5', 'E4',
-- use with caution (only 'year' is in time object) :
[6]='millennium', [7]='century', [8]='decade', [9]='year',
-- use at will (fields in time object):
[10]='month', [11]='day', [12]='hour', [13]='min', [14]='sec'
},
DEFAULTFORMAT = {
[6]='yyyye=( v.Chr.)', [7]='yyyye=( v.Chr.)', [9]='yyyye=( v.Chr.)',
[9]='yyyye=( v.Chr.)', [10]='mm/yyyy', [11]='dd.mm.yyyy', [12]='HH Uhr',
[13]='HH:MM Uhr', [14]='E=(+)e=(-)yyyy-mm-ddTHH:MM:SSZ'
},
PoT='point of time'
} }
local _, DateTime = pcall(require, "Modul:DateTime")
--------------------------- local functions -----------------------------
--------------------------- service functions -----------------------------
--[[
constructor
generates a Time object
parameters:
source: (optional) table of possible time informations as of
mw.wikibase.getBestStatements(a, b)[1].mainsnak.datavalue.value
current system time if nil
NO MANDANTORY INFOS IN TABLE! OBJECT MAY CONTAIN NO TIME DATA!
]]
function p.service:new(source)
-- time object is UTC system time if no source specified
if not source or source == '' then
d = os.date("!*t")
for k, v in pairs(p.service) do d[k] = v end
d.era = '+'
d.precision = 14
d.timezone = 0
d.calendarmodel = "http://www.wikidata.org/entity/Q1985727"
d.__tostring=function()
return d:format('+yyyy-mm-ddTHH:MM:SSZ')
end
return d
end
o = {}
setmetatable(o, self)
self.__index = self
if type(source) == "table" then
local ts = source["time"]
if not ts then
o.__tostring=function()
return 'Time not initialized!'
end
return o
end
o.era, o.year, o.month, o.day, o.hour, o.min, o.sec
= ts:match(o.MATCHING)
o.year = tonumber(o.year)
o.month = tonumber(o.month)
o.day = tonumber(o.day)
o.hour = tonumber(o.hour)
o.min = tonumber(o.min)
o.sec = tonumber(o.sec)
o.precision = source.precision
o.timezone = source.timezone
o.calendarmodel = source.calendarmodel
o.__tostring=function()
return o:format()
end
end
return o
end
--[[
Time.isLessthan(a, b)
datetime compare
parameters:
a, b 2 service.time objects
returns: true if on lowest precision level a is before b
false if necessary properties inavailable or
if a is same time or later than b on given
precision level
]]
p.service.isLessthan = function(a, b)
if not a then return false end
if not b then return false end
local precision = 11
if a.precision then precision = a.precision end
if b.precision and b.precision < precision then
precision = b.precision
end
if a.era and b.era then
if a.era ~= b.era then return b.era == '+' end
if a.year and b.year then
local na = tonumber(a.year)
local nb = tonumber(b.year)
if na ~= nb then return (a.era == '+') == (na < nb) end
else return false end
end
for i = 10, 14 do
if precision < i then return false end
local aa = a[p.service.PRECISIONLEVEL[i]]
local ab = b[p.service.PRECISIONLEVEL[i]]
if aa and ab then
local na = tonumber(aa)
local nb = tonumber(ab)
if na ~= nb then return na < nb end
else return false end
i = i + 1
end
return false
end
--[[
Time.LT(a, b)
comparator function for sort e.g.
]]
p.service.LT = function(a, b)
if a.time then a = a.time end
if b.time then b = b.time end
if not a.isLessthan then return false end
return a.isLessthan(b)
end
--[[
Time.equals(a, b)
compares any objects in terms of time properties
parameters: two of any kind
returns: true * if both objects represent the same time at lowest
common precision level
* if both objects lack the same time properties
* if both are no objects but of simple data type
false * if the objects represent different times at lowest
common precision level
* if only one lacks a relevant time property
* if only one is of simple data type even if the other
doesn't contain time properties at all
]]
p.service.equals = function(a, b)
if not a == not b then return true end
if not a then return false end
if not b then return false end
if type(a) ~= 'table' then return type (b) == 'table' end
if type (b) ~= 'table' then return false end
local precision = 11
if a.precision then precision = a.precision end
if b.precision and b.precision < precision then
precision = b.precision
end
if a.era then
if a.era == '+' then
if b.era and b.era ~= '+' then return false end
else
if b.era then
if b.era == '+' then return false end
else return false end
end
else
if b.era and b.era ~= '+' then return false end
end
if precision < 9 then precision = 9 end
for i = 9, precision do
local aa = a[p.service.PRECISIONLEVEL[i]]
local ab = b[p.service.PRECISIONLEVEL[i]]
if not aa ~= not ab then return false end
if aa and aa ~= ab then return false end
i = i + 1
end
return true
end
--[[
Time:format(fmtStr)
return formated date time value based on given format string
parameters:
fmtStr: limited format string evaluating each appearance of keys:
either
%y or %Y for year
%m for month
%d for day of month
%H for 24-hour hour value
%M for minute
%S for second
or
yy or yyyy for year
m or mm for month
d or dd for day of month
H or HH for 24-hour hour value
M or MM for minute
S or SS for second
e=(<content>) for content string appearing
if era is '-' (BC)
E=(<content>) for content string appearing
if era is '+' (CE)
returns date time value string
]]
p.service.format = function(this, fmtStr)
function d2(value)
if value > 9 then return value end
return '0' .. value
end
if not fmtStr then
if this.precision then
if this.precision < 6 then return '' end
fmtStr = this.DEFAULTFORMAT[this.precision]
else return '' end
end
if fmtStr:match('%%') then
if this.year then
fmtStr = fmtStr:gsub('%%Y', this.year)
if this.year < 100 then fmtStr = fmtStr:gsub('%%y', this.year)
else fmtStr = fmtStr:gsub('%%y', d2(this.year % 100)) end
end
if this.month then fmtStr = fmtStr:gsub('%%m', d2(this.month)) end
if this.month then fmtStr = fmtStr:gsub('%%d', d2(this.day)) end
if this.month then fmtStr = fmtStr:gsub('%%H', d2(this.hour)) end
if this.month then fmtStr = fmtStr:gsub('%%M', d2(this.min)) end
if this.month then fmtStr = fmtStr:gsub('%%S', d2(this.sec)) end
else
if this.year then
fmtStr = fmtStr:gsub('yyyy', this.year)
fmtStr = fmtStr:gsub('jjjj', this.year)
if this.year < 100 then
fmtStr = fmtStr:gsub('yy', this.year)
fmtStr = fmtStr:gsub('jj', this.year)
else
fmtStr = fmtStr:gsub('yy', d2(this.year % 100))
fmtStr = fmtStr:gsub('jj', d2(this.year % 100))
end
end
if this.month then
fmtStr = fmtStr:gsub('mm', d2(this.month))
fmtStr = fmtStr:gsub('m', this.month)
end
if this.day then
fmtStr = fmtStr:gsub('dd', d2(this.day))
fmtStr = fmtStr:gsub('tt', d2(this.day))
fmtStr = fmtStr:gsub('d', this.day)
fmtStr = fmtStr:gsub('t', this.day)
end
if this.hour then
fmtStr = fmtStr:gsub('HH', d2(this.hour))
fmtStr = fmtStr:gsub('H', this.hour)
end
if this.min then
fmtStr = fmtStr:gsub('MM', d2(this.min))
fmtStr = fmtStr:gsub('M', this.min)
end
if this.sec then
fmtStr = fmtStr:gsub('SS', d2(this.sec))
fmtStr = fmtStr:gsub('S', this.sec)
end
if this.era then
if this.era == '+' then
fmtStr = fmtStr:gsub('e=%(.*%)', '')
fmtStr = fmtStr:gsub('E=%((.*)%)', '%1')
else
fmtStr = fmtStr:gsub('E=%(.*%)', '')
fmtStr = fmtStr:gsub('e=%((.*)%)', '%1')
end
end
end
return fmtStr
end
--[[
Time:qualified(Statement)
Statement and its qualification state
parameters:
Statement a Statement that is to be qualified
returns: Statement, true if this time qualifies the snack
Statement, false if Statement is independent from this time
nil, false if this time disqualifies the snack
nil, true if Statement is nil (Ex falso quodlibet)
]]
p.service.qualified = function(this, Statement)
if not snack then return nil, true end
if type(Statement) ~= 'table' then return Statement, false end
if not Statement.qualifiers then return Statement, false end
local hasCriteria = false
-- point of time
for _, pot in ipairs(Statement.qualifiers.P585) do
local comp = p.service:new(pot.datavalue.value)
if not this:equals(comp) then return nil, false end
hasCriteria = true;
end
-- start time
for _, pot in ipairs(Statement.qualifiers.P580) do
local comp = p.service:new(pot.datavalue.value)
if this:isLessthan(comp) then return nil, false end
hasCriteria = true;
end
-- end time
for _, pot in ipairs(Statement.qualifiers.P582) do
local comp = p.service:new(pot.datavalue.value)
if comp:isLessthan(this) then return nil, false end
hasCriteria = true;
end
return Statement, hasCriteria
end
--[[
Time:getFiltered(statemens)
new claim list without time disqualified claims
parameters:
statemens data source as of mw.wikidata.getBestStatements
returns: refined claimlist
]]
p.service.getFiltered = function(this, statements)
local result = {}
for _, v in ipairs(claimlist) do
local tested, tv = this:qualified(v)
if tested then table.insert(result, tested) end
end
return result
end
--[[
Time.timify(values)
table of time objects by table of values
can process statements as of mw.wikidata.getBestStatements('Q79822', 'P569')
or qualifiers for a statement
parameters:
values: table: {<source>, <source>, ...}
<source>: (requirements as of time:new() source parameter)
returns: table: {<time object>, <time object>, ...}
Time objects of <source>-elements that could be
converted are in this result table. The others are
just ignored. Hence contains initialized time
objects only.
nil if result table empty
]]
p.service.timify = function(values)
local result = {}
for _, v in ipairs(values) do
local value = v
if value.mainsnak then value = value.mainsnak end
if value.datavalue then value = value.datavalue end
if value.value then value = value.value end
local t = service.time:new(v)
if t.year then table.insert(result, t) end
end
if #result == 0 then return nil end
return result
end
--[[
Time.filtered(claimlist, timeStr)
new claim list without time disqualified claims
parameters:
claimlist data source as of mw.wikidata.getBestStatements
timeStr time given by user
returns: refined claimlist
]]
p.service.filtered = function(statements, timeStr)
local myTime = nil
if not timeStr then myTime = p.service:new() end
if timeStr:match('^%d+$') then
myTime = p.service:new({})
myTime.era = '+'
myTime.year = tonumber(timeStr)
myTime.precision=9
myTime.__tostring=function() -- just in case s.o. edits filtered(..)
return timeStr
end
else
local t = mw.language.getContentLanguage():formatDate(
"+Y-m-d\\TH:i:s\\Z", timeStr)
local p = 14
if t:match('00:00:00Z') then p = 11
elseif t:match('00:00Z') then p = 12
elseif t:match('00Z') then p = 13
end
local source={
time=t,
precision=p,
calendarmodel='http://www.wikidata.org/entity/Q1985727',
timezone=0
}
myTime = p.service:new(source)
end
return myTime:getFiltered(statements)
end
--------------------------- template functions -----------------------------
--[[
{{#invoke:Wikidata/Time|now|<format string>}}
current system time
]]
p.now = function(frame)
return p.service:new():format(frame.args[1])
end
p.test = function(frame)
-- test constructions
local t1 = p.service:new()
mw.logObject(t1, 'p.service:new()')
mw.logObject(DateTime, 'DateTime')
mw.logObject(DateTime.Prototypes, 'DateTime.Prototypes')
end
return p