Waarom staat januari maand 0 in Java Calendar?

In java.util.Calendarwordt januari gedefinieerd als maand 0, niet als maand 1. Is daar een specifieke reden voor?

Ik heb gezien dat veel mensen daarover in de war raakten…


Antwoord 1, autoriteit 100%

Het is slechts een deel van de verschrikkelijke puinhoop die de Java-datum/tijd-API is. Opsommen wat er mis mee is, zou erg lang duren (en ik weet zeker dat ik de helft van de problemen niet ken). Toegegeven, werken met datums en tijden is lastig, maar aaargh toch.

Doe jezelf een plezier en gebruik in plaats daarvan Joda Time, of mogelijk JSR-310.

EDIT: Wat betreft de redenen waarom – zoals opgemerkt in andere antwoorden, kan dit te wijten zijn aan oude C API’s, of gewoon een algemeen gevoel om alles vanaf 0 te beginnen … behalve dat dagen natuurlijk met 1 beginnen. Ik betwijfel of iemand buiten het oorspronkelijke implementatieteam echt redenen zou kunnen noemen – maar nogmaals, ik zou de lezers dringend willen verzoeken zich niet zozeer zorgen te maken over waaromslechte beslissingen werden genomen, maar om naar het hele scala van narigheid te kijken in java.util.Calendaren zoek iets beters.

Een punt dat isvoorstander van het gebruik van op 0 gebaseerde indexen, is dat het dingen als “reeksen van namen” gemakkelijker maakt:

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

Natuurlijk mislukt dit zodra je een kalender met 13 maanden krijgt… maar de opgegeven grootte is in ieder geval het aantal maanden dat je verwacht.

Dit is geen goedereden, maar het is eenreden…

EDIT: als een soort opmerking verzoekt u om wat ideeën over wat ik denk dat er mis is met Datum/Kalender:

  • Verrassende basissen (1900 als jaarbasis in Date, weliswaar voor verouderde constructeurs; 0 als maandbasis in beide)
  • Veranderbaarheid – het gebruik van onveranderlijke typen maakt het veeleenvoudiger om te werken met wat echt effectieve waarden
  • zijn

  • Onvoldoende aantal typen: het is fijn om Dateen Calendarals verschillende dingen te hebben,
    maar de scheiding van “lokale” versus “gezoneerde” waarden ontbreekt, evenals datum/tijd versus datum versus tijd
  • Een API die leidt tot lelijke code met magische constanten, in plaats van duidelijk benoemde methoden
  • Een API waar heel moeilijk over te redeneren is – alle zaken over wanneer dingen opnieuw worden berekend, enz.
  • Het gebruik van parameterloze constructors om standaard “nu” te gebruiken, wat leidt tot moeilijk te testen code
  • De Date.toString()-implementatie die altijd de lokale tijdzone van het systeem gebruikt (waardoor veel Stack Overflow-gebruikers tot nu toe in de war waren)

Antwoord 2, autoriteit 15%

Omdat rekenen met maanden veel gemakkelijker is.

1 maand na december is januari, maar om dit te berekenen, moet u normaal gesproken het maandnummer nemen en rekenen

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

Ik weet het! Ik kan dit snel oplossen door een modulus van 12 te gebruiken.

(12 + 1) % 12 = 1

Dit werkt prima gedurende 11 maanden tot november…

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

Je kunt dit allemaal opnieuw laten werken door 1 af te trekken voordat je de maand toevoegt, dan je modulus te doen en tenslotte weer 1 op te tellen… oftewel een onderliggend probleem omzeilen.

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

Laten we nu eens nadenken over het probleem met maanden 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

Alle maanden werken hetzelfde en een omweg is niet nodig.


Antwoord 3, autoriteit 11%

Op C gebaseerde talen kopiëren C tot op zekere hoogte. De structuur tm(gedefinieerd in time.h) heeft een geheel getalveld tm_monmet het (becommentarieerde) bereik van 0-11.

Op C gebaseerde talen beginnen arrays bij index 0. Dit was dus handig voor het uitvoeren van een string in een array van maandnamen, met tm_monals index.


