Cosa c’è di sbagliato nell’API Date & Time di Java?

Molto spesso ricevo feedback negativi su Java Date e altre classi correlate alla data. Essendo uno sviluppatore .NET, non posso completamente (senza averli usati) capire, cosa c’è di sbagliato in loro.

Qualcuno può fare luce su questo?

Ah, la class Date Java. Forse uno dei migliori esempi di come non fare qualcosa in qualsiasi lingua, ovunque. Da dove comincio?

Leggere il JavaDoc potrebbe indurre a pensare che gli sviluppatori abbiano effettivamente delle buone idee. Prosegue sulla differenza tra UTC e GMT a lungo, nonostante il fatto che la differenza tra i due sia sostanzialmente il salto di secondi (cosa che accade molto raramente ).

Tuttavia, le decisioni di progettazione consistevano nel sprecare qualsiasi idea di essere un’API ben progettata. Ecco alcuni degli errori preferiti:

  • Nonostante sia stato progettato nell’ultimo decennio del millennio, ha un tasso di anni pari a due cifre dal 1900. Ci sono letteralmente milioni di soluzioni alternative che fanno 1900+ (o 1900-) nel mondo Java come risultato di questa decisione banale.
  • I mesi sono indicizzati a zero, per soddisfare il caso straordinariamente insolito di avere una matrice di mesi e di non vivere con un array di tredici elementi, il primo dei quali contiene un null . Di conseguenza, abbiamo 0..11 (e oggi è il mese 11 dell’anno 109). C’è un numero simile di ++ e – nei mesi per convertirli in una stringa.
  • Sono mutabili . Di conseguenza, ogni volta che si desidera restituire una data (ad esempio, come una struttura di istanze) è necessario restituire un clone di quella data anziché l’object data stesso (altrimenti le persone possono mutare la propria struttura).
  • Il Calendar , progettato per “risolvere” questo, fa effettivamente gli stessi errori. Sono ancora mutevoli.
  • Date rappresenta un DateTime , ma per rimandare a quelli in SQL land, c’è un’altra sottoclass java.sql.Date , che rappresenta un singolo giorno (sebbene senza un fuso orario associato ad esso).
  • Non ci sono TimeZone s associati ad una Date , e quindi intervalli (come un “giorno intero”) sono spesso rappresentati come mezzanotte-mezzanotte (spesso in un certo fuso orario arbitrario)

Infine, vale la pena notare che i secondi intercalati generalmente si correggono rispetto a un buon orologio di sistema che viene aggiornato con ntp entro un’ora (vedere i collegamenti seguenti). La possibilità che un sistema sia ancora attivo e funzionante nell’introduzione di due secondi bisestili (ogni sei mesi minimo, ogni pochi anni praticamente) è piuttosto improbabile, specialmente considerando il fatto che devi ridistribuire di volta in volta nuove versioni del tuo codice . Anche l’uso di un linguaggio dinamico che rigenera le classi o qualcosa di simile a un motore WAR inquinerà lo spazio della class e finirà per esaurirsi.

JSR 310 , che ha soppiantato le vecchie classi data-ora con java.time in Java 8, si giustifica nel JSR originale come segue:

2.5 Quale esigenza della comunità Java verrà affrontata dalla specifica proposta?

Attualmente Java SE ha due API separate per data e ora: java.util.Date e java.util.Calendar. Entrambe le API sono costantemente descritte come difficili da usare dagli sviluppatori Java su weblog e forum. In particolare, entrambi utilizzano un indice zero per mesi, che è una causa di molti bug. Calendario ha anche sofferto di molti bug e problemi di prestazioni nel corso degli anni, principalmente a causa della memorizzazione del suo stato in due modi diversi internamente.

