Modulo:Data
Questo è un modulo scritto in Lua. Le istruzioni che seguono sono contenute nella sottopagina Modulo:Data/man (modifica · cronologia)
Sandbox: Modulo:Data/sandbox (modifica · cronologia) · Sottopagine: lista · Test: Modulo:Data/test (modifica · cronologia · Esegui)
Modulo per eseguire operazioni sulle date.
Funzioni
diff
Questa funzione ritorna la differenza tra le date inserite tramite i parametri "inizio" e "fine" in una sola unità tra "anni", "mesi", "settimane", "giorni", "ore", "minuti" e "secondi". L'unità può essere specificata tramite il parametro "magnitudine", se non inserito viene utilizzata automaticamente l'unità più adatta. Il formato per le date è quello accettato dalla funzione parser #time.
- Uso
{{#invoke:Data|diff|inizio=data1|fine=data2}}
- Parametri
- inizio: data di partenza
- fine: data di arrivo, può essere successiva o anteriore a inizio, se anteriore verrà anteposto il segno "-" (meno) al risultato.
- magnitudine: l'unità da ritornare nel risultato, tra "anni", "mesi", "settimane", "giorni", "ore", "minuti" e "secondi". Se non inserita è utilizzata automaticamente la più adatta.
- magnitudine min: quando non è utilizzata magnitudine e il modulo utilizza automaticamente l'unità più adatta, serve per configurare l'unità minima da usare. Se per esempio "magnitudine min" viene impostata a "mesi" allora non verranno usate le unità minori (giorni, minuti e secondi) anche se queste fossero le più adatte. È utile quando il modulo è usato in un template: pur non conoscendo a priori le date che verranno utilizzate si può così stabilire una unità minima che verrà visualizzata.
- dir: se impostato a qualunque valore, esempio "dir=sì", nel risultato invece del segno verrà utilizzato il prefisso "tra" se positivo (esempio: "tra 1 mese") o verrà appeso "fa" se negativo (esempio "un mese fa").
- Esempi
{{#invoke:Data|diff|inizio=2016/01/01|fine=2016/02/10}}
→ 40 giorni{{#invoke:Data|diff|inizio=2016/01/01|fine=2016/02/10|magnitudine=mesi}}
→ 1 mese{{#invoke:Data|diff|inizio=2016/01/01|fine=2014/07/01}}
→ -18 mesi{{#invoke:Data|diff|inizio=2016/01/01|fine=2014/07/01|magnitudine=anni}}
→ -1 anno
Gli stessi esempi con dir=sì:
{{#invoke:Data|diff|inizio=2016/01/01|fine=2016/02/10|dir=sì}}
→ tra 40 giorni{{#invoke:Data|diff|inizio=2016/01/01|fine=2016/02/10|magnitudine=mesi|dir=sì}}
→ tra 1 mese{{#invoke:Data|diff|inizio=2016/01/01|fine=2014/07/01|dir=sì}}
→ 18 mesi fa{{#invoke:Data|diff|inizio=2016/01/01|fine=2014/07/01|magnitudine=anni|dir=sì}}
→ 1 anno fa
diff_ymd
Questa funzione ritorna la differenza in anni, mesi e giorni tra le date inserite tramite i parametri "inizio" e "fine". Il formato per le date è quello accettato dalla funzione parser #time.
- Uso
{{#invoke:Data|diff_ymd|inizio=data1|fine=data2}}
- Parametri
- inizio: data di partenza
- fine: data di arrivo, può essere successiva o anteriore a inizio, se anteriore verrà anteposto il segno "-" (meno) al risultato.
- dir: se impostato a qualunque valore, esempio "dir=sì", nel risultato invece del segno verrà utilizzato il prefisso "tra" se positivo (esempio: "tra 1 mese e 1 giorno") o verrà appeso "fa" se negativo (esempio "un mese e un giorno fa").
- Esempi
{{#invoke:Data|diff_ymd|inizio=2016/01/01|fine=2015/01/01}}
→ -1 anno{{#invoke:Data|diff_ymd|inizio=2016/01/01|fine=2017/03/04}}
→ 1 anno, 2 mesi e 3 giorni{{#invoke:Data|diff_ymd|inizio=2016/01/01|fine=2012/12/31}}
→ -3 anni e 1 giorno
Gli stessi esempi con dir=sì:
{{#invoke:Data|diff_ymd|inizio=2016/01/01|fine=2015/01/01|dir=sì}}
→ 1 anno fa{{#invoke:Data|diff_ymd|inizio=2016/01/01|fine=2017/03/04|dir=sì}}
→ tra 1 anno, 2 mesi e 3 giorni{{#invoke:Data|diff_ymd|inizio=2016/01/01|fine=2012/12/31|dir=sì}}
→ 3 anni e 1 giorno fa
compare
Questa funzione confronta due date e restituisce 0 se le date sono uguali, -1 se la prima è anteriore alla seconda, altrimenti 1. Il formato per le date è quello accettato dalla funzione parser #time.
- Uso
{{#invoke:Data|compare|data1=prima data|data2=seconda data}}
- Parametri
- data1: la prima data
- data2: la seconda data
- Esempi
{{#invoke:Data|compare|data1=2016/01/01|data2=2016/01/01}}
→ 0{{#invoke:Data|compare|data1=2016/01/01|data2=2016/01/10}}
→ -1{{#invoke:Data|compare|data1=2016/01/10|data2=2016/01/01}}
→ 1
Utilizzo da un altro modulo
Oltre che attraverso #invoke, il modulo può essere utilizzato tramite "require" da un altro modulo. Le stesse funzioni sono disponibili tramite la classe Date:
local Date = require('Modulo:Data').Date
- Esempio
local Date = require('Modulo:Data').Date
local p = {}
function p.main(frame)
local inizio, fine, diff1, diff2, text1, text2, anteriore
inizio = Date:new('2016/01/01')
fine = Date:new('2017/03/04')
-- utilizzo della funzione diff
diff1 = inizio:diff(fine, 'mesi')
-- utilizzo della funzione diff_ymd
diff2 = inizio:diffYMD(fine)
-- data per esteso
text1 = inizio:getDateString()
text2 = fine:getDateString()
-- confronto
anteriore = inizio < fine
return string.format('La differenza tra il %s e il %s è di %s, più precisamente di %s. ' ..
'La prima data è anteriore alla seconda: %s.',
text1, text2, diff1, diff2, tostring(anteriore))
end
return p
Unit test
Unit test per questo modulo sono disponibili a Modulo:Data/test, per la loro esecuzione visitare Discussioni_modulo:Data/test.
--[[
* Modulo per eseguire operazioni sulle date.
* Utilizzato da template come {{Età wikipediana}} e {{Tempo trascorso}}.
]]
require('strict')
local getArgs = require('Modulo:Arguments').getArgs
local errorCategory = '[[Categoria:Voci con errori del modulo Data]]'
-- classe esportata
local Date = {}
-------------------------------------------------------------------------------
-- Funzioni di utilità
-------------------------------------------------------------------------------
-- Error handler per xpcall, formatta l'errore
local function errhandler(msg)
local cat = mw.title.getCurrentTitle().namespace == 0 and errorCategory or ''
return string.format('<span class="error">Errore: %s</span>%s', msg, cat)
end
local function isValidDate(date)
return pcall(function() mw.getContentLanguage():formatDate('', date) end)
end
-- Controlla le date inserite dall'utente e le restituisce come oggetti Date
local function parseArgs(args, isCompare)
local data1, data2, label1, label2
if isCompare then
data1, data2 = 'data1', 'data2'
label1, label2 = 'prima data', 'seconda data'
else
data1, data2 = 'inizio', 'fine'
label1, label2 = 'data di partenza', 'data di fine'
end
if not args[data1] then
error(string.format('la %s è obbligatoria', label1), 2)
elseif not isValidDate(args[data1]) then
error(string.format('la %s non è valida', label1), 2)
elseif not args[data2] then
error(string.format('la %s è obbligatoria', label2), 2)
elseif not isValidDate(args[data2]) then
error(string.format('la %s non è valida', label2), 2)
end
return {
d1 = Date:new(args[data1]),
d2 = Date:new(args[data2])
}
end
-------------------------------------------------------------------------------
-- classe Date
-------------------------------------------------------------------------------
local function date_eq(t, t2)
return t.ut == t2.ut
end
local function date_lt(t, t2)
return t.ut < t2.ut
end
-- Costruisce un oggetto Date a partire da una stringa nel formato
-- accettato dalla funzione parser #time.
function Date:new(str, precision)
local self = {}
setmetatable(self, { __index = Date, __eq = date_eq, __lt = date_lt })
self.ut = tonumber(mw.getContentLanguage():formatDate('U', str, true))
self.precision = precision or 11
return self
end
-- Costruisce un oggetto Date a partire da una stringa nel formato:
-- "giorno mese_per_esteso anno" oppure "mese_per_esteso anno" oppure "anno".
function Date:newDMY(str)
local months = {
gennaio = 1, febbraio = 2, marzo = 3, aprile = 4, maggio = 5, giugno = 6,
luglio = 7, agosto = 8, settembre = 9, ottobre = 10, novembre = 11, dicembre = 12
}
local success, result = pcall(function()
local day, month, year = str:match("(%d+) (%a+) (%d+)")
if day then
return Date:new(string.format('%d-%d-%d', year, months[month], day))
else
month, year = str:match("(%a+) (%d+)")
if month then
return Date:new(string.format('%d-%d', year, months[month]), 10)
else
return Date:new(string.format('%d', str:match("(%d+)")), 9)
end
end
end )
return success and result or nil
end
-- Restituisce una stringa che rappresenta la data, senza l'ora.
function Date:getDateString()
local fmt = self.precision == 9 and 'Y' or
(self.precision == 10 and 'F Y' or
(self.precision == 11 and 'j F Y' or 'j F Y'))
return (mw.getContentLanguage():formatDate(fmt, '@' .. self.ut):gsub('^1%s', '1º '))
end
-- Restituisce un nuovo oggetto Date la cui data è avanzata del numero di giorni specificati.
function Date:addDays(days)
return Date:new('@' .. (self.ut + days * 86400))
end
-- Funzione di utilità per Date:diffYMD e Date:diff
-- Aggiunge un eventuale prefisso e suffisso al risultato invece del segno.
-- L'ultimo parametro diffVal è utilizzato solo da diff per evitare che
-- {{#invoke:Data|diff|inizio=2016/01/01|fine=2015/12/31|magnitudine=anni}} ritorni "-0 anni".
local function formatResult(result, date1, date2, dir, diffVal)
local ret
if dir then
-- ritorna il 'fa' anche con date1.ut == date2.ut (si potrebbe configurare con un parametro)
ret = date1.ut < date2.ut and 'tra ' .. result or result .. ' fa'
else
ret = (date1.ut <= date2.ut or diffVal == 0) and result or '-' .. result
end
return ret
end
-- Restituisce la differenza con la data date2 in anni, mesi e giorni.
function Date:diffYMD(date2, rawTable, dir)
local monthdays = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
local d1, d2 = os.date('*t', self.ut), os.date('*t', date2.ut)
local ret = { seconds = math.abs(self.ut - date2.ut) }
if self.ut >= date2.ut then d1, d2 = d2, d1 end
-- anni
ret.years = d2.year - d1.year
if ret.years > 0 and (d1.month > d2.month or (d1.month == d2.month and d1.day > d2.day)) then
ret.years = ret.years - 1
end
-- mesi
ret.months = (d2.month - d1.month + 12) % 12
if d1.day > d2.day then
ret.months = (ret.months == 0 and d1.year < d2.year) and 11 or ret.months - 1
end
-- giorni
ret.days = d2.day >= d1.day and d2.day - d1.day or (monthdays[d1.month] - d1.day) + d2.day
-- utilizza la sequence per ritornare anche la versione testuale
if ret.years > 0 then
table.insert(ret, string.format('%s %s', ret.years, ret.years == 1 and 'anno' or 'anni'))
end
if ret.months > 0 then
table.insert(ret, string.format('%s %s', ret.months, ret.months == 1 and 'mese' or 'mesi'))
end
if ret.days > 0 or ret.years + ret.months + ret.days == 0 then
table.insert(ret, string.format('%s %s', ret.days, ret.days == 1 and 'giorno' or 'giorni'))
end
return rawTable and ret or formatResult(mw.text.listToText(ret, ', ', ' e '), self, date2, dir)
end
-- Funzione di utilità per Date:diff
local function getMagnitudine(diff, magnitudine_min)
local units = {
secondi = 0, minuti = 1, ore = 2, giorni = 3, settimane = 4, mesi = 5, anni = 6
}
local ret
if diff.seconds < 120 then -- minore di due minuti
ret = 'secondi'
elseif diff.seconds < 7200 then -- minore di due ore
ret = 'minuti'
elseif diff.seconds < 172800 then -- minore di due giorni
ret = 'ore'
elseif diff.years == 0 and diff.months < 2 then -- minore di due mesi
ret = 'giorni'
elseif diff.years < 2 then -- minore di due anni
ret = 'mesi'
else
ret = 'anni'
end
-- utilizzo di magnitudine_min (il valore minimo quando è automatica)
if magnitudine_min and units[magnitudine_min] then
ret = units[magnitudine_min] > units[ret] and magnitudine_min or ret
end
return ret
end
-- Funzione di utilità per Date:diff
local function convert(seconds, unit, text, text2)
local ret = math.floor(seconds / unit)
return ret, string.format('%s %s', ret, ret == 1 and text or text2)
end
-- Restituisce la differenza con la data d2 in solo una tra le unità:
-- anni, mesi, settimane, giorni, ore, minuti e secondi.
function Date:diff(date2, magnitudine, magnitudine_min, dir)
local diff, ret, val, result
diff = self:diffYMD(date2, true)
magnitudine = magnitudine or getMagnitudine(diff, magnitudine_min)
if magnitudine == 'secondi' then
val, result = convert(diff.seconds, 1, 'secondo', 'secondi')
elseif magnitudine == 'minuti' then
val, result = convert(diff.seconds, 60, 'minuto', 'minuti')
elseif magnitudine == 'ore' then
val, result = convert(diff.seconds, 3600, 'ora', 'ore')
elseif magnitudine == 'giorni' then
val, result = convert(diff.seconds, 86400, 'giorno', 'giorni')
elseif magnitudine == 'settimane' then
val, result = convert(diff.seconds, 604800, 'settimana', 'settimane')
elseif magnitudine == 'mesi' then
val = diff.years * 12 + diff.months
result = string.format('%s %s', val, val == 1 and 'mese' or 'mesi')
else
val = diff.years
result = string.format('%s %s', diff.years, diff.years == 1 and 'anno' or 'anni')
end
return formatResult(result, self, date2, dir, val)
end
-------------------------------------------------------------------------------
-- API
-------------------------------------------------------------------------------
local p = { Date = Date }
-- Entry point per {{#invoke:Data|diff}}
function p.diff(frame)
local args = getArgs(frame)
local success, result = xpcall(function() return parseArgs(args) end, errhandler)
return success and result.d1:diff(result.d2, args.magnitudine, args['magnitudine min'], args.dir) or result
end
-- Entry point per {{#invoke:Data|diff_ymd}}
function p.diff_ymd(frame)
local args = getArgs(frame)
local success, result = xpcall(function() return parseArgs(args) end, errhandler)
return success and result.d1:diffYMD(result.d2, false, args.dir) or result
end
-- Entry point per {{#invoke:Data|compare}}
function p.compare(frame)
local success, result = xpcall(function() return parseArgs(getArgs(frame), true) end, errhandler)
return success and (result.d1 == result.d2 and 0 or ( result.d1 < result.d2 and -1 or 1 )) or result
end
return p