Antwoord 4, autoriteit 8%

Er zijn hier veel antwoorden op gegeven, maar ik zal toch mijn mening over het onderwerp geven.
De reden achter dit vreemde gedrag, zoals eerder vermeld, komt van de POSIX C time.hwaar de maanden werden opgeslagen in een int met het bereik 0-11.
Om uit te leggen waarom, bekijk het als volgt; jaren en dagen worden in gesproken taal als getallen beschouwd, maar maanden hebben hun eigen naam. Dus omdat januari de eerste maand is, wordt deze opgeslagen als offset 0, het eerste array-element. monthname[JANUARY]zou "January"zijn. De eerste maand van het jaar is het array-element van de eerste maand.

De dagnummers aan de andere kant, aangezien ze geen namen hebben, zou het verwarrend zijn om ze in een int als 0-30 op te slaan, veel day+1instructies toevoegen voor het uitvoeren en, wees natuurlijk vatbaar voor veel bugs.

Dat gezegd hebbende, de inconsistentie is verwarrend, vooral in javascript (dat ook deze “functie” heeft geërfd), een scripttaal waar dit ver van de taal geabstraheerd zou moeten worden.

TL;DR: omdat maanden namen hebben en dagen van de maand niet.


Antwoord 5, autoriteit 3%

In Java 8 is er een nieuwe Date/Time API JSR 310die logischer is. De spec lead is dezelfde als die van de hoofdauteur van JodaTime en ze delen veel vergelijkbare concepten en patronen.


Antwoord 6, autoriteit 3%

Ik zou zeggen luiheid. Arrays beginnen bij 0 (dat weet iedereen); de maanden van het jaar zijn een reeks, wat me doet geloven dat een ingenieur bij Sun gewoon niet de moeite heeft genomen om dit kleine detail in de Java-code te zetten.


Antwoord 7, autoriteit 3%

Waarschijnlijk omdat C’s “struct tm” hetzelfde doet.


Antwoord 8, autoriteit 2%

Omdat programmeurs geobsedeerd zijn door op 0 gebaseerde indexen. OK, het is een beetje ingewikkelder dan dat: het is logischer als je werkt met logica op een lager niveau om op 0 gebaseerde indexering te gebruiken. Maar over het algemeen blijf ik bij mijn eerste zin.


Antwoord 9

Persoonlijk beschouwde ik de vreemdheid van de Java-kalender-API als een indicatie dat ik mezelf moest losmaken van de Gregoriaanse denkwijze en in dat opzicht meer agnostisch moest proberen te programmeren. In het bijzonder heb ik opnieuw geleerd om hardgecodeerde constanten te vermijden voor zaken als maanden.

Welke van de volgende is waarschijnlijker correct?

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

Dit illustreert één ding dat me een beetje irriteert aan Joda Time: het kan programmeurs aanmoedigen om te denken in termen van hardgecodeerde constanten. (Maar een klein beetje. Het is niet zo dat Joda programmeurs dwingtom slecht te programmeren.)


Antwoord 10

Voor mij legt niemand het beter uit dan mindpro.com:

Gotcha’s

java.util.GregorianCalendarheeft veel minder bugs en problemen dan de
old java.util.Dateklasse, maar het is nog steeds geen pretje.

Als er programmeurs waren geweest toen zomertijd eerst was
voorgesteld, zouden ze het als krankzinnig en hardnekkig hebben uitgesproken. Met
zomertijd, is er een fundamentele dubbelzinnigheid. In de herfst wanneer?
je zet je klok een uur terug om 2 uur ‘s nachts, er zijn twee verschillende
ogenblikken in de tijd beide genoemd 01:30 lokale tijd. Je kunt het ze vertellen
alleen uit elkaar als u vastlegt of u van plan was zomertijd of
standaardtijd met de lezing.

Helaas is er geen manier om GregorianCalendarte vertellen welke
bedoeld. Je moet je toevlucht nemen tot het vertellen van de lokale tijd met de dummy
UTC TimeZone om de dubbelzinnigheid te vermijden. Programmeurs sluiten meestal hun
ogen gericht op dit probleem en hoop maar dat niemand hier iets aan doet
uur.

