En java.util.Calendarenero, 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.Calendarenero, 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.Calendary 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:
Datey Calendarcomo 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 = 12es solo (11 % 12) + 1para 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 tmestructura (definida en time.h) tiene un campo entero tm_moncon 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_monel í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.hdonde 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+1instrucciones 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.GregorianCalendartiene muchos menos errores y problemas que laold java.util.Dateclase, 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
GregorianCalendarcuá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.
GregorianCalendarestá 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));
GregorianCalendartiene 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.
DateFormatyGregorianCalendarno 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.MonthJava le proporciona otra forma de usar índices basados en 1 durante meses. Usa la java.time.Monthenumeración. Un objeto está predefinido para cada uno de los doce meses. Tienen números asignados a cada 1-12 para enero-diciembre; Llame getValuepor 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.MonthEntre 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 MonthJANUARYFEBRUARYMARCHstatic final publicsomeMethod( Month.AUGUST )
Afortunadamente, tienen una numeración sensata, 1-12 donde 1 es enero y 12 es diciembre.
Obtenga un Monthobjeto 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 Monthobjeto 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 Yeary 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.