Pourquoi le mois de janvier est-il 0 dans le calendrier Java?


Dans java.util.Calendar, janvier est défini comme le mois 0, pas 1 mois. Y a-t-il une raison spécifique à cela ?

J'ai vu beaucoup de gens se confondre à ce sujet...

Author: Stéphane Bonniez, 2008-12-05

16 answers

C'est juste une partie du désordre horrible qui est l'API Java date/time. Énumérer ce qui ne va pas avec cela prendrait très longtemps (et je suis sûr que je ne connais pas la moitié des problèmes). Certes, travailler avec des dates et des heures est délicat, mais aaargh de toute façon.

Faites-vous une faveur et utilisez plutôt Joda Time , ou éventuellement JSR-310 .

EDIT: Quant aux raisons pour lesquelles - comme indiqué dans d'autres réponses, cela pourrait bien être dû à d'anciennes API C, ou simplement à un sentiment général de départ tout à partir de 0... sauf que les jours commencent par 1, bien sûr. Je doute que quelqu'un en dehors de l'équipe de mise en œuvre d'origine puisse vraiment énoncer des raisons - mais encore une fois, j'invite les lecteurs à ne pas s'inquiéter tant de pourquoi de mauvaises décisions ont été prises, que de regarder toute la gamme de méchanceté dans java.util.Calendar et trouver quelque chose de mieux.

Un point qui est en faveur de l'utilisation d'index basés sur 0 est que cela facilite des choses comme des "tableaux de noms":

// I "know" there are 12 months
String[] monthNames = new String[12]; // and populate...
String name = monthNames[calendar.get(Calendar.MONTH)];

Bien sûr, ceci échoue dès que vous obtenez un calendrier de 13 mois... mais au moins la taille spécifiée est le nombre de mois que vous attendez.

, Ce n'est pas un bon raison, mais c'est un raison...

