Comprender java.util.Calendar WEEK_OF_YEAR

Estoy tratando de entender cómo funciona java.util.Calendar.get(java.util.Calendar.WEEK_OF_YEAR) , pero parece que me faltan algunos puntos.

 String time = "1998-12-31"; // year month day java.util.Calendar date = java.util.Calendar.getInstance(); date.setTime((new java.text.SimpleDateFormat("yyyy-MM-dd")).parse(time)); System.err.println("Week of year = " + date.get(java.util.Calendar.WEEK_OF_YEAR)); // Week of year = 1 Why ??? 

¿Por qué date.get(java.util.Calendar.WEEK_OF_YEAR) devuelve 1 para la última semana del año?

Además, WEEK_OF_YEAR para "1998-01-01" es 1 y para "1998-12-23" es 52.
¿Alguien tiene una explicación para este comportamiento?

Desde java.util.Calendar javadoc :

Primera semana

El calendario define una semana de siete días específica del lugar utilizando dos parámetros: el primer día de la semana y los días mínimos de la primera semana (del 1 al 7). Estos números se toman de los datos de recursos locales cuando se construye un Calendario. También se pueden especificar explícitamente a través de los métodos para establecer sus valores.

Al configurar u obtener los campos WEEK_OF_MONTH o WEEK_OF_YEAR, Calendar debe determinar la primera semana del mes o año como punto de referencia. La primera semana de un mes o año se define como el primer período de siete días que comienza en getFirstDayOfWeek () y que contiene al menos getMinimalDaysInFirstWeek () días de ese mes o año. Las semanas numeradas …, -1, 0 preceden a la primera semana; semanas numeradas 2, 3, … síguelo. Tenga en cuenta que la numeración normalizada devuelta por get () puede ser diferente. Por ejemplo, una subclase de calendario específica puede designar la semana anterior a la semana 1 de un año como la semana n del año anterior.

Por lo tanto, es específico de la configuración regional. En su caso, si la semana contiene días del año nuevo, se cuenta como la semana 1 del año nuevo.

Puede cambiar este comportamiento utilizando Calendar # setMinimalDaysInFirstWeek (int) .

tl; dr

 java.time.LocalDate.parse( "1998-12-31" ) .get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) 

53

