Módulo:Hours
Version check
Designación de la versión en Wikidata: 2024-05-17
Uso
This module provides the opening hours of an object from Wikidata. Its mostly used in the module VCard, but can used separately as well via {{#invoke: Hours | getHours }}
.
Examples
ID | Label | Opening hours |
---|---|---|
Q99477686 | Chuchichäschtli | Abierto: diario 6:00 pm–12:00 am |
Q6373 | British Museum | Abierto: diario 10:00 am–5:00 pm; Cerrado: 24. Dic., 25. Dic., 26. Dic., 1. Ene. |
Q219867 | Bahnhof King’s Cross | Abierto: Lun–Vie 5:00 am–01:36; sab 5:00 am–01:11; dom 5:30 am–01:36 |
Q2292227 | Skigebiet Gaißau-Hintersee | Abierto: diario 9:00 am–4:00 pm (15 de diciembre–15. Mar.) |
Q11323320 | Pablo Gargallo Museum | Abierto: mar, mie, jue, vie, sab 10:00 am–2:00 pm, 5:00 pm–9:00 pm; dom, Feriado legal 10:00 am–2:30 pm |
Q27831262 | Öömrang Hüs | Abierto: Lun–Vie 11:00 am–1:30 pm, 3:00 pm–5:00 pm (Temporada alta); sab 11:00 am–1:30 pm (Temporada alta); Cerrado: dom, Feriado legal |
Q99690036 | Nationalpark-Tor Rurberg | Abierto: diario 9:00 am–1:00 pm, 1:30 pm–5:00 pm (1. Abr.–31. Oct.), 10:00 am–1:00 pm, 1:30 pm–4:00 pm (1. Nov.–31. Mar.) |
Q2583681 | Grand Egyptian Museum | Abierto: diario 9:00 am–5:00 pm |
Esta documentación está transcluida desde Módulo:Hours/doc.
Los editores pueden experimentar en la zona de pruebas (crear) y en los casos de prueba (crear) del módulo.
Por favor, añade las categorías en la subpágina de documentación. Subpáginas de este módulo.
Los editores pueden experimentar en la zona de pruebas (crear) y en los casos de prueba (crear) del módulo.
Por favor, añade las categorías en la subpágina de documentación. Subpáginas de este módulo.
-- getting opening hours from Wikidata
-- module variable and administration
local hr = {
moduleInterface = {
suite = 'Hours',
serial = '2024-05-17',
item = 99600452
},
labelTable = nil,
typeTables = nil
}
-- module import
-- require( 'strict' )
local hi = require( 'Module:Hours/i18n' )
local wu = require( 'Module:Wikidata utilities' )
-- local variables
local categIds = {}
local showOptions = {}
local function isSet( s )
return s and s ~= ''
end
-- insert a value into a table only if it is set
local function tableInsert( tab, value )
if isSet( value ) then
table.insert( tab, value )
end
end
-- value count for any variable
local function getCount( tab )
return type( tab ) == 'table' and #tab or 0
end
local function getLabelFromTables( id )
local label = hi.dateIds[ id ]
if not label and hr.labelTable then
label = hr.labelTable[ id ]
end
if not label and hr.typeTables and hr.typeTables.idTable then
local item = hr.typeTables.typeTable[ hr.typeTables.idTable[ id ] ]
if item then
item = item.label or item.n or item
local at = mw.ustring.find( item, ',' )
if at then
item = mw.ustring.sub( item, 1, at - 1 )
end
label = mw.text.trim( item )
end
end
return label
end
-- getting normalized time hh:dd
local function getNormalizedTime( s )
local count
s, count = mw.ustring.gsub( s, hi.texts.timePattern, '%1:%2' )
return ( count > 0 ) and s or nil
end
function hr.formatTime( s )
local t = getNormalizedTime( s )
if not t then
return s
end
local formatStr = hi.texts.formatTime
t = mw.text.split( t, ':', true )
if #t == 1 then
t[ 2 ] = '00'
end
if hi.options.hour12 then
local isAM = true
local n = tonumber( t[ 1 ] )
if n > 12 then
isAM = false
t[ 1 ] = '' .. ( n - 12 )
end
formatStr = isAM and hi.texts.formatAM or hi.texts.formatPM
end
s = mw.ustring.format( formatStr, mw.text.trim( t[ 1 ] ),
mw.text.trim( t[ 2 ] ) )
if hi.options.leadingZero then
s = s:gsub( '^(%d):', '0%1:' )
else
s = s:gsub( '^0(%d):', '%1:' )
end
if hi.options.removeZeros then
s = s:gsub( '^(%d%d?):00', '%1' )
end
return s
end
-- getting label for a qualifier id
-- to save computing time firstly the id will fetched from Hours/i18n table
-- if available, otherwise from Wikidata
local function getLabelFromId( id, wikilang, fallbackLang )
if not isSet( id ) then
return ''
end
-- from table
local label = getLabelFromTables( id )
-- from Wikidata
if not label and mw.wikibase.isValidEntityId( id ) then
label = wu.getLabel( id, wikilang )
if not label and isSet( fallbackLang ) then
label = wu.getLabel( id, fallbackLang )
if label then
categIds.fallbackLabel = 1
end
end
if label then
categIds.hoursLabelFromWikidata = 1
end
end
-- abbreviate labels
if isSet( label ) then
for i, abbr in ipairs( hi.abbr ) do
label = mw.ustring.gsub( label, abbr.f, abbr.a )
end
label = mw.ustring.gsub( label, '', '' ) -- zero-width space
end
-- additional time formatting
if isSet( label ) then
if hi.months then
for full, short in pairs( hi.months ) do
label = mw.ustring.gsub( label, full, short )
end
end
label = hr.formatTime( label )
end
return label or ''
end
local function abbreviateTimeStr( s, all, pattern, repl )
if not isSet( s ) or not isSet( pattern ) or not repl then
return s or ''
end
if all then
s = mw.ustring.gsub( s, pattern, repl )
else
local matchPattern = mw.ustring.gsub( pattern, '%(%%d%)', '' )
local first, stop = mw.ustring.find( s, pattern )
if first then
local second = mw.ustring.find( s, pattern, stop + 1 )
if second and mw.ustring.match( s, matchPattern ) ==
mw.ustring.match( s, matchPattern, stop + 1 ) then
s = mw.ustring.gsub( s, pattern, repl, 1 )
end
end
end
return s
end
-- getting time period string
-- i: position in from and to arrays
-- id: label for P3035 value
local function getTimePeriod( from, to, i, id )
local result = ''
if id and ( id == getLabelFromTables( hi.times.daily )
or id == getLabelFromTables( hi.times.is24_7 ) )
and from and to and from[ i ] == getLabelFromTables( hi.times.Jan1 ) and
to[ i ] == getLabelFromTables( hi.times.Dec31 ) then
return ''
end
if from and isSet( from[ i ] ) and to and isSet( to[ i ] ) then
result = mw.ustring.format( hi.texts.fromTo, from[ i ], to[ i ] )
if isSet( hi.texts.hourPattern ) then
result = abbreviateTimeStr( result, hi.texts.hourReplAll,
hi.texts.hourPattern, hi.texts.hourRepl )
end
elseif from and isSet( from[ i ] ) then
result = mw.ustring.format( hi.texts.from, from[ i ] )
elseif to and isSet( to[ i ] ) then
result = mw.ustring.format( hi.texts.to, to[ i ] )
end
return result
end
-- collecting all maintenance categories
function hr.getCategories( formatStr )
local result = wu.getCategories( formatStr )
for k, v in pairs( categIds ) do
result = result .. ( hi.categories[ k ] or hi.categories.unknownError )
end
if showOptions.demo then
-- remove category links
result = result:gsub( '%[%[[^%[]*%]%]', '' )
end
return result
end
-- getting a string with listed days at which an establishment is closed
local function getClosed( arr )
return ( arr and #arr > 0 ) and mw.ustring.format( hi.texts.closed or '%s',
table.concat( arr, hi.texts.comma ) ) or ''
end
-- converting day range from Mo, Tu, We to Mo–We, and so on
local function getRange( arr, minPos, maxPos )
if maxPos > 0 and minPos > 0 and maxPos > minPos then
arr[ minPos ] = mw.ustring.format( hi.texts.fromTo, arr[ minPos ],
arr[ maxPos ] )
for i = maxPos, minPos + 1, -1 do
table.remove( arr, i )
end
end
end
-- looking for day ranges like Mo, Tu, We and so on and converting them
local function convertDayRange( arr )
local count = #arr
local minPos = 0
local maxPos = 0
local value, valueMin
while count > 0 do
value = hi.weekdays[ arr[ count ] ]
if not value then
getRange( arr, minPos, maxPos )
maxPos = 0
elseif maxPos == 0 then
maxPos = count
valueMin = value
elseif maxPos > 0 and value == valueMin - 1 then
minPos = count
valueMin = value
else
getRange( arr, minPos, maxPos )
maxPos = 0
end
count = count - 1
end
getRange( arr, minPos, maxPos )
end
-- concating non-empty strings
local function concatStrings( sep, str1, str2 )
local tab = {}
tableInsert( tab, str1 )
tableInsert( tab, str2 )
return table.concat( tab, sep )
end
-- add comment if not yet exists
local function addComment( tab, value )
if not isSet( value ) then
return
elseif #tab == 0 then
table.insert( tab, value )
else
for i = 1, #tab, 1 do
if tab[ i ] == value then
break
end
if i == #tab then
table.insert( tab, value )
end
end
end
end
-- main function for usage in Lua modules
-- entity: entity id or entity table
-- wikilang: content language of the wiki
-- fallbackLang: optional additional language for fallback
-- formatStr: optional format string for property categories
-- show: table of show options (addCategories, msg, nomsg) or nil
-- lastedit: dat of last edit. If false no date will be fetched
-- labelTabel: additional table with Q-id label pairs
function hr.getHoursFromWikidata( entity, wikilang, fallbackLang, formatStr,
show, lastEdit, labelTable, typeTables )
-- collecting days at which an establishment is closed
local closeDays = {}
local closeDaysHelper = {}
local function mergeDays( days )
if not days or #days == 0 then
return
end
for i, day in ipairs( days ) do
if not closeDaysHelper[ day ] then
table.insert( closeDays, day )
closeDaysHelper[ day ] = ''
end
end
end
local function clearDays()
closeDays = {}
closeDaysHelper = {}
end
-- adding additional properties if an additional Q-id table is given
hr.labelTable = labelTable
hr.typeTables = typeTables
-- preparing show options
showOptions = show or {}
showOptions.addCategories = hi.options.addCategories
if showOptions.msg then
showOptions.addCategories = true
elseif showOptions.nomsg then
showOptions.addCategories = false
end
-- format string for property categories
if not isSet( formatStr ) then
formatStr = hi.categories.properties
end
-- 1st step: getting statements for P3025: open days
local statements = wu.getValuesWithQualifiers( entity, hi.wd.opened, nil,
hi.wd.all, hi.wd.retrieved, nil, getLabelFromId, wikilang, fallbackLang )
lastEdit = wu.getLastedit( lastEdit, statements )
-- converting statements to human-readable strings
local result = {}
local comments, s
local is24_7 = getLabelFromTables( hi.times.is24_7 )
for i, statement in ipairs( statements ) do
-- opening times
local times = {}
local count = math.max( getCount( statement[ hi.wd.hourOpenFrom ] ),
getCount( statement[ hi.wd.hourOpenTo ] ) )
if count > 0 then
for j = 1, count, 1 do
s = getTimePeriod( statement[ hi.wd.hourOpenFrom ],
statement[ hi.wd.hourOpenTo ], j )
if isSet( s ) then
table.insert( times, s )
elseif statement.value ~= is24_7 then
categIds.withoutTime = 1
end
end
elseif statement.value ~= is24_7 then
categIds.withoutTime = 1
end
s = table.concat( times, hi.texts.comma )
-- comments
comments = {}
count = math.max( getCount( statement[ hi.wd.dayOpenFrom ] ),
getCount( statement[ hi.wd.dayOpenTo ] ) )
for j = 1, count, 1 do
addComment( comments,
getTimePeriod( statement[ hi.wd.dayOpenFrom ],
statement[ hi.wd.dayOpenTo ], j, statement.value ) )
end
for j, key in ipairs( hi.wd.comments ) do
if statement[ key ] then
addComment( comments, table.concat( statement[ key ],
hi.texts.comma ) )
end
end
-- concating times and comments
times = {}
tableInsert( times, s )
s = table.concat( comments, hi.texts.comma )
if isSet( s ) then
table.insert( times, mw.ustring.format( hi.texts.parentheses, s ) )
end
local item = {}
tableInsert( item, table.concat( times, hi.texts.space ) )
-- close statements (P3026) as qualifiers for open days property (P3025)
mergeDays( statement[ hi.wd.closed ] )
if not hi.options.clusterClosed and ( i == #statements or
statements[ i ].value ~= statements[ i + 1 ].value ) then
convertDayRange( closeDays )
tableInsert( item, getClosed( closeDays ) )
clearDays()
end
s = table.concat( item, hi.texts.comma )
-- copying each statement to result table
if statement.sort2 == 1 then
tableInsert( result, { value = { statement.value }, times = s } )
elseif s ~= '' then
result[ #result ].times = concatStrings( hi.texts.comma,
result[ #result ].times, s )
end
end
-- checking for duplicates
for i = #result, 2, -1 do
if result[ i ].times == result[ i - 1 ].times then
for j, value in ipairs( result[ i ].value ) do
table.insert( result[ i - 1 ].value, value )
end
table.remove( result, i )
end
end
-- converting day range
for i = 1, #result, 1 do
local arr = result[ i ].value
convertDayRange( arr )
result[ i ] = concatStrings( hi.texts.space,
table.concat( arr, hi.texts.comma ), result[ i ].times )
end
-- 2nd step: getting separated close statements (P3026)
local statements = wu.getValuesWithQualifiers( entity, hi.wd.closed, nil,
hi.wd.commentsForClosed, hi.wd.retrieved, nil, getLabelFromId, wikilang,
fallbackLang )
if #statements > 0 then
lastEdit = wu.getLastedit( lastEdit, statements )
-- getting closed values
local closed = {}
for i, statement in ipairs( statements ) do
local closedDate = {}
table.insert( closedDate, statement.value )
-- getting comments
comments = {}
for j, key in ipairs( hi.wd.commentsForClosed ) do
if statement[ key ] then
addComment( comments, table.concat( statement[ key ],
hi.texts.comma ) )
end
end
s = table.concat( comments, hi.texts.comma )
if isSet( s ) then
table.insert( closedDate, mw.ustring.format( hi.texts.parentheses, s ) )
end
table.insert( closed, table.concat( closedDate, hi.texts.space ) )
end
mergeDays( closed )
end
convertDayRange( closeDays )
tableInsert( result, getClosed( closeDays ) )
-- 3rd step: adding additional statements
local statements = wu.getValuesWithQualifiers( entity, hi.wd.stateOfUse,
nil, {}, hi.wd.retrieved, nil, getLabelFromId, wikilang, fallbackLang )
if #statements > 0 then
lastEdit = wu.getLastedit( lastEdit, statements )
for i, statement in ipairs( statements ) do
tableInsert( result, statement.value )
end
end
-- merging all statements
local result = table.concat( result, hi.texts.semicolon )
-- adding maintenance categories
if result ~= '' then
categIds.hoursFromWikidata = 1
if showOptions.addCategories then
result = result .. hr.getCategories( formatStr )
end
end
return result, lastEdit
end
-- invoke helper functions
-- splitting show parameters
local function splitAndCheck( s )
local arr = {}
local err = {}
if isSet( s ) then
for i, v in ipairs( mw.text.split( s, ',', true ) ) do
v = mw.text.trim( v )
if not hi.show[ v ] then
table.insert( err, v )
else
arr[ v ] = ''
end
end
end
return arr, err
end
-- check if pareameters are valid
local function checkParameters( args )
local err = {}
if not args and type( args ) ~= 'table' then
return err
end
for k, v in pairs( args ) do
if not hi.params[ k ] then
table.insert( err, k )
end
end
return err
end
-- formating and concating error strings
local function getErrorStr( arr, formatStr )
return #arr == 0 and '' or
mw.ustring.format( formatStr, table.concat( arr, hi.texts.comma ) )
end
-- main function for template #invoke calls
-- id: Q id of an establishment
-- format: output format like 'opened at %s'
-- fallback: fallback language if labels are not available in content language
function hr.getHours( frame )
local args = frame.args
args.id = mw.text.trim( args.id or '' )
if not isSet( args.id ) or not mw.wikibase.isValidEntityId( args.id ) then
return hi.categories.invalidId
end
if not isSet( args.format ) then
args.format = hi.texts.format
else
local s, count = args.format:gsub( '%%s', '%%s' )
if count ~= 1 then
args.format = hi.texts.format
end
end
args.fallback = args.fallback or ''
local wikilang = mw.getContentLanguage():getCode()
local paramErr = checkParameters( args )
local show, showErr = splitAndCheck( args.show )
local result, lastEdit = hr.getHoursFromWikidata( args.id, wikilang,
args.fallback, '', show, false, nil )
if result ~= '' then
result = mw.ustring.format( args.format, result )
end
return result .. getErrorStr( paramErr, hi.categories.unknownParams )
.. getErrorStr( showErr, hi.categories.unknownShowOptions )
end
return hr