EDIT: Comme un commentaire demande quelques idées sur ce que je pense être faux avec la date / Calendrier:

  • Bases surprenantes (1900 comme base de l'année en Date, certes pour les constructeurs obsolètes; 0 comme base du mois dans les deux)
  • Mutabilité-utilisation de types immuables en fait beaucoup plus simple de travailler avec ce que sont vraiment efficacement valeurs
  • Un ensemble insuffisant de types: c'est bien d'avoir Date et Calendar comme des choses différentes, mais la séparation des valeurs "locales" vs "zonées" est manquante, tout comme la date/heure vs date vs heure
  • Une API qui conduit à un code laid avec des constantes magiques, au lieu de méthodes clairement nommées
  • Une API qui est très difficile à raisonner-toutes les affaires sur le moment où les choses sont recalculées etc
  • L'utilisation de constructeurs sans paramètres par défaut à "now", ce qui conduit à un code difficile à tester
  • L'implémentation Date.toString() qui utilise toujours le fuseau horaire local du système (ce qui a confondu de nombreux utilisateurs de Stack Overflow avant maintenant)
 297
Author: Jon Skeet, 2012-11-07 11:04:45

Les langages basés sur C copient C dans une certaine mesure. La structure tm (définie dans time.h) a un champ entier tm_mon avec la plage (commentée) de 0-11.

Les langages basés sur C démarrent les tableaux à l'index 0. C'était donc pratique pour sortir une chaîne dans un tableau de noms de mois, avec tm_mon comme index.

 34
Author: stesch, 2008-12-05 16:35:42

Parce que faire des maths avec des mois est beaucoup plus facile.

1 mois après décembre est janvier, mais pour comprendre cela normalement, vous devez prendre le numéro du mois et faire des calculs

12 + 1 = 13 // What month is 13?

Je sais! Je peux résoudre ce problème rapidement en utilisant un module de 12.

(12 + 1) % 12 = 1

Cela fonctionne très bien pendant 11 mois jusqu'en novembre...

(11 + 1) % 12 = 0 // What month is 0?

Vous pouvez faire tout ce travail à nouveau en soustrayant 1 avant d'ajouter le mois, puis faites votre module et enfin ajoutez 1 à nouveau... aka travailler autour d'un problème sous-jacent.

((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!

Réfléchissons maintenant au problème avec les mois 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

Tous les mois fonctionnent de la même manière et un travail de contournement n'est pas nécessaire.

 33
Author: arucker, 2013-08-01 19:34:20

Il y a eu beaucoup de réponses à cela, mais je donnerai quand même mon point de vue sur le sujet. La raison de ce comportement étrange, comme indiqué précédemment, vient du POSIX C time.h où les mois sont stockés dans un int avec la plage 0-11. Pour expliquer pourquoi, regardez comme ça, les années et les jours sont considérés comme des nombres dans la langue parlée, mais les mois ont leurs propres noms. Donc, parce que janvier est le premier mois, il sera stocké en tant que décalage 0, le premier élément du tableau. monthname[JANUARY] est "January". Première month dans l'année est le premier élément de tableau month.

Les numéros de jour d'autre part, puisqu'ils n'ont pas de noms, les stocker dans un int comme 0-30 serait déroutant, ajouter beaucoup de day+1 instructions pour la sortie et, bien sûr, être sujet à beaucoup de bugs.

Cela étant dit, l'incohérence est déroutante, en particulier en javascript (qui a également hérité de cette "fonctionnalité"), un langage de script où cela devrait être abstrait loin du langague.

TL; DR : Parce que les mois ont des noms et les jours du mois ne le font pas.

 22
Author: piksel bitworks, 2014-03-13 18:54:21

Dans Java 8, il y a une nouvelle API Date/Heure JSR 310 qui est plus saine d'esprit. Le responsable de la spécification est le même que l'auteur principal de JodaTime et ils partagent de nombreux concepts et modèles similaires.

 10
Author: Alex Miller, 2014-05-05 02:10:57

Je dirais la paresse. Les tableaux commencent à 0 (tout le monde le sait); les mois de l'année sont un tableau, ce qui m'amène à croire qu'un ingénieur de Sun n'a tout simplement pas pris la peine de mettre cette petite finesse dans le code Java.

 9
Author: TheSmurf, 2008-12-05 16:25:13

Probablement parce que "struct tm" de C fait la même chose.

 9
Author: Paul Tomblin, 2008-12-05 16:37:16

Parce que les programmeurs sont obsédés par les index basés sur 0. OK, c'est un peu plus compliqué que cela: il est plus logique lorsque vous travaillez avec une logique de niveau inférieur d'utiliser l'indexation basée sur 0. Mais dans l'ensemble, je vais toujours rester avec ma première phrase.

 5
Author: Dinah, 2008-12-05 16:26:02

Personnellement, j'ai pris l'étrangeté de l'API Java calendar comme une indication que je devais me séparer de l'état d'esprit centré sur le grégorien et essayer de programmer de manière plus agnostique à cet égard. Plus précisément, j'ai appris une fois de plus à éviter les constantes codées en dur pour des choses comme des mois.

, Lequel est le plus susceptible d'être correct?

if (date.getMonth() == 3) out.print("March");

if (date.getMonth() == Calendar.MARCH) out.print("March");

Cela illustre une chose qui m'irrite un peu à propos du temps Joda - cela peut encourager les programmeurs à penser en termes de constantes codées en dur. (Seulement un peu, cependant. Ce n'est pas comme si Joda forçait les programmeurs à mal programmer.)

 4
Author: Paul Brinkley, 2008-12-05 17:09:57

java.util.Month

Java vous fournit une autre façon d'utiliser des index basés sur 1 pendant des mois. L'utilisation de la java.time.Month enum. Un objet est prédéfini pour chacun des douze mois. Ils ont des numéros attribués à chaque 1-12 pour janvier-décembre; appel getValue pour le nombre.

Utilisez Month.JULY (vous donne 7) au lieu de Calendar.JULY (vous donne 6).

(import java.time.*;)
 3
Author: Digital_Reality, 2017-02-21 02:12:44

Tl; dr

Month.FEBRUARY.getValue()  // February → 2.

2

Détails

La réponse de Jon Skeet est correcte.

Maintenant, nous avons un remplacement moderne pour ces anciennes classes date-heure gênantes: le java.temps classes.

java.time.Month

Entre les classes est l'Month enum. Une énumération porte un ou plusieurs objets prédéfinis, des objets qui sont automatiquement instanciés lorsque la classe se charge. Sur Month nous avons un douzaine de ces objets, chacun donné un nom: JANUARY, FEBRUARY, MARCH, et ainsi de suite. Chacun d'entre eux est une constante de classe static final public. Vous pouvez utiliser et transmettre ces objets n'importe où dans votre code. Exemple: someMethod( Month.AUGUST )

Heureusement, ils ont une numérotation saine, 1-12 où 1 est Janvier et 12 est Décembre.

Obtenez un objet Month pour un numéro de mois particulier (1-12).

Month month = Month.of( 2 );  // 2 → February.

Dans l'autre sens, demandez à un objet Month son numéro de mois.

int monthNumber = Month.FEBRUARY.getValue();  // February → 2.

Beaucoup d'autres méthodes pratiques sur cette classe, comme connaître le nombre de jours dans chaque mois. La classe peut même générer un nom localisé du mois.

Vous pouvez obtenir le nom localisé du mois, en différentes longueurs ou abréviations.

String output = 
    Month.FEBRUARY.getDisplayName( 
        TextStyle.FULL , 
        Locale.CANADA_FRENCH 
    );

Février

De plus, vous devriez passer des objets de cette énumération autour de votre base de code plutôt que de simples nombres entiers. Cela fournit une sécurité de type, garantit une plage de valeurs valide et rend votre code de plus l'auto-documentation. VoirOracle Tutorial si vous ne connaissez pas la facilité enum étonnamment puissante en Java.

Vous pouvez également trouver utile de la Year et YearMonth des classes.


À propos de java.temps

Le java.time framework est intégré à Java 8 et versions ultérieures. Ces classes permettent d'éviter la pénible vieux héritage date-heure classes telles que java.util.Date, .Calendar, & java.text.SimpleDateFormat.

Le projet Joda-Time, maintenant en mode maintenance, conseille la migration vers java.temps.

Pour en savoir plus, consultez le tutoriel Oracle. Et rechercher Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310.

Où obtenir le java.les classes de temps?

Le projet ThreeTen-Extra étend java.temps avec des cours supplémentaires. Ce projet est un terrain d'essai pour d'éventuels ajouts futurs à java.temps. Vous pouvez trouver quelques classes utiles ici comme Interval, YearWeek, YearQuarter, et plus.

 2
Author: Basil Bourque, 2018-01-26 01:59:06

Pour moi, personne ne l'explique mieux que mindpro.com:

Je ne sais pas si c'est le cas.]}

java.util.GregorianCalendar a beaucoup moins de bugs et gotchas que le old java.util.Date classe mais il n'y a toujours pas de pique-nique.

Y avait-il eu des programmeurs quand l'heure d'été était la première proposé, ils auraient opposé leur veto comme fou et intraitable. Avec l'heure d'été, il y a une ambiguïté fondamentale. À l'automne, quand vous réglez vos horloges en arrière une heure à 2 heures du matin il y en a deux différent instants dans le temps les deux ont appelé 1h30 heure locale. Vous pouvez leur dire en dehors seulement si vous enregistrez si vous avez prévu l'heure d'été ou heure normale avec la lecture.

Malheureusement, il n'y a aucun moyen de dire GregorianCalendar, qui vous destiné. Vous devez recourir à lui dire l'heure locale avec le mannequin Fuseau horaire UTC pour éviter l'ambiguïté. Les programmeurs ferment généralement leur les yeux sur ce problème et j'espère que personne ne fait rien pendant cela heure.

Bug du millénaire. Les bogues ne sont toujours pas hors des classes de calendrier. Même dans JDK (Java Development Kit) 1.3, il y a un bogue de 2001. Considérer le code suivant:

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 */

Le bug disparaît à 7H du matin le 01/01/2001 pour MST.

GregorianCalendar est contrôlé par un géant de pile de int non typé les constantes magiques. Cette technique détruit totalement tout espoir de vérification des erreurs de compilation. Par exemple, pour obtenir le mois que vous utilisez GregorianCalendar. get(Calendar.MONTH));

GregorianCalendar a le brut GregorianCalendar.get(Calendar.ZONE_OFFSET) et l'heure d'été GregorianCalendar. get( Calendar. DST_OFFSET), mais aucun moyen d'obtenir le décalage de fuseau horaire réel utilisé. Vous devez obtenir ces deux séparément et les additionner.

GregorianCalendar.set( year, month, day, hour, minute) ne définit pas les secondes à 0.

DateFormat et GregorianCalendar ne pas mesh correctement. Vous devez spécifiez le calendrier deux fois, une fois indirectement comme date.

Si l'utilisateur n'a pas configuré son fuseau horaire correctement, il sera par défaut tranquillement à PST ou GMT.

Dans GregorianCalendar, les mois sont numérotée à partir de janvier=0, plutôt que 1 comme tout le monde sur la planète fait. Pourtant les jours commencent à 1 tout comme les jours de la semaine avec Dimanche=1, Lundi=2, Saturday Samedi=7. Encore DateFormat. parse se comporte de manière traditionnelle avec January=1.

 1
Author: Edwin Dalorzo, 2012-04-29 14:26:18

En plus de la réponse de paresse de DannySmurf, j'ajouterai que c'est pour vous encourager à utiliser les constantes, telles que Calendar.JANUARY.

 0
Author: Powerlord, 2008-12-05 16:27:01

Il n'est pas exactement défini comme zéro en soi, il est défini comme Calendrier.Janvier. C'est le problème de l'utilisation des ints comme constantes au lieu des énumérations. Calendrier.Janvier == 0.

 0
Author: Pål GD, 2008-12-06 14:32:11

Parce que l'écriture de la langue est plus difficile qu'il n'y paraît, et la gestion du temps en particulier est beaucoup plus difficile que la plupart des gens ne le pensent. Pour une petite partie du problème (en réalité, pas Java), voir la vidéo YouTube "Le problème avec Time & Timezones - Computerphile" à https://www.youtube.com/watch?v=-5wpm-gesOY . Ne soyez pas surpris si votre tête tombe de rire dans la confusion.

 0
Author: Tihamer, 2018-01-09 02:16:30

Parce que tout commence par 0. C'est un fait de base de la programmation en Java. Si une chose devait s'écarter de cela, cela mènerait à toute une confusion. Ne discutons pas de leur formation et codons avec eux.

 -2
Author: Syrrus, 2015-11-02 03:10:44