Ing. Tarin Gamberini

Citazione

Counter

Counter

Standards

Valid XHTML 1.0!

Valid CSS!

Level A conformance icon, W3C-WAI Web Content Accessibility Guidelines 1.0

Campagne

Sito sensibilizzato, a livello AA, ad un corretto uso della e-mail

Best viewed with any browser

Get Thunderbird

Get Firefox

Use OpenOffice.org

pspad

Home > Java > Differenza fra due date

This page is under translation...

Java e la rappresentazione del tempo

Indice

Differenza fra due date

Come calcolare la differenza fra due date in Java?

Una domanda che sento porre periodicamente ed alla quale raramente è risposto con chiarezza, forse anche a causa della documentazione relativa alle classi java.util.Date e java.util.GregorianCalendar che in alcuni punti lascia un po' a desiderare.

Nella documentazione troviamo che il metodo getTime() della classe java.util.Date, oppure il metodo getTimeInMillis() della classe java.util.GregorianCalendar, ritorna il numero di millisecondi a partire dall'epoca 1 Gennaio 1970 00:00:00.000 GMT (Gregoriano). Ciò che invece è omesso è che sia getTime() che getTimeInMillis() nel ritornare il numero di millisecondi tengono conto: degli anni bisestili e dei cambi d'ora legale e solare.

Algoritmo intuitivo

Uno dei modi più intuitivi per calcolare i giorni di differenza che intercorrono fra due date è il seguente.

Definiamo le due date dallaData < allaData :

GregorianCalendar dallaData = null;
GregorianCalendar allaData = null;

convertiamole in millisecondi:

long dallaDataMilliSecondi 
 = dallaData.getTimeInMillis();

long allaDataMilliSecondi
 = allaData.getTimeInMillis();

calcoliamone la differenza:

long millisecondiFraDueDate = 
 allaDataMilliSecondi - 
 dallaDataMilliSecondi;

ed infine convertiamo i millisecondi di differenza in giorni, utilizzando la divisione fra interi:

//1 giorno = 1000*60*60*24 ms 
// = 86400000 ms
double giorniFraDueDate = 
 millisecondiFraDueDate / 86400000;

L'errore

L'errore commesso nel convertire, da millisecondi a giorni, la differenza fra le due date consiste nell'aver implicitamente supposto che ogni giorno duri 24 ore. Sotto tale ipotesi, infatti, la divisione fra interi darebbe effettivamente i giorni di differenza fra due date. In realtà l'ipotesi è errata poichè non tutti i giorni durano 24 ore. Infatti chiamati:

  • Gsl il giorno di cambio dall'ora solare a quella legale
  • Gls il giorno di cambio dall'ora legale a quella solare

osserviamo che Gls dura 23 ore, mentre Gls dura 25 ore.

Ecco che la divisione fra interi da un risultato errato nel caso i millisecondi di differenza non siano un multiplo intero di 24 ore. Invece che affermare che ogni giorno duri 24 ore è più corretto asserire che il giorno medio in un anno dura 24 ore. La media su 365 giorni da esattamente 24 ore poichè l'ora in meno del Gls è compensata dall'ora in più del Gsl:

1^        Gls          Gsl       365^
24 ... 24 23 24 ... 24 25 24 ... 24

Il Gsl del 2005 cade il 27/03 e provando a calcolare, con la divisione intera, i giorni che intercorrono dalla data 27/03/2005 alla data 28/03/2005 si ottiene il risultato giorniFraDueDate = 0.0. Risultato sorprendente per chi pensa che ogni giorno duri 24 ore. Tuttavia la divisione intera tronca le 23 ore, pari a circa 0.9 giorni, così sembra che ci siano zero giorni fra il 27 ed il 28.

Ricalcolando i giorni di differenza fra il 27/03/2005 ed il 28/03/2005 con la divisione fra reali:

//1 giorno medio = 1000*60*60*24 ms
// = 86400000 ms
double giorniFraDueDate = 
 millisecondiFraDueDate / 86400000.0;

otteniamo che giorniFraDueDate = 0.9583333333333334 pari a 23.0000000000000016 ore.

Analogamente il Gls del 2005 cade il 30/10 e provando a calcolare, con la divisione intera, i giorni che intercorrono dalla data 30/10/2005 alla data 31/10/2005 si ottiene il risultato giorniFraDueDate = 1.0. Risultato corretto per chi pensa che ogni giorno duri 24 ore.

Ricalcolando i giorni di differenza fra il 30/10/2005 ed il 31/10/2005 con la divisione fra reali otteniamo che giorniFraDueDate = 1.0416666666666667 pari a 25.0000000000000008 ore.

