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
, presenti
nella JDK 1.5, 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
, restituisce 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.
Indice
- Algoritmo intuitivo
- L'errore
- Riassumendo
- Una soluzione
- Algoritmo di test DifferenzaDate1.java
- Algoritmo di test DifferenzaDate2.java
- Altre soluzioni
- Approfondimenti
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 il giorno di cambio dall'ora solare a quella legale gs→l dura 23 ore, mentre il giorno di cambio dall'ora legale a quella solare gl→s dura 25 ore.
Ecco che la divisione fra interi produce 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 gl→s è compensata dall'ora in più del gs→l:
1 ... gls ... gsl ... 365
24 ... 24 23 24 ... 24 25 24 ... 24
Il gs→l 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 gl→s 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 gs→l. In tutti gli altri casi il calcolo è corretto.
Con la divisione fra reali sono introdotti dei decimali nella differenza fra due date quando fra di esse è compreso un gs→l, oppure un gl→s. Mentre non sono introdotti decimali se fra le due date cadono sia il gs→l che il gl→s, poichè l'ora in meno del gl→s è compensata dall'ora in più del gs→l.
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 gs→l gl→s. 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 gs→l gl→s, contengono anche un solo gs→l, oppure un solo gl→s.
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
|
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 gs→l e gl→s:
] 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
Algoritmo di test DifferenzaDate2.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
|
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 gs→l e gl→s:
] 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
Link di approfondimento
- Joda è un progetto che punta al miglioramento delle funzionalità base di Java. In particolare Joda Time rimpiazza le classi java Data e Time
- La data Giuliana è un altro sistema di rappresentazione del tempo
- Alcune informazioni, meno tecniche delle precedenti, sulla data Giuliana
- L'articolo di un collega, Daniele Cremonini, sintetizza l'approccio classico all'utilizzo delle date in Java
Invia un commento
Un commento è inviato attraverso una normalissima e-mail. Il tuo indirizzo e-mail non sarà pubblicato o diffuso.
Questo blog è moderato, per cui alcuni commenti potrebbero non essere pubblicati. I commenti sono solitamente approvati dal moderatore in uno/tre giorni.