Operazioni su date e tempi in SQL e PHP SQL In numerosi casi, anche in temi d esame di Esame di Stato, ci si confronta con problemi in cui è necessario effettuare operazioni e confronti su/tra date o tempi o la loro combinazione. Si rende necessario quindi avere una serie di strumenti e funzioni sia in SQL che sulle pagine PHP atti a risolvere elegantemente problemi che altrimenti potrebbero prospettare soluzioni lunghe e contorte e quindi poco possibili negli stretti tempi d esame. Funzione NOW() Permette di ricavare un dato DATETIME di cui può essere ovviamente utilizzata solo la parte data o tempo, relativo al momento corrente. Per convertirlo nel classico dato (equivalente) in secondi tipico dei sistemi Unix (timestamp), si può operare così: La funzione UNIX_TIMESTAMP( ) trasforma qualunque valore data e tempo in un equivalente numero di secondi calcolato dalla solita 1/1/1970. Differenza tra date - Funzione SQL DATEDIFF( ) Uno dei problemi che frequentemente possono capitare anche in SQL è quello di dover ricavare intervalli di tempo tra due date. Esiste una funzione che facilmente permette un simile calcolo senza doversi addentrare in complicati calcoli, poco proponibili tra l altro in una query. La funzione DATEDIFF(.) acquisisce due date e permette di calcolarne la differenza in giorni. DATEDIFF(<data_recente>,<data_meno recente>) Se le date sono invertite nel loro ordine i risultati saranno dei numeri in giorni negativi. Ad esempio applicando tale operazione sulla tabella Prenotazioni del DB della simulazione di Aprile potremmo ottenere dei risultati del tipo: Pagina 1
In questo caso la query calcola la differenza in giorni tra la data della prenotazione e quella dell effettiva esecuzione del viaggio. Sottrazione tra date NON è possibile calcolare la differenza (in giorni) tra due date effettuando direttamente un differenza tra i due campi coinvolti tramite l operatore -. Infatti la query non dà errore, ma, in generale, i risultati NON sono corretti. Differenza tra tempi funzione TIMEDIFF( ) In analogia con quanto detto per le date, esiste la possibilità di calcolare la differenza in formato hh:mm:ss, tra due tempi. Questo conto viene effettuato con la funzione TIMEDIFF( ): TIMEDIFF(<tempo recente>, <tempo meno recente>) La funzione è applicabile anche a dati di tipo DATETIME, ma la sua utilità è limitata. Il risultato della funzione, come già detto, è un tempo (tipo TIME), ma esso risulta in MySQL limitato a 839:00:00, ossia 839 ore. Quindi la funzione risulta inadatta a calcolare differenze tra dati di tipo DATETIME, se essi non sono più che prossimi. Differenza tra Datetime Per effettuare una differenza su dati compositi di tipo datetime, è possibile convertire ognuno di essi in tempo Unix ossia in una quantità di secondi (prendendo come riferimento la data e tempo 1/1/1970 00:00:00) ed effettuando poi una sottrazione tra le due quantità di secondi. Per ricavare il delta tempo in ore, minuti e secondi si può ricorrere alla seguente query: Dove il numero di ore si ricava come divisione intera per 3600 sul totale dei secondi. Il numero dei minuti si ricava prendendo il resto di tale divisione e dividendolo per 60 (per determinare a quanti minuti equivalgono il numero di secondi residuo). Infine i secondi si derivano dal resto della divisione per 3600 e su tale risultato calcolando il resto della divisione per 60. Utilizzando DIV e MOD che sono gli operatori SQL per effettuare la divisione intera e il modulo (calcolo del resto). Pagina 2
Estrazione della sola data o del solo tempo da Datetime SQL possiede la capacità di estrarre da un dato Datetime, solo la parte tempo con la funzione TIME( ) o solo la parte data con la funzione DATE( ): Estrazione di ore, minuti o secondi da un time o un datetime Quando sia necessario isolare da un dato di tipo time o datetime il solo valore in ore, o quello in minuti o quello in secondi si può farlo tramite le funzioni HOUR(.), MINUTE(.), SECOND(.). Ad esempio: Estrazione di anno, mese o giorno da un dato date o datetime Quando sia necessario isolare da un dato date o datetime il solo valore relativo all anno, o quello del mese o quello del giorno di può farlo tramite le funzioni HOUR(.), MINUTE(.), SECOND(.). Ad esempio: Questo isolare componenti di una data o di un tempo può essere utile / richiesto in molteplici query d esame: Pagina 3
Media su intervalli di tempo La funzione AVG non è in grado di operare correttamente per calcolare la media su una serie di intervalli di tempo (dati in ore minuti e secondi) 1. In questo caso risulta utile ricorrere alle funzioni TIME_TO_SEC TIME_TO_SEC(<tempo in hh:mm:ss>) (trasforma un tempo strutturato in soli secondi) e SEC_TO_TIME, che è la sua inversa. In questo caso prima di effettuare la media si trasforma il tempo in secondi per poi ritrasformarlo eventualmente in normale formato orario, una volta effettuata la media sulle quantità in secondi. Ad esempio, se si chiedesse applicato al DB 15 Tennis, di individuare la media tra i tempi di prenotazione, supponendo che i tempi non siano legati a particolari regole o limitazioni, la risposta sarebbe una query del tipo: Aggiunta di certo lasso di tempo ad un datetime In alcuni casi invece di una differenza tra due date / tempi o date e tempi, si ha il problema di aggiungere ad essi una certa quantità di tempo, in h:m:s, o in giorni. In questo caso tornano utili per semplificare i calcoli le funzioni ADDTIME( ) ed ADDDATE( ). La prima aggiunge ad un datetime un certo tempo 2, ottenendone un altro: Un'altra possibilità è che si debba aggiungere una quantità di tempo in giorni. In questo caso la parte tempo non viene coinvolta nel calcolo, e rimane invariata (nell esempio 40 giorni): 1 Ma sarà possibile utilizzarla in modo del tutto normale su intervalli calcolati in giorni (delta tra date) o secondi (delta tra datetime) 2 Questo tempo non può eccedere il consueto limite di circa 840 ore. Pagina 4
In caso si debba effettuare una operazione di sottrazione di un certo numero di giorni o di un certo tempo, sarà possibile utilizzare le medesime funzioni indicate sopra, con opportuni valori negativi: PHP Problematiche analoghe a quelle incontrate in SQL, si riscontrano anche scrivendo codice PHP nelle pagine attive. PHP è un linguaggio molto ricco di funzioni e proprio per questo permette una gestione molto completa dei vari casi che si possono presentare. Per operare su tale tipo di problemi scegliamo di utilizzare una classe presente allo scopo in PHP detta Datetime. La classe ovviamente si integra bene con i formati dati presenti in MySQL. Generazione di un oggetto Datetime PHP Per operare con la classe Datetime è necessario generare oggetti appartenenti alla classe. Questo può essere fatto in modo consueto ed intuitivo: $dt = new Datetime(); in questo primo caso abbiamo generato un oggetto Datetime caricato con la data ed ora corrente. Se volessimo avere conferma di ciò dovremmo andare a stampare il contenuto dell oggetto, e ciò si fa operando tramite il metodo format( ), oltre che con la classica echo: $dt->format("y-m-d H:i:s"); Se si volesse generare un oggetto Datetime con contenuto diverso dalla data ed ora corrente, basta inserire tra le parentesi del costruttore una stringa che rappresenti la data e l ora voluta, esattamente nel formato previsto da MySQL: $dt2 = new Datetime('2019-01-01 13:30:00'); Pagina 5
Se viene inserita solo una data, viene generato un oggetto Datetime che contiene la data indicata e l ora pari a 00:00:00 (mezzanotte). Se viene inserita solo un ora, viene generato un oggetto Datetime contenente la data corrente e ora pari a quella indicata. Confronti fra date + tempo (oggetti Datetime) Una delle funzionalità molto utili di rappresentare in questo modo date, tempi o entrambi è che sarà possibile effettuare dei confronti direttamente in PHP con i consueti operatori >, <, >=, <=, ==,!=. Ad esempio la serie di istruzioni: $dt1 = new Datetime(); $dt2 = new Datetime(); if ($dt == $dt2) echo "Date e ora coincidenti"; Produrrà sempre la scritta indicata in quanto produrrà sempre due date + tempo uguali. Ad esempio il codice: $dt1 = new Datetime(); Sleep(5); $dt2 = new Datetime(); if ($dt1 < $dt2) echo "Prima data superiore."; produce due date + tempo in cui $dt1 è inferiore a $dt2, e questo viene rilevato dalla condizione. Ovviamente questo è molto pratico e permette di effettuare operazioni routinarie, ma altrimenti abbastanza complesse. Differenza tra date + tempo Se il problema è ricavare una differenza tra una data + tempo ed un'altra è possibile ricavare la stessa in modo assai semplice in PHP. Supponiamo il codice: $dt1 = new Datetime('2019-03-10 22:42:00'); $dt2 = new Datetime('2019-04-14 13:37:10'); $di = $dt2->diff($dt1); echo $di->format("giorni: %a Ore,min,sec: %H:%i:%s"); L oggetto che si ricava è di natura differente, vale a dire di tipo Dateinterval. Esso è preposto ad accumulare una differenza di tempo. L indicatore %a indica una differenza in giorni. Gli altri i valori di ore, minuti e secondi. La differenza risulta: Si noti che i giorni divengono correttamente 34 perché l ora nella prima data è superiore a quella della seconda. Oppure è possibile stampare i singoli dati singolarmente, accedendo ai campi dell oggetto di tipo Dateinterval: echo $di->days. " ". $di->h. ":". $di->m. ":". $di->s; Pagina 6
Anche per questo tipo di oggetti è possibile effettuare confronti; in pratica risulta falso il confronto tra le due quantità di tempo $di e $di2, se: $dt1 = new Datetime('2019-03-10 22:42:00'); $dt2 = new Datetime('2019-04-14 13:37:10'); $di = $dt2->diff($dt1); $dt3 = new Datetime('2019-03-10 22:42:00'); $dt4 = new Datetime('2019-03-14 13:37:10'); $di2 = $dt2->diff($dt1); echo (int) ($di > $di2); che rende 0, ossia falso, in quanto infatti l intervallo di tempo espresso da $di è superiore a quello espresso da $di2. Estrazione delle singole componenti della data o ora Desiderando estrarre una sola componente della data o dell ora, si deve di nuovo far ricorso alla funzione format( ), usando un solo, ovviamente opportuno, indicatore. $dt = new Datetime('2019-03-10 22:42:00'); per isolare solo il dato ora ad esempio, come fatto precedentemente si scriverà: $dt->format( H ); Un esempio applicativo potrebbe essere: if (($di->format('h') > 8) && ($di->format('h') < 12)) echo "Mattina"; else if (($di->format('h') >= 12) && ($di->format('h') < 20)) echo "Pomeriggio"; else echo "Notte"; che renderà la scritta relativa alla parte di giornata espressa dal valore dell ora. Pagina 7