En java.util.Calendar
, janvier est défini comme le mois 0, pas le mois 1. Y a-t-il une raison spécifique à cela?
J'ai vu beaucoup de gens devenir confus à ce sujet ...
En java.util.Calendar
, janvier est défini comme le mois 0, pas le mois 1. Y a-t-il une raison spécifique à cela?
J'ai vu beaucoup de gens devenir confus à ce sujet ...
Réponses:
Cela fait juste partie du désordre horrible qu'est l'API Java date / heure. Énumérer ce qui ne va pas prendrait beaucoup de temps (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 de toute façon.
Faites-vous plaisir et utilisez Joda Time à la place, ou peut - être 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 tout commencer à 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 expliquer les raisons - mais encore une fois, j'exhorte les lecteurs à ne pas trop s'inquiéter des raisons pour lesquelles de mauvaises décisions ont été prises, que de regarder toute la gamme des méchancetés java.util.Calendar
et de trouver quelque chose de mieux.
Un point qui est favorable à l'utilisation base 0 index est qu'il fait des choses comme « tableaux de noms » plus facile:
// I "know" there are 12 months
String[] monthNames = new String[12]; // and populate...
String name = monthNames[calendar.get(Calendar.MONTH)];
Bien sûr, cela échoue dès que vous obtenez un calendrier avec 13 mois ... mais au moins la taille spécifiée est le nombre de mois que vous attendez.
Ce n'est pas une bonne raison, mais c'est une raison ...
EDIT: En tant que commentaire, demande des idées sur ce que je pense ne pas être correct avec la date / le calendrier
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 la date vs heureDate.toString()
implémentation qui utilise toujours le fuseau horaire local du système (ce qui a perturbé de nombreux utilisateurs de Stack Overflow auparavant)Parce que faire des mathématiques avec des mois est beaucoup plus facile.
1 mois après décembre est janvier, mais pour comprendre cela normalement, vous devrez prendre le numéro du mois et faire des calculs
12 + 1 = 13 // What month is 13?
Je connais! 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 cela à nouveau en soustrayant 1 avant d'ajouter le mois, puis faites votre module et enfin ajoutez 1 à nouveau ... alias contourner un problème sous-jacent.
((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!
Réfléchissons maintenant au problème des 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 contournement n'est pas nécessaire.
((11 - 1 + 1) % 12) + 1 = 12
c'est juste (11 % 12) + 1
pour les mois 1 à 12, il vous suffit d'ajouter le 1 après avoir fait le modulo. Aucune magie requise.
Les langages basés sur C copient C dans une certaine mesure. La tm
structure (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 commencent les tableaux à l'index 0. Cela était donc pratique pour sortir une chaîne dans un tableau de noms de mois, avec tm_mon
comme index.
Il y a eu beaucoup de réponses à cela, mais je vais quand même donner mon avis sur le sujet. La raison derrière ce comportement étrange, comme indiqué précédemment, vient du POSIX C time.h
où les mois étaient stockés dans un int avec la plage 0-11. Pour expliquer pourquoi, regardez-le comme ceci; 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, comme janvier est le premier mois, il sera stocké comme décalage 0, le premier élément du tableau. monthname[JANUARY]
serait "January"
. Le premier mois de l'année est l'élément de tableau du premier mois.
Les numéros de jour, d'autre part, car ils n'ont pas de nom, les stocker dans un entier comme 0-30 serait source de confusion, ajouterait beaucoup d' day+1
instructions pour la sortie et, bien sûr, serait sujet à beaucoup de bugs.
Cela étant dit, l'incohérence est source de confusion, en particulier en javascript (qui a également hérité de cette "fonctionnalité"), un langage de script où cela devrait être abstrait loin de la langague.
TL; DR : Parce que les mois ont des noms et les jours du mois n'en ont pas.
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 pas pris la peine de mettre cette petite touche dans le code Java.
Probablement parce que la "structure tm" de C fait de même.
Parce que les programmeurs sont obsédés par les index basés sur 0. OK, c'est un peu plus compliqué que cela: cela a plus de sens lorsque vous travaillez avec une logique de niveau inférieur pour utiliser l'indexation basée sur 0. Mais dans l'ensemble, je m'en tiendrai à ma première phrase.
Personnellement, j'ai pris l'étrangeté de l'API du calendrier Java comme une indication que je devais me séparer de la mentalité grégorienne et essayer de programmer de manière plus agnostique à cet égard. Plus précisément, j'ai réappris à éviter les constantes codées en dur pour des choses comme des mois.
Lequel des énoncés suivants 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'énerve un peu au sujet de Joda Time - 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 obligeait les programmeurs à mal programmer.)
Pour moi, personne ne l'explique mieux que mindpro.com :
Gotchas
java.util.GregorianCalendar
a beaucoup moins de bugs et de pièges que laold java.util.Date
classe mais ce n'est toujours pas un pique-nique.S'il y avait eu des programmeurs lorsque l'heure d'été a été proposée pour la première fois, ils l'auraient opposé son veto comme insensé et intraitable. Avec l'heure d'été, il existe une ambiguïté fondamentale. À l'automne, lorsque vous réglez vos horloges d'une heure à 2 heures du matin, il y a deux instants différents, tous deux appelés 1:30 AM heure locale. Vous ne pouvez les différencier que si vous enregistrez si vous avez l'intention de passer à l'heure d'été ou à l'heure standard avec la lecture.
Malheureusement, il n'y a aucun moyen de dire
GregorianCalendar
ce que vous vouliez. Vous devez recourir à lui dire l'heure locale avec le factice UTC TimeZone pour éviter l'ambiguïté. Les programmeurs ferment généralement les yeux sur ce problème et espèrent juste que personne ne fera rien pendant cette heure.Bug du millénaire. Les bogues ne sont toujours pas sortis des classes de calendrier. Même dans JDK (Java Development Kit) 1.3, il existe un bogue de 2001. Considérez 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 le 01/01/2001 pour MST.
GregorianCalendar
est contrôlé par un géant de tas de constantes magiques int. Cette technique détruit totalement tout espoir de vérification des erreurs au moment de la compilation. Par exemple pour obtenir le mois que vous utilisezGregorianCalendar. get(Calendar.MONTH));
GregorianCalendar
a le brutGregorianCalendar.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 ajouter ensemble.
GregorianCalendar.set( year, month, day, hour, minute)
ne met pas les secondes à 0.
DateFormat
etGregorianCalendar
ne s'engrènent pas correctement. Vous devez spécifier le calendrier deux fois, une fois indirectement en tant que date.Si l'utilisateur n'a pas configuré correctement son fuseau horaire, il passera par défaut discrètement à PST ou GMT.
Dans GregorianCalendar, les mois sont numérotés à partir de janvier = 0, plutôt que 1 comme tout le monde sur la planète. Pourtant, les jours commencent à 1, tout comme les jours de la semaine avec dimanche = 1, lundi = 2,… samedi = 7. Pourtant DateFormat. l'analyse se comporte de manière traditionnelle avec janvier = 1.
java.util.Month
Java vous offre une autre façon d'utiliser des index basés sur 1 pendant des mois. Utilisez l' java.time.Month
énumération. 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; appeler getValue
le numéro.
Utilisez Month.JULY
(vous donne 7) au lieu de Calendar.JULY
(vous donne 6).
(import java.time.*;)
Month.FEBRUARY.getValue() // February → 2.
2
La réponse de Jon Skeet est correcte.
Nous avons maintenant un remplacement moderne pour ces anciennes classes de date-heure gênantes : les classes java.time .
java.time.Month
Parmi ces classes se trouve l' énumération . Une énumération contient un ou plusieurs objets prédéfinis, objets qui sont automatiquement instanciés lors du chargement de la classe. Sur nous avons une douzaine de ces objets, chacun donné un nom: , , et ainsi de suite. Chacun d'eux est une constante de classe. Vous pouvez utiliser et transmettre ces objets n'importe où dans votre code. Exemple:Month
Month
JANUARY
FEBRUARY
MARCH
static final public
someMethod( Month.AUGUST )
Heureusement, ils ont une numérotation saine, 1-12 où 1 est janvier et 12 est décembre.
Obtenez un Month
objet pour un numéro de mois particulier (1-12).
Month month = Month.of( 2 ); // 2 → February.
Dans l'autre sens, demandez à un Month
objet 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 de 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
En outre, vous devez passer des objets de cette énumération autour de votre base de code plutôt que de simples nombres entiers . Cela garantit la sécurité du type, garantit une plage de valeurs valide et rend votre code plus auto-documenté. Voir le didacticiel Oracle si vous ne connaissez pas la fonction d'énumération étonnamment puissante de Java.
Vous pouvez également trouver utiles les classes Year
et YearMonth
.
Le framework java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciens gênants hérités des classes date-heure tels que java.util.Date
, .Calendar
, et java.text.SimpleDateFormat
.
Le projet Joda-Time , désormais en mode maintenance , conseille la migration vers java.time.
Pour en savoir plus, consultez le didacticiel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .
Où obtenir les classes java.time?
Le projet ThreeTen-Extra étend java.time avec des classes supplémentaires. Ce projet est un terrain d'essai pour de futurs ajouts possibles à java.time. Vous trouverez peut - être des classes utiles ici, comme Interval
, YearWeek
, YearQuarter
et plus .
Il n'est pas exactement défini comme zéro en soi, il est défini comme Calendar.January. C'est le problème de l'utilisation des entiers comme constantes au lieu des énumérations. Calendar.January == 0.
Parce que l'écriture de la langue est plus difficile qu'il n'y paraît, et le temps de traitement 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 du temps et des fuseaux horaires - Computerphile" sur https://www.youtube.com/watch?v=-5wpm-gesOY . Ne soyez pas surpris si votre tête tombe de rire de confusion.
En plus de la réponse de DannySmurf sur la paresse, j'ajouterai que c'est pour vous encourager à utiliser les constantes, telles que Calendar.JANUARY
.
Parce que tout commence par 0. C'est un fait basique de la programmation en Java. Si une chose devait s'en écarter, cela conduirait à une confusion totale. Ne discutons pas de leur formation et ne codons pas avec eux.