In java.util.Calendar
wordt 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.Calendar
en 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
- Onvoldoende aantal typen: het is fijn om
Date
enCalendar
als 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)
zijn
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_mon
met 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_mon
als 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.h
waar 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+1
instructies 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.GregorianCalendar
heeft veel minder bugs en problemen dan de
old java.util.Date
klasse, 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
GregorianCalendar
te 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.
GregorianCalendar
wordt 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));
GregorianCalendar
heeft 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.
DateFormat
enGregorianCalendar
passen 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.Month
enum. Voor elk van de twaalf maanden is vooraf één object gedefinieerd. Ze hebben nummers toegewezen aan elke 1-12 voor januari-december; bel getValue
voor 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 Calendar
krijgt 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 Calendar
object dat je krijgt vrijwel altijd een instantie zijn van de GregorianCalendar
subklasse (aangezien de Calendar
klasse 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
- Internationale kalenders in Javadoor Laura Werner
- Oracle-tutorial: Datum Tijdmet uitleg over het gebruik van java.time.
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 Month
enum. Een enum bevat een of meer vooraf gedefinieerde objecten, objecten die automatisch worden geïnstantieerd wanneer de klasse wordt geladen. Op Month
hebben 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 Month
object voor een bepaald maandnummer (1-12).
Month month = Month.of( 2 ); // 2 → February.
Als je de andere kant op gaat, vraag een Month
object 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 Year
en YearMonth
lessen.
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?
- Java SE 8en SE 9en hoger
- Ingebouwd.
- Onderdeel van de standaard Java API met een gebundelde implementatie.
- Java 9 voegt enkele kleine functies en reparaties toe.
- Java SE 6en SE 7
- Veel van de java.time-functionaliteit is teruggekoppeld naar Java 6 & 7 in ThreeTen-Backport.
- Android
- Het ThreeTenABP-project past ThreeTen- Backport(hierboven vermeld) specifiek voor Android.
- Zie Hoe te gebruiken….
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
, YearQuarter
en 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.