En java.util.Calendar
enero, se define como el mes 0, no el mes 1. ¿Hay alguna razón específica para eso?
He visto a muchas personas confundirse con eso ...
En java.util.Calendar
enero, se define como el mes 0, no el mes 1. ¿Hay alguna razón específica para eso?
He visto a muchas personas confundirse con eso ...
Respuestas:
Es solo parte del desorden horrendo que es la API de fecha / hora de Java. Enumerar lo que está mal llevaría mucho tiempo (y estoy seguro de que no sé la mitad de los problemas). Es cierto que trabajar con fechas y horas es complicado, pero aaargh de todos modos.
Hazte un favor y usa Joda Time en su lugar, o posiblemente JSR-310 .
EDITAR: En cuanto a las razones por las cuales, como se señaló en otras respuestas, bien podría deberse a las antiguas API de C, o simplemente a una sensación general de comenzar todo desde 0 ... excepto que los días comienzan con 1, por supuesto. Dudo si alguien fuera del equipo de implementación original realmente podría explicar las razones, pero nuevamente, insto a los lectores a que no se preocupen tanto por las malas decisiones que se tomaron, sino que observen toda la gama de maldad java.util.Calendar
y encuentren algo mejor.
Un punto que está a favor de usar índices basados en 0 es que facilita cosas como "matrices de nombres":
// I "know" there are 12 months
String[] monthNames = new String[12]; // and populate...
String name = monthNames[calendar.get(Calendar.MONTH)];
Por supuesto, esto falla tan pronto como obtienes un calendario con 13 meses ... pero al menos el tamaño especificado es la cantidad de meses que esperas.
Esta no es una buena razón, pero es una razón ...
EDITAR: Como comentario, solicita algunas ideas sobre lo que creo que está mal con Fecha / Calendario:
Date
y Calendar
como cosas diferentes, pero falta la separación de valores "locales" frente a "divididos en zonas", como es la fecha / hora frente a fecha frente a horaDate.toString()
implementación que siempre usa la zona horaria local del sistema (eso ha confundido a muchos usuarios de Stack Overflow antes)Porque hacer matemáticas con meses es mucho más fácil.
1 mes después de diciembre es enero, pero para resolver esto normalmente tendrías que tomar el número del mes y hacer cálculos
12 + 1 = 13 // What month is 13?
¡Lo sé! Puedo arreglar esto rápidamente usando un módulo de 12.
(12 + 1) % 12 = 1
Esto funciona bien durante 11 meses hasta noviembre ...
(11 + 1) % 12 = 0 // What month is 0?
Puede hacer que todo esto funcione nuevamente restando 1 antes de agregar el mes, luego haga su módulo y finalmente agregue 1 nuevamente ... también conocido como evitar un problema subyacente.
((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!
Ahora pensemos en el problema con los meses 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
Todos los meses funcionan igual y no es necesario un trabajo alternativo.
((11 - 1 + 1) % 12) + 1 = 12
es solo (11 % 12) + 1
para los meses 1 ... 12, solo necesita agregar el 1 después de hacer el módulo. No se requiere magia.
Los lenguajes basados en C copian C hasta cierto punto. La tm
estructura (definida en time.h
) tiene un campo entero tm_mon
con el rango (comentado) de 0-11.
Los lenguajes basados en C comienzan las matrices en el índice 0. Por lo tanto, esto era conveniente para generar una cadena en una matriz de nombres de mes, con tm_mon
el índice.
Ha habido muchas respuestas a esto, pero daré mi opinión sobre el tema de todos modos. La razón detrás de este comportamiento extraño, como se indicó anteriormente, proviene del POSIX C, time.h
donde los meses se almacenaron en un int con el rango 0-11. Para explicar por qué, míralo así; Los años y los días se consideran números en el idioma hablado, pero los meses tienen sus propios nombres. Entonces, como enero es el primer mes, se almacenará como desplazamiento 0, el primer elemento de la matriz. monthname[JANUARY]
sería "January"
. El primer mes del año es el primer elemento de matriz del mes.
Los números de día, por otro lado, ya que no tienen nombres, almacenarlos en un int como 0-30 sería confuso, agregar muchas day+1
instrucciones para generar y, por supuesto, ser propensos a muchos errores.
Dicho esto, la inconsistencia es confusa, especialmente en javascript (que también ha heredado esta "característica"), un lenguaje de secuencias de comandos donde esto debería abstraerse lejos del idioma.
TL; DR : Porque los meses tienen nombres y los días del mes no.
Yo diría pereza. Las matrices comienzan en 0 (todos lo saben); los meses del año son un conjunto, lo que me lleva a creer que un ingeniero de Sun simplemente no se molestó en poner esta pequeña delicadeza en el código Java.
Probablemente porque la "estructura tm" de C hace lo mismo.
Porque los programadores están obsesionados con los índices basados en 0. OK, es un poco más complicado que eso: tiene más sentido cuando trabajas con lógica de nivel inferior para usar indexación basada en 0. Pero en general, seguiré con mi primera oración.
Personalmente, tomé la extrañeza de la API del calendario Java como una indicación de que necesitaba divorciarme de la mentalidad gregoriana y tratar de programar de manera más agnóstica en ese sentido. Específicamente, aprendí una vez más a evitar constantes codificadas para cosas como meses.
¿Cuál de los siguientes es más probable que sea correcto?
if (date.getMonth() == 3) out.print("March");
if (date.getMonth() == Calendar.MARCH) out.print("March");
Esto ilustra una cosa que me molesta un poco sobre Joda Time: puede alentar a los programadores a pensar en términos de constantes codificadas. (Sin embargo, solo un poco. No es como si Joda estuviera obligando a los programadores a programar mal).
Para mí, nadie lo explica mejor que mindpro.com :
Gotchas
java.util.GregorianCalendar
tiene muchos menos errores y problemas que laold java.util.Date
clase, pero todavía no es un día de campo.Si hubiera habido programadores cuando se propuso por primera vez el horario de verano, lo habrían vetado como una locura e intratable. Con el horario de verano, existe una ambigüedad fundamental. En el otoño, cuando retrasas tus relojes una hora a las 2 AM, hay dos instantes diferentes en el tiempo, ambos llamados 1:30 AM hora local. Puede distinguirlos solo si registra si tenía previsto el horario de verano o el horario estándar con la lectura.
Desafortunadamente, no hay forma de saber
GregorianCalendar
cuál fue su intención. Debe recurrir a decirle la hora local con la zona horaria UTC ficticia para evitar la ambigüedad. Los programadores generalmente cierran los ojos a este problema y solo esperan que nadie haga nada durante esta hora.Bicho del milenio. Los errores aún no están fuera de las clases de calendario. Incluso en JDK (Java Development Kit) 1.3 hay un error de 2001. Considere el siguiente código:
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 */
El error desaparece a las 7 a.m. del 01/01 2001 para MST.
GregorianCalendar
está controlado por un gigante de pila de constantes mágicas int sin tipo. Esta técnica destruye totalmente cualquier esperanza de verificación de errores en tiempo de compilación. Por ejemplo para obtener el mes que usasGregorianCalendar. get(Calendar.MONTH));
GregorianCalendar
tiene el crudoGregorianCalendar.get(Calendar.ZONE_OFFSET)
y el horario de veranoGregorianCalendar. get( Calendar. DST_OFFSET)
, pero no hay forma de obtener el desplazamiento de zona horaria real que se está utilizando. Debe obtener estos dos por separado y agregarlos juntos.
GregorianCalendar.set( year, month, day, hour, minute)
no establece los segundos a 0.
DateFormat
yGregorianCalendar
no encajan correctamente. Debe especificar el Calendario dos veces, una vez indirectamente como una Fecha.Si el usuario no ha configurado su zona horaria correctamente, el valor predeterminado será silenciosamente PST o GMT.
En GregorianCalendar, los meses están numerados a partir de enero = 0, en lugar de 1 como todos los demás en el planeta. Sin embargo, los días comienzan en 1 al igual que los días de la semana con domingo = 1, lunes = 2, ... sábado = 7. Sin embargo, DateFormat. parse se comporta de la manera tradicional con enero = 1.
java.util.Month
Java le proporciona otra forma de usar índices basados en 1 durante meses. Usa la java.time.Month
enumeración. Un objeto está predefinido para cada uno de los doce meses. Tienen números asignados a cada 1-12 para enero-diciembre; Llame getValue
por el número.
Utiliza Month.JULY
(Te da 7) en lugar de Calendar.JULY
(Te da 6).
(import java.time.*;)
Month.FEBRUARY.getValue() // February → 2.
2
La respuesta de Jon Skeet es correcta.
Ahora tenemos un reemplazo moderno para esas problemáticas clases antiguas de fecha y hora heredadas: las clases java.time .
java.time.Month
Entre esas clases está la enumeración . Una enumeración lleva uno o más objetos predefinidos, objetos que se instancian automáticamente cuando se carga la clase. En tenemos una docena de tales objetos, cada uno dado un nombre: , , , y así sucesivamente. Cada uno de esos es una constante de clase. Puede usar y pasar estos objetos a cualquier parte de su código. Ejemplo:Month
Month
JANUARY
FEBRUARY
MARCH
static final public
someMethod( Month.AUGUST )
Afortunadamente, tienen una numeración sensata, 1-12 donde 1 es enero y 12 es diciembre.
Obtenga un Month
objeto para un número de mes en particular (1-12).
Month month = Month.of( 2 ); // 2 → February.
Yendo en la otra dirección, solicite a un Month
objeto su número de mes.
int monthNumber = Month.FEBRUARY.getValue(); // February → 2.
Muchos otros métodos útiles en esta clase, como saber la cantidad de días en cada mes . La clase incluso puede generar un nombre localizado del mes.
Puede obtener el nombre localizado del mes, en varias longitudes o abreviaturas.
String output =
Month.FEBRUARY.getDisplayName(
TextStyle.FULL ,
Locale.CANADA_FRENCH
);
février
Además, debe pasar objetos de esta enumeración alrededor de su base de código en lugar de simples números enteros . Hacerlo proporciona seguridad de escritura, asegura un rango válido de valores y hace que su código sea más autodocumentado. Consulte el Tutorial de Oracle si no está familiarizado con la increíblemente poderosa instalación de enumeración en Java.
También puede encontrar útiles las clases Year
y YearMonth
.
El marco java.time está integrado en Java 8 y versiones posteriores. Estas clases suplantar la vieja problemáticos heredados clases de fecha y hora como java.util.Date
, .Calendar
, y java.text.SimpleDateFormat
.
El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a java.time.
Para obtener más información, consulte el Tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .
¿Dónde obtener las clases java.time?
El proyecto ThreeTen-Extra extiende java.time con clases adicionales. Este proyecto es un campo de pruebas para posibles adiciones futuras a java.time. Usted puede encontrar algunas clases útiles aquí, como Interval
, YearWeek
, YearQuarter
, y más .
No se define exactamente como cero per se, se define como Calendario. Enero. Es el problema de usar ints como constantes en lugar de enumeraciones. Calendario enero == 0.
Debido a que la escritura de idiomas es más difícil de lo que parece, y el tiempo de manejo en particular es mucho más difícil de lo que la mayoría de la gente piensa. Para una pequeña parte del problema (en realidad, no Java), vea el video de YouTube "El problema con el tiempo y las zonas horarias - Computerphile" en https://www.youtube.com/watch?v=-5wpm-gesOY . No se sorprenda si su cabeza se cae de la risa en la confusión.
Además de la respuesta de holgazanería de DannySmurf, agregaré que es para alentarlo a usar las constantes, como Calendar.JANUARY
.
Porque todo comienza con 0. Este es un hecho básico de programación en Java. Si una cosa se desviara de eso, eso conduciría a una gran confusión. No discutamos la formación de ellos y codifiquemos con ellos.