In java.util.Calendar
, gennaio è definito come il mese 0, non il mese 1. C'è qualche motivo specifico per farlo?
Ho visto molte persone confondersi a riguardo ...
In java.util.Calendar
, gennaio è definito come il mese 0, non il mese 1. C'è qualche motivo specifico per farlo?
Ho visto molte persone confondersi a riguardo ...
Risposte:
Fa solo parte dell'orrendo casino che è l'API data / ora di Java. Elencare ciò che non va richiederebbe molto tempo (e sono sicuro di non conoscere la metà dei problemi). Certo, lavorare con date e orari è complicato, ma comunque comunque.
Fatevi un favore e usate invece Joda Time , o eventualmente JSR-310 .
EDIT: Per quanto riguarda i motivi per cui - come notato in altre risposte, potrebbe essere dovuto alle vecchie API C, o semplicemente alla sensazione generale di iniziare tutto da 0 ... tranne che i giorni iniziano con 1, ovviamente. Dubito che qualcuno al di fuori del team di implementazione originale possa davvero affermare le ragioni - ma ancora una volta, esorto i lettori a non preoccuparsi così tanto del perché sono state prese decisioni sbagliate, al fine di esaminare l'intera gamma di cattiveria java.util.Calendar
e trovare qualcosa di meglio.
Un punto, che è a favore dell'utilizzo di 0 basati su indici è che rende le cose come "array di nomi" più facile:
// I "know" there are 12 months
String[] monthNames = new String[12]; // and populate...
String name = monthNames[calendar.get(Calendar.MONTH)];
Naturalmente, questo non riesce non appena si ottiene un calendario con 13 mesi ... ma almeno la dimensione specificata è il numero di mesi previsti.
Questa non è una buona ragione, ma è una ragione ...
MODIFICA: Come commento una sorta di richiesta di alcune idee su ciò che penso sia sbagliato in Data / Calendario:
Date
e Calendar
come cose diverse, ma manca la separazione dei valori "locali" vs "suddivisi in zone", così come data / ora vs data vs oraDate.toString()
implementazione che utilizza sempre il fuso orario locale del sistema (che ha confuso molti utenti Stack Overflow prima d'ora)Perché fare matematica con i mesi è molto più semplice.
1 mese dopo dicembre è gennaio, ma per capirlo normalmente dovresti prendere il numero del mese e fare matematica
12 + 1 = 13 // What month is 13?
Lo so! Posso risolverlo rapidamente usando un modulo di 12.
(12 + 1) % 12 = 1
Funziona benissimo per 11 mesi fino a novembre ...
(11 + 1) % 12 = 0 // What month is 0?
Puoi fare di nuovo tutto questo sottraendo 1 prima di aggiungere il mese, quindi esegui il modulo e infine aggiungi di nuovo 1 ... alias aggira un problema di fondo.
((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!
Ora pensiamo al problema con mesi 0-11.
(0 + 1) % 12 = 1 // February
(1 + 1) % 12 = 2 // March
(2 + 1) % 12 = 3 // April
(3 + 1) % 12 = 4 // May
(4 + 1) % 12 = 5 // June
(5 + 1) % 12 = 6 // July
(6 + 1) % 12 = 7 // August
(7 + 1) % 12 = 8 // September
(8 + 1) % 12 = 9 // October
(9 + 1) % 12 = 10 // November
(10 + 1) % 12 = 11 // December
(11 + 1) % 12 = 0 // January
Tutti i mesi funzionano allo stesso modo e non è necessario risolvere il problema.
((11 - 1 + 1) % 12) + 1 = 12
è solo (11 % 12) + 1
per mesi 1..12 devi solo aggiungere 1 dopo aver fatto il modulo. Nessuna magia richiesta.
Le lingue basate su C copiano C in una certa misura. La tm
struttura (definita in time.h
) ha un campo intero tm_mon
con l'intervallo (commentato) di 0-11.
I linguaggi basati su C iniziano le matrici all'indice 0. Quindi questo è stato utile per emettere una stringa in una matrice di nomi di mese, con tm_mon
come indice.
Ci sono state molte risposte a questo, ma darò comunque la mia opinione sull'argomento. Il motivo dietro questo strano comportamento, come affermato in precedenza, deriva dal POSIX C in time.h
cui i mesi sono stati memorizzati in un int con l'intervallo 0-11. Per spiegare il perché, guardalo in questo modo; anni e giorni sono considerati numeri nella lingua parlata, ma i mesi hanno i loro nomi. Quindi, poiché gennaio è il primo mese, verrà archiviato come offset 0, il primo elemento dell'array. monthname[JANUARY]
sarebbe "January"
. Il primo mese dell'anno è l'elemento array del primo mese.
D'altra parte, i numeri dei giorni, dato che non hanno nomi, memorizzarli in un int come 0-30 sarebbe confuso, aggiungere molte day+1
istruzioni per l'output e, naturalmente, essere inclini a molti bug.
Detto questo, l'incoerenza è fonte di confusione, specialmente in javascript (che ha anche ereditato questa "caratteristica"), un linguaggio di scripting in cui questo dovrebbe essere sottratto lontano dalla lingua.
TL; DR : Perché i mesi hanno nomi e i giorni del mese no.
In Java 8, esiste una nuova API JSR 310 Data / Ora che è più sana. Il lead delle specifiche è lo stesso dell'autore principale di JodaTime e condividono molti concetti e modelli simili.
Direi pigrizia. Le matrici iniziano da 0 (lo sanno tutti); i mesi dell'anno sono un array, il che mi porta a credere che alcuni ingegneri della Sun non si siano preoccupati di inserire questo piccolo valore nel codice Java.
Probabilmente perché la "struct tm" di C fa lo stesso.
Perché i programmatori sono ossessionati dagli indici basati su 0. OK, è un po 'più complicato di così: ha più senso quando lavori con la logica di livello inferiore per utilizzare l'indicizzazione basata su 0. Ma nel complesso, continuerò comunque con la mia prima frase.
Personalmente, ho preso la stranezza dell'API del calendario Java come indicazione che avevo bisogno di separarmi dalla mentalità centrata sul Gregoriano e provare a programmare in modo più agnostico a tale riguardo. In particolare, ho imparato ancora una volta a evitare costanti codificate per cose come mesi.
Quale delle seguenti è più probabile che sia corretta?
if (date.getMonth() == 3) out.print("March");
if (date.getMonth() == Calendar.MARCH) out.print("March");
Questo illustra una cosa che mi infastidisce un po 'di Joda Time: può incoraggiare i programmatori a pensare in termini di costanti codificate. (Solo un po ', però. Non è come se Joda stesse forzando i programmatori a programmare male.)
Per me nessuno lo spiega meglio di mindpro.com :
Trabocchetti
java.util.GregorianCalendar
ha molti meno bug e trucchi rispetto allaold java.util.Date
classe, ma non è ancora un picnic.Se ci fossero stati programmatori quando fu proposta la prima ora legale, l'avrebbero posto come pazzo e intrattabile. Con l'ora legale c'è un'ambiguità fondamentale. In autunno quando torni indietro di un'ora alle 2 del mattino, ci sono due istanti diversi nel tempo entrambi chiamati 1:30 AM ora locale. Puoi distinguerli solo se registri se intendi l'ora legale o l'ora solare con la lettura.
Sfortunatamente, non c'è modo di dire
GregorianCalendar
quale intendevi. È necessario ricorrere a comunicare l'ora locale con il fittizio TimeCone UTC per evitare l'ambiguità. I programmatori di solito chiudono gli occhi su questo problema e sperano solo che nessuno faccia nulla durante questa ora.Bug del millennio. I bug non sono ancora fuori dalle classi di Calendar. Anche in JDK (Java Development Kit) 1.3 esiste un bug del 2001. Considera il seguente codice:
GregorianCalendar gc = new GregorianCalendar(); gc.setLenient( false ); /* Bug only manifests if lenient set false */ gc.set( 2001, 1, 1, 1, 0, 0 ); int year = gc.get ( Calendar.YEAR ); /* throws exception */
Il bug scompare alle 07:00 del 2001/01/01 per MST.
GregorianCalendar
è controllato da un gigante di pile di costanti magiche int non tipizzate. Questa tecnica distrugge totalmente ogni speranza di controllo degli errori in fase di compilazione. Ad esempio per ottenere il mese che usiGregorianCalendar. get(Calendar.MONTH));
GregorianCalendar
ha l'GregorianCalendar.get(Calendar.ZONE_OFFSET)
ora legale e l'ora legaleGregorianCalendar. get( Calendar. DST_OFFSET)
, ma non è possibile ottenere l'offset del fuso orario effettivo. È necessario ottenere questi due separatamente e sommarli.
GregorianCalendar.set( year, month, day, hour, minute)
non imposta i secondi su 0.
DateFormat
eGregorianCalendar
non si mesh correttamente. È necessario specificare il Calendario due volte, una volta indirettamente come Data.Se l'utente non ha configurato correttamente il proprio fuso orario, verrà impostato automaticamente su PST o GMT.
In GregorianCalendar, i mesi sono numerati a partire da gennaio = 0, anziché 1 come fanno tutti gli altri sul pianeta. Eppure i giorni iniziano a 1 come i giorni della settimana con domenica = 1, lunedì = 2, ... sabato = 7. Tuttavia DateFormat. L'analisi si comporta in modo tradizionale con gennaio = 1.
java.util.Month
Java offre un altro modo per utilizzare indici basati su 1 per mesi. Usa l' java.time.Month
enum. Un oggetto è predefinito per ciascuno dei dodici mesi. Hanno numeri assegnati a ciascuno 1-12 per gennaio-dicembre; chiamare getValue
il numero.
Usa Month.JULY
(Ti dà 7) invece di Calendar.JULY
(Ti dà 6).
(import java.time.*;)
Month.FEBRUARY.getValue() // February → 2.
2
La risposta di Jon Skeet è corretta.
Ora abbiamo un moderno rimpiazzo per quelle fastidiose vecchie classi di data-ora: le classi java.time .
java.time.Month
Tra quelle classi c'è l' enum . Un enum contiene uno o più oggetti predefiniti, oggetti che vengono istanziati automaticamente al caricamento della classe. Su abbiamo una dozzina di tali oggetti, ogni dato un nome: , , , e così via. Ognuna di queste è una costante di classe. Puoi usare e passare questi oggetti ovunque nel tuo codice. Esempio:Month
Month
JANUARY
FEBRUARY
MARCH
static final public
someMethod( Month.AUGUST )
Fortunatamente, hanno una numerazione sana, 1-12 dove 1 è gennaio e 12 è dicembre.
Ottieni un Month
oggetto per un determinato numero di mese (1-12).
Month month = Month.of( 2 ); // 2 → February.
Andando nella direzione opposta, chiedi a un Month
oggetto il numero del mese.
int monthNumber = Month.FEBRUARY.getValue(); // February → 2.
Molti altri metodi utili su questa classe, come conoscere il numero di giorni in ogni mese . La classe può persino generare un nome localizzato del mese.
È possibile ottenere il nome localizzato del mese, in varie lunghezze o abbreviazioni.
String output =
Month.FEBRUARY.getDisplayName(
TextStyle.FULL ,
Locale.CANADA_FRENCH
);
février
Inoltre, dovresti passare oggetti di questo enum intorno alla tua base di codice piuttosto che semplici numeri interi . In questo modo si garantisce la sicurezza dei tipi, si garantisce un intervallo di valori valido e si rende il codice più autocompattante. Vedi Oracle Tutorial se non hai familiarità con la funzione enum sorprendentemente potente di Java.
Potresti anche trovare utili le classi Year
e YearMonth
.
Il framework java.time è integrato in Java 8 e versioni successive. Queste classi soppiantare la vecchia fastidiosi legacy classi data-time come java.util.Date
, .Calendar
, e java.text.SimpleDateFormat
.
Il progetto Joda-Time , ora in modalità manutenzione , consiglia la migrazione a java.time.
Per saperne di più, consulta il tutorial Oracle . E cerca Stack Overflow per molti esempi e spiegazioni. La specifica è JSR 310 .
Dove ottenere le classi java.time?
Il progetto ThreeTen-Extra estende java.time con classi aggiuntive. Questo progetto è un banco di prova per possibili aggiunte future a java.time. Si possono trovare alcune classi utili, come per esempio Interval
, YearWeek
, YearQuarter
e altro .
Non è esattamente definito come zero di per sé, è definito come Calendar.January. È il problema dell'utilizzo di ints come costanti invece di enum. Calendar.January == 0.
Perché la scrittura della lingua è più difficile di quanto sembri, e gestire il tempo in particolare è molto più difficile di quanto la maggior parte della gente pensi. Per una piccola parte del problema (in realtà, non Java), vedi il video di YouTube "The Problem with Time & Timezones - Computerphile" su https://www.youtube.com/watch?v=-5wpm-gesOY . Non essere sorpreso se la tua testa cade dal ridere in confusione.
Oltre alla risposta di pigrizia di DannySmurf, aggiungerò che è per incoraggiarti a usare le costanti, come Calendar.JANUARY
.
Perché tutto inizia con 0. Questo è un fatto fondamentale della programmazione in Java. Se una cosa dovesse deviare da quella, ciò porterebbe a tutta una confusione. Non discutiamo della loro formazione e codifichiamo con loro.