Přeskočit na obsah

Modul:Time

Tato stránka je zamčena
Z Wikipedie, otevřené encyklopedie

Tento modul slouží ke zpracování časových údajů ve formátuISO 8601,např. při jejich přebírání z Wikidat. Není určený k přímému použití v článcích nebo šablonách. Byl přejat ztest2wiki.


require"Modul:No globals"

localTime={}

--[[
Check if a value is a number in the given range
@param mixed value
@param number min
@param number max
@return boolean
]]--
localfunctionvalidateNumberInRange(value,min,max)
returntype(value)=='number'andvalue>=minandvalue<=max
end

--Internal functions
--[[
Validate a time defintion
@param table definition data
@return boolean
]]--
localfunctionvalidate(definition)
--Validate constantes
ifnotTime.knowsPrecision(definition.precision)or(definition.calendar~=Time.CALENDAR.GREGORIANanddefinition.calendar~=Time.CALENDAR.JULIAN)then
returnfalse
end

--Validate year
ifnot(type(definition.year)=='number'or(definition.year==nilandprecision==Time.PRECISION.DAY))then
returnfalse
end
ifdefinition.precision<=Time.PRECISION.YEARthen
returntrue
end

--Validate month
ifnotvalidateNumberInRange(definition.month,1,12)then
returnfalse
end
ifdefinition.precision<=Time.PRECISION.MONTHthen
returntrue
end

--Validate day
ifnotvalidateNumberInRange(definition.day,1,31)then
returnfalse
end
ifdefinition.precision<=Time.PRECISION.DAYthen
returntrue
end

--Validate hour
ifnotvalidateNumberInRange(definition.hour,0,23)then
returnfalse
end
ifdefinition.precision<=Time.PRECISION.HOURthen
returntrue
end

--Validate minute
ifnotvalidateNumberInRange(definition.minute,0,59)then
returnfalse
end
ifdefinition.precision<=Time.PRECISION.MINUTEthen
returntrue
end

--Validate second
ifnotvalidateNumberInRange(definition.second,0,60)then
returnfalse
end

returntrue
end

--[[
Try to find the relevant precision for a time definition
@param table time definition
@return number the precision
]]--
localfunctionguessPrecision(definition)
ifdefinition.month==nilthen
returnTime.PRECISION.YEAR
elseifdefinition.day==nilthen
returnTime.PRECISION.MONTH
elseifdefinition.hour==nilthen
returnTime.PRECISION.DAY
elseifdefinition.minute==nilthen
returnTime.PRECISION.HOUR
elseifdefinition.second==nilthen
returnTime.PRECISION.MINUTE
else
returnTime.PRECISION.SECOND
end
end

--[[
Try to find the relevant calendar for a time definition
@param table time definition
@return string the calendar name
]]--
localfunctionguessCalendar(definition)
ifdefinition.year~=nilanddefinition.year<1583anddefinition.precision>Time.PRECISION.MONTHthen
returnTime.CALENDAR.JULIAN
else
returnTime.CALENDAR.GREGORIAN
end
end

--[[
Parse an ISO 8601 string and return it as a time definition
@param string iso the iso datetime
@param boolean withoutRecurrence concider date in the format XX-XX as year-month and not month-day
@return table
]]--
localfunctionparseIso8601(iso,withoutRecurrence)
localdefinition={}

--Split date and time
iso=mw.text.trim(iso:upper())
localbeginMatch,endMatch,date,time,offset=iso:find('([%+%-]?[%d%-]+)[T ]?([%d%.:]*)([Z%+%-]?[%d:]*)')

ifbeginMatch~=1orendMatch~=iso:len()then--iso is not a valid ISO string
return{}
end

--date
ifdate~=nilthen
localisBC=false
ifdate:sub(1,1)=='-'then
isBC=true
date=date:sub(2,date:len())
end
localparts=mw.text.split(date,'-')
ifnotwithoutRecurrenceandtable.maxn(parts)==2andparts[1]:len()==2then
--MM-DD case
definition.month=tonumber(parts[1])
definition.day=tonumber(parts[2])
else
ifisBCthen
definition.year=-1*tonumber(parts[1])-1--Years BC are counted since 0 and not -1
else
definition.year=tonumber(parts[1])
end
definition.month=tonumber(parts[2])
definition.day=tonumber(parts[3])
end
end

--time
iftime~=nilthen
localparts=mw.text.split(time,':')
definition.hour=tonumber(parts[1])
definition.minute=tonumber(parts[2])
definition.second=tonumber(parts[3])
end

--ofset
ifoffset~=nilthen
ifoffset=='Z'then
definition.utcoffset='+00:00'
else
definition.utcoffset=offset
end
end

returndefinition
end

--[[
Format UTC offset for ISO output
@param string offset UTC offset
@return string UTC offset for ISO
]]--
localfunctionformatUtcOffsetForIso(offset)
ifoffset=='+00:00'then
return'Z'
else
returnoffset
end
end

--[[
Prepend as mutch as needed the character c to the string str in order to to have a string of length length
@param mixed str
@param string c
@param number length
@return string
]]--
localfunctionprepend(str,c,length)
str=tostring(str)
whilestr:len()<lengthdo
str=c..str
end
returnstr
end