Riassumendo

Con la divisione fra interi nel calcolare la differenza fra due date si perde un giorno se fra di esse è compreso almeno un Gsl. In tutti gli altri casi il calcolo è corretto.

Con la divisione fra reali vengono introdotti dei decimali nella differenza fra due date quando fra di esse è compreso un Gsl, oppure un Gls. Mentre non vengono introdotti decimali se fra le due date cadono sia il Gsl che il Gls, poichè l'ora in meno del Gls è compensata dall'ora in più del Gsl.

Per generalizzare i risultati ottenuti, per la divisione fra reali, a due date appartenenti ad anni diversi si può pensare al tempo come ad un susseguirsi di periodi, nei quali vige l'ora solare (winter time) o l'ora legale (summer time), intervallati da giorni di cambio d'ora:

... oraSolare Gsl oraLegale
Gls oraSolare Gsl oraLegale ...

I giorni di differenza fra due date sono un numero intero se queste ultime appartengono ad uno stesso tipo di periodo (Solare o Legale) poiché contengono un numero pari di coppie Gsl Gls. Mentre i giorni di differenza fra due date sono un numero razionale se queste ultime appartengono a diversi tipi di periodo (Solare o Legale) poiché, oltre a contenere un numero pari di coppie Gsl Gls, contengono anche un solo Gsl, oppure un solo Gls.

Una soluzione

Una fra le soluzioni possibili è quella di arrotondare il risultato della conversione in giorni calcolato con la divisione fra reali:

//1 giorno medio = 1000*60*60*24 ms
// = 86400000 ms
double giorniFraDueDate = 
 Math.round( 
  millisecondiFraDueDate / 86400000.0 
 );

Algoritmo di test DifferenzaDate1.java

Calcola i giorni di differenza fra un giorno ed il giorno immediatamente successivo. Il calcolo è ripetuto per tutte le coppie successive di date dal 01/01/2003 alla 31/12/2006. Interessanti i risultati per i giorni Gsl e Gls:

]da      , a       ] int real round
]27/03/04, 28/03/04] 1.0 1.0  1.0
]28/03/04, 29/03/04] 0.0 0.9583333334 1.0
]29/03/04, 30/03/04] 1.0 1.0  1.0
...
]30/10/04, 31/10/04] 1.0 1.0  1.0
]31/10/04, 01/11/04] 1.0 1.0416666667 1.0
]01/11/04, 02/11/04] 1.0 1.0  1.0

Download

Scarica il codice sorgente di DifferenzaDate1.java:

jar format DifferenzaDate1.java download from taringamberini.com 3kB

  • Controlla l'integrità del file scaricato verificando il suo digest MD5: dbce5c08d1bb4826b299f2fabbdab4a7
Read the legal notes by which the material on this site is distribuited.

Algoritmo di test DifferenzaDate2.java

Calcola i giorni di differenza fra due date: la prima è fissata al 01/01/2003, la seconda parte dal giorno successivo e viene incrementata fino al 31/12/2006. Interessanti i risultati per i giorni Gsl e Gls:

]da      , a       ] int   real  round
]01/01/03, 30/03/03] 88.0  88.0  88.0
]01/01/03, 31/03/03] 88.0  88.958333333 89.0
]01/01/03, 01/04/03] 89.0  89.958333333 90.0
...
]01/01/03, 26/10/03] 297.0 297.95833333 298.0
]01/01/03, 27/10/03] 299.0 299.0 299.0
]01/01/03, 28/10/03] 300.0 300.0 300.0

Download

Scarica il codice sorgente di DifferenzaDate2.java:

jar format DifferenzaDate2.java download from taringamberini.com 3kB

  • Controlla l'integrità del file scaricato verificando il suo digest MD5: 67d74de595e4e705ad2b38a32b418151
Read the legal notes by which the material on this site is distribuited.

Approfondimenti

Per chi volesse approfondire l'argomento rimando ai link:

  • Joda (External link from taringamberini.com) è un progetto sourceforge.net che punta al miglioramento delle funzionalità base di Java. In particolare Joda Time rimpiazza le classi java Data e Time;
  • La data Giuliana (External link from taringamberini.com) è un altro sistema di rappresentazione del tempo;
  • Alcune informazioni, meno tecniche delle precedenti, sulla data Giuliana (External link from taringamberini.com);
  • L'articolo di un collega, Daniele Cremonini, sintetizza l'approccio classico (External link from taringamberini.com) all'utilizzo delle date in Java.

Copyright © 2003, 2008 Tarin Gamberini

Last updated 20/01/2008