Millenniumbug. De bugs zijn nog steeds niet uit de Calendar-klassen.
Zelfs in JDK (Java Development Kit) 1.3 zit een bug uit 2001. Overwegen
de volgende code:

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

De bug verdwijnt om 7 uur ‘s ochtends op 01/01/01 voor MST.

GregorianCalendarwordt bestuurd door een gigantische stapel ongetypte int
magische constanten. Deze techniek vernietigt alle hoop op
foutcontrole tijdens compileren. Bijvoorbeeld om de maand te krijgen die u gebruikt
GregorianCalendar. get(Calendar.MONTH));

GregorianCalendarheeft de onbewerkte
GregorianCalendar.get(Calendar.ZONE_OFFSET)en de zomertijd
GregorianCalendar. get( Calendar. DST_OFFSET), maar geen manier om de . te krijgen
werkelijke tijdzone-offset wordt gebruikt. Je moet deze twee apart krijgen
en voeg ze samen.

GregorianCalendar.set( year, month, day, hour, minute)wordt niet ingesteld
de seconden naar 0.

DateFormaten GregorianCalendarpassen niet goed in elkaar. Je moet
specificeer de kalender twee keer, een keer indirect als een datum.

Als de gebruiker zijn tijdzone niet correct heeft geconfigureerd, wordt deze standaard ingesteld
stil naar PST of GMT.

In GregorianCalendar worden maanden genummerd vanaf januari=0,
in plaats van 1 zoals iedereen op de planeet doet. Toch beginnen de dagen bij 1
net als dagen van de week met zondag=1, maandag=2,… zaterdag=7. Nog
Datumnotatie. parse gedraagt ​​zich op de traditionele manier met januari=1.


Antwoord 11

java.time.Month

Java biedt u een andere manier om maandenlang indexen op basis van 1 te gebruiken. Gebruik de java.time.Monthenum. Voor elk van de twaalf maanden is vooraf één object gedefinieerd. Ze hebben nummers toegewezen aan elke 1-12 voor januari-december; bel getValuevoor het nummer.

Maak gebruik van Month.JULY(geeft u 7)
in plaats van Calendar.JULY(geeft je 6).

(import java.time.*;)

Antwoord 12

De ware reden waarom

Je zou denken dat toen we het grootste deel van Date hebben afgeschaft en de nieuwe hebben toegevoegd
Kalenderklasse, we zouden de grootste ergernis van Date hebben opgelost: het feit
dat januari maand 0 is. Dat hadden we zeker moeten doen, maar helaas
wij niet. We waren bang dat programmeurs in de war zouden raken als Date
gebruikte op nul gebaseerde maanden en Kalender gebruikte op één gebaseerde maanden. en een paar
programmeurs waarschijnlijk zou zijn geweest. Maar achteraf gezien, het feit dat
Kalender is nog steeds op nul gebaseerd, heeft een enorme hoeveelheid aan
verwarring, en het was waarschijnlijk de grootste enkele fout in de Java
internationale API’s.

Geciteerd uit International Calendars in Javadoor Laura Werner, link onderaan.

Het betere alternatief: java.time

Dit is misschien een herhaling van wat anderen hebben gezegd, gooi de oude en slecht ontworpen Calendar-klasse overboord en gebruik java.time, de moderne Java-datum- en tijd-API. De maanden worden consequent doorgenummerd van 1 voor januari tot en met 12 voor december.

Als u een Calendarkrijgt van een verouderde API die nog niet is geüpgraded naar java.time, moet u deze eerst converteren naar een moderne ZonedDateTime. Afhankelijk van uw behoeften kunt u vanaf daar verdere conversies uitvoeren. In het grootste deel van de wereld zal het Calendarobject dat je krijgt vrijwel altijd een instantie zijn van de GregorianCalendarsubklasse (aangezien de Calendarklasse zelf abstract is) . Om te demonstreren:

   Calendar oldfashionedCalendarObject = Calendar.getInstance();
    ZonedDateTime zdt
            = ((GregorianCalendar) oldfashionedCalendarObject).toZonedDateTime();
    System.out.println(zdt);
    System.out.format("Month is %d or %s%n", zdt.getMonthValue(), zdt.getMonth());