localfunctioneq(t1,t2)
returnnot(t1<t2ort2<t1)
end

localfunctionlt(t1,t2)
ift1.calendar~=t2.calendarthen
--...
end
ift1.year~=t2.yearthen
returnt1.year<t2.year
end
ift1.month~=t2.monththen
returnt1.month<t2.month
end
returnt1.day<t2.day
end

--Public interface
--[[
Build a new Time
@param table definition definition of the time
@return Time|nil
]]--
functionTime.new(definition)
--Default values
ifdefinition.precision==nilthen
definition.precision=guessPrecision(definition)
end
ifdefinition.calendar==nilthen
definition.calendar=guessCalendar(definition)
end

ifnotvalidate(definition)then
returnnil
end

localtime={
year=definition.yearornil,
month=definition.monthor1,
day=definition.dayor1,
hour=definition.houror0,
minute=definition.minuteor0,
second=definition.secondor0,
utcoffset=definition.utcoffsetor'+00:00',
calendar=definition.calendarorTime.CALENDAR.GREGORIAN,
precision=definition.precisionor0
}

setmetatable(time,{
__index=Time,
__lt=lt,
__eq=eq,
__tostring=function(self)returnself:toString()end
})

returntime
end

--[[
Build a new Time from an ISO 8601 datetime
@param string iso the time as ISO string
@param boolean withoutRecurrence concider date in the format XX-XX as year-month and not month-day
@return Time|nil
]]--
functionTime.newFromIso8601(iso,withoutRecurrence)
returnTime.new(parseIso8601(iso,withoutRecurrence))
end

--[[
Build a new Time from a Wikidata time value
@param table wikidataValue the time as represented by Wikidata
@return Time|nil
]]--
functionTime.newFromWikidataValue(wikidataValue)
localdefinition=parseIso8601(wikidataValue.time)
definition.precision=wikidataValue.precision

ifwikidataValue.calendarmodel=='http:// wikidata.org/entity/Q1985727'then
definition.calendar=Time.CALENDAR.GREGORIAN
elseifwikidataValue.calendarmodel=='http:// wikidata.org/entity/Q1985786'then
definition.calendar=Time.CALENDAR.JULIAN
else
returnnil
end

returnTime.new(definition)
end

--[[
Return a Time as a ISO 8601 string
@return string
]]--
functionTime:toIso8601()
localiso=''
ifself.year~=nilthen
ifself.year<0then
--Years BC are counted since 0 and not -1
iso='-'..prepend(-1*self.year-1,'0',4)
else
iso=prepend(self.year,'0',4)
end
end

--month
ifself.precision<Time.PRECISION.MONTHthen
returniso
end
ifself.iso~=''then
iso=iso..'-'
end
iso=iso..prepend(self.month,'0',2)

--day
ifself.precision<Time.PRECISION.DAYthen
returniso
end
iso=iso..'-'..prepend(self.day,'0',2)

--hour
ifself.precision<Time.PRECISION.HOURthen
returniso
end
iso=iso..'T'..prepend(self.hour,'0',2)

--minute
ifself.precision<Time.PRECISION.MINUTEthen
returniso..formatUtcOffsetForIso(self.utcoffset)
end
iso=iso..':'..prepend(self.minute,'0',2)

--second
ifself.precision<Time.PRECISION.SECONDthen
returniso..formatUtcOffsetForIso(self.utcoffset)
end
returniso..':'..prepend(self.second,'0',2)..formatUtcOffsetForIso(self.utcoffset)
end

--[[
Return a Time as a string
@param mw.language|string|nil language to use. By default the content language.
@return string
]]--
functionTime:toString(language)
iflanguage==nilthen
language=mw.language.getContentLanguage()
elseiftype(language)=='string'then
language=mw.language.new(language)
end

--return language:formatDate( 'r', self:toIso8601() )
returnself:toIso8601()
--TODO: improve
end

--[[
Return a Time in HTMl (with a <time> node)
@param mw.language|string|nil language to use. By default the content language.
@param table|nil attributes table of attributes to add to the <time> node.
@return string
]]--
functionTime:toHtml(language,attributes)
ifattributes==nilthen
attributes={}
end
attributes['datetime']=self:toIso8601()
returnmw.text.tag('time',attributes,self:toString(language))
end

--[[
All possible precisions for a Time (same ids as Wikibase)
]]--
Time.PRECISION={
GY=0,--Gigayear
MY100=1,--100 Megayears
MY10=2,--10 Megayears
MY=3,--Megayear
KY100=4,--100 Kiloyears
KY10=5,--10 Kiloyears
KY=6,--Kiloyear
YEAR100=7,--100 years
YEAR10=8,--10 years
YEAR=9,
MONTH=10,
DAY=11,
HOUR=12,
MINUTE=13,
SECOND=14
}

--[[
Check if the precision is known
@param number precision ID
@return boolean
]]--
functionTime.knowsPrecision(precision)
for_,idinpairs(Time.PRECISION)do
ifid==precisionthen
returntrue
end
end
returnfalse
end

--[[
Supported calendar models
]]--
Time.CALENDAR={
GREGORIAN='Gregorian',
JULIAN='Julian'
}

returnTime