java - semanas - obtener fecha a partir de numero de semana excel
¿Cuál es una forma buena y sencilla de calcular el número de semana ISO 8601? (8)
Creo que puede usar el objeto de calendario (simplemente configure FirstDayOfWeek en Monday y MinimalDaysInFirstWeek en 4 para que cumpla con ISO 8601) y llame a get (Calendar.WEEK_OF_YEAR).
Supongamos que tengo una fecha, es decir, año, mes y día, como números enteros. ¿Cuál es un algoritmo bueno (correcto), conciso y bastante legible para calcular el número de semana ISO 8601 de la semana en la que aparece la fecha especificada? Me he encontrado con un código verdaderamente horrendo que me hace pensar que seguramente debe haber una mejor manera.
Estoy buscando hacer esto en Java pero psuedocode para cualquier tipo de lenguaje orientado a objetos está bien.
La biblioteca joda-time tiene un calendario ISO8601 y proporciona esta funcionalidad:
http://joda-time.sourceforge.net/cal_iso.html
aaaa-Www-dTHH: MM: SS.SSS Este formato de ISO8601 tiene los siguientes campos:
* four digit weekyear, see rules below * two digit week of year, from 01 to 53 * one digit day of week, from 1 to 7 where 1 is Monday and 7 is Sunday * two digit hour, from 00 to 23 * two digit minute, from 00 to 59 * two digit second, from 00 to 59 * three decimal places for milliseconds if required
Las semanas siempre son completas, y la primera semana de un año es la que incluye el primer jueves del año. Esta definición puede significar que la primera semana del año comienza el año anterior y la última semana termina el año siguiente. El campo de un año se define para referirse al año que posee la semana, que puede diferir del año real.
El resultado de todo esto es que crea un objeto DateTime y llama al getWeekOfWeekyear (), que tiene un nombre bastante confuso (pero lógico), donde un weekyear es la definición de semana específica de un año utilizado por ISO8601.
En general, joda-time es una API fantásticamente útil, he dejado de usar java.util.Calendar y java.util.Date por completo, excepto cuando necesito interactuar con una API que los usa.
esto es al revés: te da la fecha del lunes de la semana (en perl)
use POSIX qw(mktime);
use Time::localtime;
sub monday_of_week {
my $year=shift;
my $week=shift;
my $p_date=shift;
my $seconds_1_jan=mktime(0,0,0,1,0,$year-1900,0,0,0);
my $t1=localtime($seconds_1_jan);
my $seconds_for_week;
if (@$t1[6] < 5) {
#first of january is a thursday (or below)
$seconds_for_week=$seconds_1_jan+3600*24*(7*($week-1)-@$t1[6]+1);
} else {
$seconds_for_week=$seconds_1_jan+3600*24*(7*($week-1)-@$t1[6]+8);
}
my $wt=localtime($seconds_for_week);
$$p_date=sprintf("%02d/%02d/%04d",@$wt[3],@$wt[4]+1,@$wt[5]+1900);
}
/* Build a calendar suitable to extract ISO8601 week numbers
* (see http://en.wikipedia.org/wiki/ISO_8601_week_number) */
Calendar calendar = Calendar.getInstance();
calendar.setMinimalDaysInFirstWeek(4);
calendar.setFirstDayOfWeek(Calendar.MONDAY);
/* Set date */
calendar.setTime(date);
/* Get ISO8601 week number */
calendar.get(Calendar.WEEK_OF_YEAR);
Si quiere estar a la vanguardia, puede tomar la última versión de la base de código JSR-310 (Date Time API) dirigida por Stephen Colebourne (de Joda Fame). Es una interfaz fluida y es, de hecho, un rediseño de abajo hacia arriba de Joda.
La clase Calendar casi funciona, pero el año basado en la semana ISO no coincide con lo que informa un sistema compatible con el "Paquete horario de Olson". Este ejemplo de un cuadro de Linux muestra cómo un valor de año basado en una semana (2009) puede diferir del año real (2010):
$ TZ=UTC /usr/bin/date --date="2010-01-01 12:34:56" "+%a %b %d %T %Z %%Y=%Y,%%G=%G %%W=%W,%%V=%V %s"
Fri Jan 01 12:34:56 UTC %Y=2010,%G=2009 %W=00,%V=53 1262349296
Pero la clase Calendario de Java todavía informa 2010, aunque la semana del año es correcta. Las clases Joda-Time mencionadas por skaffman manejan esto correctamente:
import java.util.Calendar;
import java.util.TimeZone;
import org.joda.time.DateTime;
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTimeInMillis(1262349296 * 1000L);
cal.setMinimalDaysInFirstWeek(4);
cal.setFirstDayOfWeek(Calendar.MONDAY);
System.out.println(cal.get(Calendar.WEEK_OF_YEAR)); // %V
System.out.println(cal.get(Calendar.YEAR)); // %G
DateTime dt = new DateTime(1262349296 * 1000L);
System.out.println(dt.getWeekOfWeekyear()); // %V
System.out.println(dt.getWeekyear()); // %G
Ejecutando ese programa muestra:
53 2010 53 2009
Por lo tanto, el número de semana ISO 8601 es correcto desde el Calendario, pero el año basado en la semana no lo es.
La página man para strftime (3) informa:
%G The ISO 8601 week-based year (see NOTES) with century as a decimal number. The 4-digit year corresponding to the ISO week number (see %V). This has the same for‐ mat and value as %Y, except that if the ISO week number belongs to the previous or next year, that year is used instead. (TZ)
Solo Java.util.Calendar puede hacer el truco: puede crear una instancia de calendario y establecer el primer día de la semana y los días mínimos en la primera semana
Calendar calendar = Calendar.getInstance();
calendar.setMinimalDaysInFirstWeek(4);
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setTime(date);
// Now you are ready to take the week of year.
calendar.get(Calendar.WEEK_OF_YEAR);
Esto es proporcionado por el javaDoc
La determinación de la semana es compatible con el estándar ISO 8601 cuando getFirstDayOfWeek () es MONDAY y getMinimalDaysInFirstWeek () es 4, cuyos valores se utilizan en las configuraciones regionales donde se prefiere el estándar. Estos valores pueden establecerse explícitamente llamando a setFirstDayOfWeek () y setMinimalDaysInFirstWeek ().
tl; dr
LocalDate.of( 2015 , 12 , 30 )
.get (
IsoFields.WEEK_OF_WEEK_BASED_YEAR
)
53
…o…
org.threeten.extra.YearWeek.from (
LocalDate.of( 2015 , 12 , 30 )
)
2015-W53
java.time
El soporte para la semana ISO 8601 ahora está integrado en Java 8 y posterior, en el marco java.time . Evite las antiguas y notoriamente problemáticas clases java.util.Date/.Calendar, ya que han sido suplantadas por java.time.
Estas nuevas clases de java.time incluyen LocalDate
para el valor de solo la fecha sin hora del día o zona horaria. Tenga en cuenta que debe especificar un huso horario para determinar "hoy" ya que la fecha no es la misma en todo el mundo.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
O especifique el año, mes y día del mes como se sugiere en la Pregunta.
LocalDate localDate = LocalDate.of( year , month , dayOfMonth );
La clase IsoFields
proporciona información de acuerdo con la norma ISO 8601, incluida la semana del año para un año de una semana.
int calendarYear = now.getYear();
int weekNumber = now.get ( IsoFields.WEEK_OF_WEEK_BASED_YEAR );
int weekYear = now.get ( IsoFields.WEEK_BASED_YEAR );
Cerca del comienzo / final de un año, el año basado en la semana puede ser ± 1 diferente al año calendario. Por ejemplo, observe la diferencia entre los calendarios gregoriano e ISO 8601 para el final de 2015: las semanas 52 y 1 pasan a ser 52 y 53.
ThreeTen-Extra - YearWeek
La clase YearWeek
representa el número del año basado en la semana ISO 8601 y el número de la semana juntos como un solo objeto. Esta clase se encuentra en el proyecto ThreeTen-Extra . El proyecto agrega funcionalidad a las clases java.time integradas en Java.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
YearWeek yw = YearWeek.now( zoneId ) ;
Genera un YearWeek
partir de una fecha.
YearWeek yw = YearWeek.from (
LocalDate.of( 2015 , 12 , 30 )
)
Esta clase puede generar y analizar cadenas en formato estándar ISO 8601.
String output = yw.toString() ;
2015-W53
YearWeek yw = YearWeek.parse( "2015-W53" ) ;
Puede extraer el número de semana o el número de semana por año.
int weekNumber = yw.getWeek() ;
int weekBasedYearNumber = yw.getYear() ;
Puede generar una fecha particular ( LocalDate
) especificando el día de la semana deseado que se encontrará dentro de esa semana. Para especificar el día de la semana, use la enumeración DayOfWeek
incorporada en Java 8 y posterior.
LocalDate ld = yw.atDay( DayOfWeek.WEDNESDAY ) ;