O bien, agregue una biblioteca, y luego …

 org.threeten.extra.YearWeek.from( // Convert from a `LocalDate` object to a `YearWeek` object representing the entire week of that date's week-based year. LocalDate.parse( "1998-12-31" ) // Parse string into a `LocalDate` objects. ).getWeek() // Extract an integer number of that week of week-based-year, either 1-52 or 1-53 depending on the year. 

53

Detalles

Estoy intentando comprender cómo funciona java.util.Calendar.get (java.util.Calendar.WEEK_OF_YEAR)

¡No! Esa clase es un desastre sangriento, y es mejor dejarla en el olvido.

La respuesta de npe es correcta. En Calendar , la definición de una semana varía según la configuración regional. Una característica bien intencionada, pero confusa.

Definición de semana estándar

Hay muchas maneras de definir “una semana” y “primera semana del año”.

Sin embargo, hay una definición estándar principal : el estándar ISO 8601 . Ese estándar define las semanas del año , incluida la primera semana del año .

la semana con el primer jueves del año

Una semana estándar comienza el lunes y termina el domingo.

La semana número 1 de un año estándar basado en una semana tiene el primer jueves del año calendario.

java.time

Las clases java.time suplantaron las problemáticas clases heredadas de fecha y hora. Estas clases modernas admiten la semana ISO 8601 a través de la clase IsoFields , que contiene tres constantes que implementan TemporalField :

  • WEEK_OF_WEEK_BASED_YEAR
  • WEEK_BASED_YEAR
  • WEEK_BASED_YEARS

Llame a LocalDate::get para acceder a TemporalField .

 LocalDate ld = LocalDate.parse( "1998-12-31" ) ; int weekOfWeekBasedYear = ld.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ; int yearOfWeekBasedYear = ld.get( IsoFields.WEEK_BASED_YEAR ) ; 

ld.toString (): 1998-12-31

weekOfWebasedYear: 53

yearOfWeBasedYear: 1998

Observe que el día después, el primer día del nuevo año calendario 1999, también se encuentra en la misma semana, la semana # 53 de la semana 1998.

 LocalDate firstOf1999 = ld.plusDays( 1 ); int weekOfWeekBasedYear_FirstOf1999 = firstOf1999.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ; int yearOfWeekBasedYear_FirstOf1999 = firstOf1999.get( IsoFields.WEEK_BASED_YEAR ) ; 

firstOf1999.toString (): 1999-01-01

weekOfWeBasedYear_FirstOf1999: 53

yearOfWeekBasedYear_FirstOf1999: 1998

Formato de cadena ISO 8601

La norma ISO 8601 define un formato de texto así como un significado para los valores de la semana-año base: yyyy-Www . Para una fecha específica, agregue el día de la semana, numerado del 1 al 7, de lunes a domingo: yyyy-Www-d .

Construye una cadena de este tipo

 String outputWeek = ld.format( DateTimeFormatter.ISO_WEEK_DATE ) ; // yyyy-Www 

1998-W53

 String outputDate = outputWeek + "-" + ld.getDayOfWeek().getValue() ; // yyyy-Www-d 

1998-W53-4

YearWeek

Este trabajo es mucho más fácil si agrega la biblioteca ThreeTen-Extra a su proyecto. Luego usa la clase YearWeek .

 YearWeek yw = YearWeek.from( ld ) ; // Determine ISO 8601 week of a `LocalDate`. 

Genera la cadena estándar.

 String output = yw.toString() ; 

1998-W53

Y parse.

 YearWeek yearWeek = YearWeek.parse( "1998-W53" ) ; 

yearWeek.toString (): 1998-W53

Determine una fecha. Pase un objeto enum de java.time.DayOfWeek para el día de la semana de lunes a domingo.

 LocalDate localDate = yw.atDay( DayOfWeek.MONDAY ) ; 

localDate.toString (): 1998-12-28

Recomiendo encarecidamente agregar esta biblioteca a su proyecto. Entonces puedes pasar objetos inteligentes en lugar de tontos. Hacerlo hace que su código sea más autodocumentado, proporciona seguridad de tipo y garantiza valores válidos.


Acerca de java.time

El marco java.time está integrado en Java 8 y posterior. Estas clases suplantan a las problemáticas antiguas clases heredadas de fecha y hora como java.util.Date , Calendar y SimpleDateFormat .

El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases 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 .

Usando un controlador JDBC que cumpla con JDBC 4.2 o posterior, puede intercambiar objetos java.time directamente con su base de datos. No necesita cadenas ni clases java.sql. *.

¿Dónde obtener las clases de java.time?

  • Java SE 8 , Java SE 9 y posterior
    • Incorporado.
    • Parte de la API Java estándar con una implementación integrada.
    • Java 9 agrega algunas características y correcciones menores.
  • Java SE 6 y Java SE 7
    • Gran parte de la funcionalidad de java.time se transfiere a Java 6 y 7 en ThreeTen-Backport .
  • Androide
    • Versiones posteriores de las implementaciones del paquete de Android de las clases java.time.
    • Para Android anterior, el proyecto ThreeTenABP adapta ThreeTen-Backport (mencionado anteriormente). Consulte Cómo utilizar ThreeTenABP ….

El proyecto ThreeTen-Extra amplía java.time con clases adicionales. Este proyecto es un terreno de prueba para posibles adiciones futuras a java.time. Puede encontrar algunas clases útiles aquí, como Interval , YearWeek , YearQuarter y más .


Joda-Time

ACTUALIZACIÓN: El proyecto Joda-Time ahora está en modo de mantenimiento , y el equipo recomienda la migración a las clases java.time . Esta sección quedó intacta como historia.

El excelente marco Joda-Time usa ISO 8601 para sus valores predeterminados. Sus clases incluyen esta información de la semana del año. Joda-Time es un reemplazo popular para las clases java.util.Date y java.util.Calendar notoriamente problemáticas incluidas con Java.

Código de ejemplo

Aquí hay un código de ejemplo para obtener el primer momento del primer día de la primera semana del año de la fecha y hora actual.

Tenga en cuenta la llamada a withTimeAtStartOfDay para obtener el primer momento del día.

 DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" ); DateTime now = new DateTime( timeZone ); DateTime firstWeekStart = now.withWeekOfWeekyear(1).withDayOfWeek(1).withTimeAtStartOfDay(); DateTime firstWeekStop = firstWeekStart.plusWeeks( 1 ); Interval firstWeek = new Interval( firstWeekStart, firstWeekStop ); 

Volcado a la consola …

 System.out.println( "now: " + now ); System.out.println( "firstWeekStart: " + firstWeekStart ); System.out.println( "firstWeekStop: " + firstWeekStop ); System.out.println( "firstWeek: " + firstWeek ); 

Cuando se ejecuta …

 now: 2014-02-07T12:49:33.623+01:00 firstWeekStart: 2013-12-30T00:00:00.000+01:00 firstWeekStop: 2014-01-06T00:00:00.000+01:00 firstWeek: 2013-12-30T00:00:00.000+01:00/2014-01-06T00:00:00.000+01:00