Zum Inhalt springen

Modul:Wikidata/Time

aus Wikipedia, der freien Enzyklopädie
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 28. Oktober 2022 um 19:05 Uhr durch Vollbracht (Diskussion | Beiträge) (Ausgliederung aus SimpleDataAccess). Sie kann sich erheblich von der aktuellen Version unterscheiden.
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
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
			function:
				Time.lessthan		comparator function for sort e.g.
				Time.timify(values)	table of time objects by table of values

== for use in templates ==

]=]
	
--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'
	},
	
} }

--------------------------- 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.precision = 14
		d.timezone = 0
		d.calendarmodel = "http://www.wikidata.org/entity/Q1985727"
		return d
	end
	o = {}
	setmetatable(o, self)
	self.__index = self
	if type(source) == "table" then
		local ts = source["time"]
		if not ts then 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
	end
	return o
end

--[[
	Time.lessthan(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.lessthan = 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: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 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.timify(values)
	table of time objects by table of values
	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 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

--------------------------- template functions -----------------------------

--[[
	{{#invoke:Wikidata/Time|now|<format string>}}
	current system time
]]
p.now = function(frame)
	return p.service:new():format(frame.args[1])
end

return p