Un bug classico (4639407) impediva la creazione di determinate date in un object Calendario. Si potrebbe scrivere una sequenza di codice che potrebbe creare una data in alcuni anni ma non in altri, avendo l’effetto di impedire ad alcuni utenti di inserire le date di nascita corrette. Ciò è stato causato dalla class Calendar che consente solo un guadagno in ora legale di un’ora in estate, quando storicamente era più di 2 ore nel periodo della seconda guerra mondiale. Anche se questo bug è stato risolto, se in un momento futuro un paese ha scelto di introdurre un guadagno in ora legale di oltre tre ore in estate, la class Calendar verrà nuovamente interrotta.

Anche l’API Java SE corrente soffre di ambienti multi-thread. Le classi immutabili sono note per essere intrinsecamente thread-safe in quanto il loro stato non può cambiare. Tuttavia, sia la data che il calendario sono modificabili, il che richiede che i programmatori considerino esplicitamente la clonazione e il threading. Inoltre, la mancanza di sicurezza del thread in DateTimeFormat non è ampiamente nota ed è stata la causa di molti problemi di threading difficili da rintracciare.

Oltre ai problemi con le classi che Java SE ha per datetime, non ha classi per la modellazione di altri concetti. Le date o le ore non temporali, le durate, i periodi e gli intervalli non hanno una rappresentazione di class in Java SE. Di conseguenza, gli sviluppatori usano frequentemente un int per rappresentare una durata temporale, con javadoc che specifica l’unità.

La mancanza di un modello completo di data e ora comporta inoltre che molte operazioni comuni siano più complicate di quanto dovrebbero essere. Ad esempio, il calcolo del numero di giorni tra due date è attualmente un problema particolarmente difficile.

Questo JSR affronterà il problema di un modello completo di data e ora, incluse date e orari (con e senza fusi orari), durate e periodi di tempo, intervalli, formattazione e analisi.

  • Le istanze di data sono mutabili , il che è quasi sempre inopportuno.
  • Hanno una doppia natura. Rappresentano sia un timestamp che una data del calendario. Risulta che questo è problematico quando si eseguono calcoli sulle date.
  • Le rappresentazioni numeriche dei dati del calendario sono contro-intuitive in molti casi. Ad esempio: getMonth() è a base zero, getYear() è basato su 1900 (ad esempio, l’anno 2009 è rappresentato come 109).
  • Mancano molte funzionalità che ti aspetti da una class Date .

Sento per te … come programmatore .NET, ho fatto le stesse domande, l’ora API in .NET (timespans, overloading dell’operatore) è molto comoda.

Innanzitutto, per creare una data specifica, puoi utilizzare un’API obsoleta o:

 Calendar c = Calendar.getInstance(); c.set(2000, 31, 12) 

Per sottrarre un giorno fai cose cattive come

 Date firstDate = ... Calendar c = Calendar.getInstance(); c.setTime(fistDate); c.add(Calendar.DATE,-1); Date dayAgo = c.getTime(); 

o peggio

 Date d = new Date(); Date d2 = new Date(d.getTime() - 1000*60*60*24); 

Per scoprire quanto tempo è trascorso tra due date (in giorni / settimane / mesi) … diventa ancora peggio

Tuttavia DateUtils da apache ( org.apache.commons.lang.time.DateUtils ) offre alcuni metodi convenienti e mi sono trovato a usarli solo ultimamente

Come ha scritto Brabster, Joda Time è anche una buona libreria esterna, ma Apache sembra più “comune” di ogni altra cosa …

Trovo che l’API di Java Data sia utilizzabile, per essere onesti. La maggior parte dei problemi che ho visto e sentito riguardano la verbosità, la necessità di coinvolgere più classi per fare qualcosa di utile ( Calendar , Date , DateFormat / SimpleDateFormat ) e la mancanza di semplici accessor come getDayOfWeek() .

Joda Time è un’API alternativa ben rispettata in Java e nella sezione Why Joda Time fornisce alcuni argomenti in più sul motivo per cui è un’alternativa valida che potrebbe essere di interesse.