Uitvoer toen ik zojuist in mijn tijdzone rende:

2021-03-17T23:18:47.761+01:00[Europe/Copenhagen]
Month is 3 or MARCH

Links


Antwoord 13

tl;dr

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

2

Details

Het Antwoord van Jon Skeetis correct.

We hebben nu een moderne vervanging voor die lastige oude verouderde datum-tijdklassen: de java.timeklassen.

java.time.Month

Een van die klassen is de Monthenum. Een enum bevat een of meer vooraf gedefinieerde objecten, objecten die automatisch worden geïnstantieerd wanneer de klasse wordt geladen. Op Monthhebben we een dozijn van dergelijke objecten, elk met een naam: JANUARY, FEBRUARY, MARCH, enzovoort. Elk daarvan is een klasseconstante static final public. U kunt deze objecten overal in uw code gebruiken en doorgeven. Voorbeeld: someMethod( Month.AUGUST )

Gelukkig hebben ze een normale nummering, 1-12 waarbij 1 januari is en 12 december.

Krijg een Monthobject voor een bepaald maandnummer (1-12).

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

Als je de andere kant op gaat, vraag een Monthobject om het maandnummer.

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

Veel andere handige methoden in deze klasse, zoals het kennen van het aantal dagen in elke maand. De klas kan zelfs genereer een gelokaliseerde naamvan de maand.

Je kunt de gelokaliseerde naam van de maand krijgen, in verschillende lengtes of afkortingen.

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

février

U moet ook objecten van deze opsomming rond uw codebasis doorgeven in plaats van louter gehele getallen. Dit zorgt voor typeveiligheid, zorgt voor een geldig waardenbereik en maakt uw code zelfdocumenterend. Zie Oracle Tutorialals u niet bekend bent met de verrassend krachtige enum-faciliteit in Java.

U vindt wellicht ook de Yearen YearMonthlessen.


Over java.time

De java.timeframework is ingebouwd in Java 8 en hoger. Deze klassen vervangen de lastige oude legacydatum-tijdklassen zoals java.util.Date, .Calendar, & java.text.SimpleDateFormat.

Het Joda-Time-project, nu in onderhoudsmodus, adviseert migratie naar java.time.

Zie de Oracle-zelfstudievoor meer informatie. En zoek Stack Overflow voor veel voorbeelden en uitleg. Specificatie is JSR 310.

Waar zijn de java.time-klassen te verkrijgen?

Het ThreeTen-Extraproject breidt java.time uit met extra klassen. Dit project is een proeftuin voor mogelijke toekomstige toevoegingen aan java.time. Mogelijk vindt u hier enkele nuttige klassen, zoals Interval, YearWeek, YearQuarteren meer.


Antwoord 14

Het is niet per se gedefinieerd als nul, het is gedefinieerd als Calendar.January. Het is het probleem van het gebruik van ints als constanten in plaats van opsommingen. Kalender.Januari == 0.


Antwoord 15

Omdat het schrijven in een taal moeilijker is dan het lijkt, en met name de verwerkingstijd een stuk moeilijker is dan de meeste mensen denken. Voor een klein deel van het probleem (in werkelijkheid, niet Java), zie de YouTube-video “The Problem with Time & Timezones – Computerphile” op https://www.youtube.com/watch?v=-5wpm-gesOY. Wees niet verbaasd als je hoofd eraf valt van het lachen in verwarring.


Antwoord 16

Naast DannySmurfs antwoord van luiheid, zal ik toevoegen dat het is om je aan te moedigen de constanten te gebruiken, zoals Calendar.JANUARY.


Antwoord 17

Omdat alles begint met 0. Dit is een basisgegeven van programmeren in Java. Als daar één ding van zou afwijken, dan zou dat tot een hele sluier van verwarring leiden. Laten we niet discussiëren over de vorming ervan en met hen coderen.

Other episodes