¿Cómo calculo el número de semana dada una fecha?

Si tengo una fecha, ¿cómo puedo calcular el número de semana para esa fecha dentro de ese año?

Por ejemplo, en 2008, del 1 de enero al 6 de enero en la semana 1 y del 7 al 13 de enero en la semana 2, entonces si mi fecha fuera el 10 de enero de 2008, mi número de semana sería 2.

Un algoritmo sería genial para comenzar y un código de muestra también me ayudaría: estoy desarrollando en C ++ en Windows.

Relacionado:

Obtener el número de semana de una fecha en MS SQL Server 2005?

Pseudocódigo:

int julian = getDayOfYear(myDate) // Jan 1 = 1, Jan 2 = 2, etc... int dow = getDayOfWeek(myDate) // Sun = 0, Mon = 1, etc... int dowJan1 = getDayOfWeek("1/1/" + thisYear) // find out first of year's day // int badWeekNum = (julian / 7) + 1 // Get our week# (wrong! Don't use this) int weekNum = ((julian + 6) / 7) // probably better. CHECK THIS LINE. (See comments.) if (dow < dowJan1) // adjust for being after Saturday of week #1 ++weekNum; return (weekNum) 

Para aclarar, este algoritmo supone que numerarás tus semanas así:

 SMTWRFS 1 2 3 <-- week #1 4 5 6 7 8 9 10 <-- week #2 [etc.] 

getDayOfWeek () y getDayOfYear () son operaciones estándar de objetos de fecha en la mayoría de los idiomas. Si el tuyo no los tiene, puedes contar hacia adelante a partir de una fecha conocida (el 1 de enero de 1970 es común), después de mirar hacia arriba para ver qué día de la semana era.

Si va a implementar sus propias rutinas de conteo de fechas, recuerde que los años que son divisibles entre 100 NO son años bisiestos, a menos que también sean divisibles entre 400. Entonces, 1900 no fue un año bisiesto, sino 2000. Si vas a trabajar mucho en el pasado, tienes que lidiar con los calendarios gregoriano y juliano, etc., mira Wikipedia para obtener mucha información sobre eso.

Este enlace habla sobre funciones de fecha / hora en Windows / C ++ en mayor detalle.

Tenga en cuenta que si bien su definición de la semana n del año es sostenible, tampoco es “la” estándar.

ISO 8601 define un estándar para la representación de fechas, horas y zonas horarias. Define las semanas que comienzan en lunes. También dice que la semana 1 de un año es la que contiene al menos 4 días del año dado. En consecuencia, los días 29, 30 y 31 de diciembre de 20xx podrían estar en la semana 1 de 20xy (donde xy = xx + 1), y el 1, 2 y 3 de enero de 20xy podrían estar en la última semana de 20xx. Además, puede haber una semana 53.

[ Agregado : tenga en cuenta que el estándar C y la función `strftime () brindan semanas que comienzan el domingo y semanas que comienzan el lunes. No está claro que el estándar C prevea el número del año de la semana 0 para las semanas basadas en el domingo. Ver también la respuesta de Emerick Rogul.]

Luego viene la interesante fase de prueba: ¿cuándo obtienes la semana 53? Una respuesta es el viernes 1 de enero de 2010, que es en 2009-W53 (como, de hecho, es el domingo 3 de enero de 2010). Del mismo modo, el sábado 1 de enero de 2005 es en 2004-W53, pero el domingo 1 de enero de 2006 es en 2005-W52.

Es un extracto de un comentario en el siguiente código, que en realidad está en Informix SPL (lenguaje de procedimiento almacenado), pero es legible, aunque probablemente no se pueda escribir, sin mucha explicación adicional. El ‘||’ operador es la operación de concatenación de cadenas SQL, y el domingo es el día 0, el lunes es el día 1, … el sábado es el día 6 de la semana. Hay notas extensas en los comentarios, incluido el texto relevante del estándar. Los comentarios de una línea comienzan ‘ -- ‘; posiblemente los comentarios multilínea comiencen con ‘ { ‘ y terminen en el siguiente ‘ } ‘.

 -- @(#)$Id: iso8601_weekday.spl,v 1.1 2001/04/03 19:34:43 jleffler Exp $ -- -- Calculate ISO 8601 Week Number for given date -- Defines procedure: iso8601_weekday(). -- Uses procedure: iso8601_weeknum(). { According to a summary of the ISO 8601:1988 standard "Data Elements and Interchange Formats -- Information Interchange -- Representation of dates and times": The week notation can also be extended by a number indicating the day of the week. For example the day 1996-12-31 which is the Tuesday (day 2) of the first week of 1997 can also be written as 1997-W01-2 or 1997W012 for applications like industrial planning where many things like shift rotations are organized per week and knowing the week number and the day of the week is more handy than knowing the day of the month. This procedure uses iso8601_weeknum() to format the YYYY-Www part of the date, and appends '-d' to the result, allowing for Informix's coding of Sunday as day 0 rather than day 7 as required by ISO 8601. } CREATE PROCEDURE iso8601_weekday(dateval DATE DEFAULT TODAY) RETURNING CHAR(10); DEFINE rv CHAR(10); DEFINE dw CHAR(4); LET dw = WEEKDAY(dateval); IF dw = 0 THEN LET dw = 7; END IF; RETURN iso8601_weeknum(dateval) || '-' || dw; END PROCEDURE; -- @(#)$Id: iso8601_weeknum.spl,v 1.1 2001/02/27 20:36:25 jleffler Exp $ -- -- Calculate ISO 8601 Week Number for given date -- Defines procedures: day_one_week_one() and iso8601_weeknum(). { According to a summary of the ISO 8601:1988 standard "Data Elements and Interchange Formats -- Information Interchange -- Representation of dates and times": In commercial and industrial applications (delivery times, production plans, etc.), especially in Europe, it is often required to refer to a week of a year. Week 01 of a year is per definition the first week which has the Thursday in this year, which is equivalent to the week which contains the fourth day of January. In other words, the first week of a new year is the week which has the majority of its days in the new year. Week 01 might also contain days from the previous year and the week before week 01 of a year is the last week (52 or 53) of the previous year even if it contains days from the new year. A week starts with Monday (day 1) and ends with Sunday (day 7). For example, the first week of the year 1997 lasts from 1996-12-30 to 1997-01-05 and can be written in standard notation as 1997-W01 or 1997W01 The week notation can also be extended by a number indicating the day of the week. For example the day 1996-12-31 which is the Tuesday (day 2) of the first week of 1997 can also be written as 1997-W01-2 or 1997W012 for applications like industrial planning where many things like shift rotations are organized per week and knowing the week number and the day of the week is more handy than knowing the day of the month. Referring to the standard itself, section 3.17 defines a calendar week: week, calendar: A seven day period within a calendar year, starting on a Monday and identified by its ordinal number within the year; the first calendar week of the year is the one that includes the first Thursday of that year. In the Gregorian calendar, this is equivalent to the week which includes 4 January. Section 5.2.3 "Date identified by Calendar week and day numbers" states: Calendar week is represented by two numeric digits. The first calendar week of a year shall be identified as 01 [...] Day of the week is represented by one decimal digit. Monday shall be identified as day 1 of any calendar week [...] Section 5.2.3.1 "Complete representation" states: When the application clearly identifies the need for a complete representation of a date identified by calendar week and day numbers, it shall be one of the alphanumeric representations as follows, where CCYY represents a calendar year, W is the week designator, ww represents the ordinal number of a calendar week within the year, and D represents the ordinal number within the calendar week. Basic format: CCYYWwwD Example: 1985W155 Extended format: CCYY-Www-D Example: 1985-W15-5 Both the summary and the formal definition are intuitively clear, but it is not obvious how to translate it into an algorithm. However, we can deal with the problem by exhaustively enumerating the seven options for the day of the week on which 1st January falls (with actual year values for concreteness): 1st January 2001 is Monday => Week 1 starts on 2001-01-01 1st January 2002 is Tuesday => Week 1 starts on 2001-12-31 1st January 2003 is Wednesday => Week 1 starts on 2002-12-30 1st January 2004 is Thursday => Week 1 starts on 2003-12-29 1st January 2010 is Friday => Week 1 starts on 2010-01-04 1st January 2005 is Saturday => Week 1 starts on 2005-01-03 1st January 2006 is Sunday => Week 1 starts on 2006-01-02 (Cross-check: 1st January 1997 was a Wednesday; the summary notes state that week 1 of 1997 started on 1996-12-30, which is consistent with the table derived for dates in the first decade of the third millennium above). When working with the Informix DATE types, bear in mind that Informix uses WEEKDAY values 0 = Sunday, 1 = Monday, 6 = Saturday. When the weekday of the first of January has the value in the LH column, you need to add the value in the RH column to the 1st of January to obtain the date of the first day of the first week of the year. Weekday Offset to 1st January 1st day of week 1 0 +1 1 0 2 -1 3 -2 4 -3 5 +3 6 +2 This can be written as MOD(11-w,7)-3 where w is the (Informix encoding of the) weekday of 1st January and the value 11 is used to ensure that no negative values are presented to the MOD operator. Hence, the expression for the date corresponding to the 1st day (Monday) of the 1st week of a given year, yyyy, is: d1w1 = MDY(1, 1, yyyy) + MOD(11 - WEEKDAY(MDY(1,1,yyyy)), 7) - 3 This expression is encapsulated in stored procedure day_one_week_one: } CREATE PROCEDURE day_one_week_one(yyyy INTEGER) RETURNING DATE; DEFINE jan1 DATE; LET jan1 = MDY(1, 1, yyyy); RETURN jan1 + MOD(11 - WEEKDAY(jan1), 7) - 3; END PROCEDURE; { Given this date d1w1, we can calculate the week number of any other date in the same year as: TRUNC((dateval - d1w1) / 7) + 1 The residual issues are ensuring that the wraparounds are correct. If the given date is earlier than the start of the first week of the year that contains it, then the date belongs to the last week of the previous year. If the given date is on or after the start of the first week of the next year, then the date belongs to the first week of the next year. Given these observations, we can write iso8601_weeknum as shown below. (Beware: iso8601_week_number() is too long for servers with the 18-character limit; so is day_one_of_week_one()). Then comes the interesting testing phase -- when do you get week 53? One answer is on Friday 1st January 2010, which is in 2009-W53 (as, indeed, is Sunday 3rd January 2010). Similarly, Saturday 1st January 2005 is in 2004-W53, but Sunday 1st January 2006 is in 2005-W52. } CREATE PROCEDURE iso8601_weeknum(dateval DATE DEFAULT TODAY) RETURNING CHAR(8); DEFINE rv CHAR(8); DEFINE yyyy CHAR(4); DEFINE ww CHAR(2); DEFINE d1w1 DATE; DEFINE tv DATE; DEFINE wn INTEGER; DEFINE yn INTEGER; -- Calculate year and week number. LET yn = YEAR(dateval); LET d1w1 = day_one_week_one(yn); IF dateval < d1w1 THEN -- Date is in early January and is in last week of prior year LET yn = yn - 1; LET d1w1 = day_one_week_one(yn); ELSE LET tv = day_one_week_one(yn + 1); IF dateval >= tv THEN -- Date is in late December and is in the first week of next year LET yn = yn + 1; LET d1w1 = tv; END IF; END IF; LET wn = TRUNC((dateval - d1w1) / 7) + 1; -- Calculation complete: yn is year number and wn is week number. -- Format result. LET yyyy = yn; IF wn < 10 THEN LET ww = '0' || wn; ELSE LET ww = wn; END IF LET rv = yyyy || '-W' || ww; RETURN rv; END PROCEDURE; 

Para completar, la función inversa también es fácil de escribir con la función day_one_week_one() anterior:

 -- @(#)$Id: ywd_date.spl,v 1.1 2012/12/29 05:13:27 jleffler Exp $ -- @(#)Create ywd_date() and ywdstr_date() stored procedures -- Convert a date in format year, week, day (ISO 8601) to DATE. -- Two variants: -- ywd_date(yyyy SMALLINT, ww SMALLINT, dd SMALLINT) RETURNING DATE; -- ywdstr_date(ywd CHAR(10)) RETURNING DATE; -- NB: If week 53 is supplied, there is no check that the year had week -- 53 (GIGO). -- NB: If year yyyy is a leap year and yyyy-01-01 falls on Wed (3) or -- Thu (4), there are 53 weeks in the year. -- NB: If year yyyy is not a leap year and yyyy-01-01 falls on Thu (4), -- there are 53 weeks in the year. CREATE PROCEDURE ywd_date(yyyy SMALLINT, ww SMALLINT, dd SMALLINT) RETURNING DATE AS date; DEFINE d DATE; -- Check ranges IF yyyy < 1 OR yyyy > 9999 OR ww < 1 OR ww > 53 OR dd < 1 OR dd > 7 THEN RETURN NULL; END IF; LET d = day_one_week_one(yyyy); LET d = d + (ww - 1) * 7 + (dd - 1); RETURN d; END PROCEDURE; -- Input: 2012-W52-5 CREATE PROCEDURE ywdstr_date(ywd CHAR(10)) RETURNING DATE AS date; DEFINE yyyy SMALLINT; DEFINE ww SMALLINT; DEFINE dd SMALLINT; LET yyyy = SUBSTR(ywd, 1, 4); LET ww = SUBSTR(ywd, 7, 2); LET dd = SUBSTR(ywd, 10, 1); RETURN ywd_date(yyyy, ww, dd); END PROCEDURE; CREATE TEMP TABLE test_dates(d DATE); INSERT INTO test_dates VALUES('2011-12-28'); INSERT INTO test_dates VALUES('2011-12-29'); INSERT INTO test_dates VALUES('2011-12-30'); INSERT INTO test_dates VALUES('2011-12-31'); INSERT INTO test_dates VALUES('2012-01-01'); INSERT INTO test_dates VALUES('2012-01-02'); INSERT INTO test_dates VALUES('2012-01-03'); INSERT INTO test_dates VALUES('2012-01-04'); INSERT INTO test_dates VALUES('2012-01-05'); INSERT INTO test_dates VALUES('2012-01-06'); INSERT INTO test_dates VALUES('2012-01-07'); SELECT d, iso8601_weeknum(d), iso8601_weekday(d), ywdstr_date(iso8601_weekday(d)) FROM test_dates ORDER BY d; 

Como se señala en los comentarios, el código aceptará una fecha de semana 53 incluso si el año solo acepta 52 semanas.

Recomiendo utilizar las funciones de tiempo de la biblioteca estándar C para calcular el número de la semana. Específicamente, la función strftime tiene especificadores para imprimir el número de semana (entre muchos otros valores) dada una fecha en formato desglosado ( struct tm ). Aquí hay un pequeño progtwig de muestra que ilustra esto:

 #include  #include  #include  int main(void) { struct tm tm; char timebuf[64]; // Zero out struct tm memset(&tm, 0, sizeof tm); // November 4, 2008 11:00 pm tm.tm_sec = 0; tm.tm_min = 0; tm.tm_hour = 23; tm.tm_mday = 4; tm.tm_mon = 10; tm.tm_year = 108; tm.tm_isdst = -1; // Call mktime to recompute tm.tm_wday and tm.tm_yday mktime(&tm); if (strftime(timebuf, sizeof timebuf, "%W", &tm) != 0) { printf("Week number is: %s\n", timebuf); } return 0; } 

El resultado de este progtwig (comstackdo con GCC en Linux y Microsoft Visual Studio 2005 SP1 en Windows) es:

 Week number is: 44 

Puede obtener más información sobre strftime aquí .

struct tm se usa para representar el “tiempo descompuesto” y tiene al menos los siguientes campos:

 int tm_sec Segundos [0,60]. 
 int tm_min Minutos [0,59]. 
 int tm_hour Hora [0,23]. 
 int tm_mday Día del mes [1,31]. 
 int tm_mon Mes del año [0,11]. 
 int tm_year Años desde 1900. 
 int tm_wday Día de la semana [0,6] (domingo = 0). 
 int tm_yday Día del año [0,365]. 
 int tm_isdst Indicador de horario de verano. 

Puede crear una struct tm desde un time_t con la función localtime ().

Puede crear un time_t a partir de una struct tm con la función mktime ().

La mejor parte de struct tm es que puedes hacer cosas como agregar 24 al miembro del mes del año y cuando llamas a mktime () obtendrás un time_t eso es 2 años en el futuro (esto funciona con cualquiera de sus miembros, entonces puede, por ejemplo, incrementar la hora por 1000 y luego obtener un tiempo_t 41 días en el futuro) …

Lo siento, soy nuevo aquí y no puedo comentar sobre la respuesta en sí, pero el pseudo código de la respuesta con la marca de verificación no es correcto compeltey.

Pseudocódigo:

 int julian = getDayOfYear(myDate) // Jan 1 = 1, Jan 2 = 2, etc... int dow = getDayOfWeek(myDate) // Sun = 0, Mon = 1, etc... int dowJan1 = getDayOfWeek("1/1/" + thisYear) // find out first of year's day int weekNum = (julian / 7) + 1 // Get our week# if (dow < dowJan1) // adjust for being after Saturday of week #1 ++weekNum; return (weekNum) 

no debe buscar el "primer día del año", sino el último día del año pasado.

 getDayOfWeek("12/31/" + thisYear-1) 

sería correcto en lugar de

 getDayOfWeek("1/1/" + thisYear) 

Si no lo hace, el último día de la semana del año pasado (como el lunes) siempre estará una semana más adelante.

Para convertir en ambas direcciones, consulte aquí: Artículo de Wikipedia sobre las fechas de la semana ISO

Mi definición que no es ISO 8601 (lo suficientemente buena para mis propósitos y rápida):

 // week number of the year // (Monday as the first day of the week) as a decimal number [00,53]. // All days in a new year preceding the first Monday are considered to be in week 0. int GetWeek(const struct tm& ts) { return (ts.tm_yday + 7 - (ts.tm_wday ? (ts.tm_wday - 1) : 6)) / 7; } 

Use gmtime o localtime para calcular los días desde el domingo (es decir, el día de la semana) y los días desde el 1 de enero (tenga en cuenta que el 1 de enero es “0” en este último).

El bit arbitrario es decidir qué día del año comienza la semana 1: por lo general, solo depende de qué día de la semana fue el 1 de enero, lo que por supuesto puede calcular a partir de las dos piezas de información de gmtime. Luego use una tabla de búsqueda para las 7 posibilidades, probablemente sea más fácil que codificar las reglas.

Por ejemplo, creo que Outlook usa el estándar de que la semana 1 es la primera semana que contiene un jueves. Entonces, si el 1 de enero es un domingo, entonces el primer día de la semana 1 es el 1 de enero o el día 0. Las posibilidades restantes son el lunes, -1; Martes, -2; Miércoles, -3; Jueves, -4; Viernes, 2; Sábado, 1.

Tenga en cuenta los números negativos: “Domingo de la semana 1” no existe en realidad en 4 de 7 casos, pero si pretendemos que fue un día atrás en el año anterior, obtendremos la respuesta correcta.

Una vez que tenga eso, el número de días entre éste y su fecha le indica el número de la semana: divida entre 7 y agregue 1.

Dicho esto, me imagino que hay una API de Windows en alguna parte que le dará el mismo número de semana que usa Outlook. Simplemente no sé qué es, y por supuesto si sus reglas de la Semana 1 son diferentes a las de Outlook, entonces probablemente no sea de mucha utilidad.

Código no probado:

 int firstdays[7] = { 0, -1, -2, -3, -4, 2, 1 }; // or some other Week 1 rule struct tm breakdown; time_t target = time_you_care_about(); _gmtime_s(&breakdown,&target); int dayofweek = breakdown.tm_wday; int dayofyear = breakdown.tm_yday; int jan1wday = (dayofweek - dayofyear) % 7; if (jan1wday < 0) jan1wday += 7; int week1first = firstdays[jan1wday]; if (dayofyear < week1first) return 0; return ((dayofyear - week1first)/7) + 1; 

Algo así, de todos modos.

Boost proporciona gregorian :: date :: week_number () ver http://www.boost.org/doc/libs/1_38_0/doc/html/boost/gregorian/date.html y http://www.boost.org/ doc / libs / 1_38_0 / boost / date_time / gregorian / greg_date.hpp .

Sin embargo, no veo la forma de obtener el número del año que coincide con el número de la semana (que puede ser diferente del año calendario para esa fecha).

Mi suposición fue que la primera semana del año puede contener hasta 7 días, como se ilustra en la respuesta de Olie. El código no maneja las culturas donde la semana comienza en otro día que el domingo y esa es una gran parte del mundo.

 tm t = ... //the date on which to find week of year int wy = -1; struct tm t1; t1.tm_year = t.tm_year; t1.tm_mday = t1.tm_mon = 1; //set to 1st of January time_t tt = mktime(&t1); //compute tm //remove days for 1st week int yd = t.tm_yday - (7 - t1.tm_wday); if(yd <= 0 ) //first week is now negative wy = 0; else wy = (int)std::ceil( (double) ( yd/7) ); //second week will be 1 
 public int GetWeekOfYear(DateTime todayDate) { int days = todayDate.DayOfYear; float result = days / 7; result=result+1; Response.Write(result.ToString()); return Convert.ToInt32(result); } 

Solo pase la fecha actual como el parámetro de esta función. Entonces obtendrás el número de la semana actual. Espero que resuelva tu problema. Cualquier sugerencia es bienvenida.

 time_t t = time(NULL); tm* timePtr = localtime(&t); double day_of_year=timePtr->tm_yday +1 ; // 1-365 int week_of_year =(int) ceill(day_of_year/7.0); 

Esta es mi solución, pero no está en C ++

 NoOfDays = (CurrentDate - YearStartDate)+1 IF NoOfDays MOD 7 = 0 Then WeekNo = INT(NoOfDays/7) ELSE WeekNo = INT(NoOfDays/7)+1 END 
 /** * @brief WeekNo * @param yr * @param mon * @param day * @param iso * @return * * Given a date, return the week number * Note. The first week of the year begins on the Monday * following the previous Thursday * Follows ISO 8601 * * Mutually equivalent definitions for week 01 are: * * the week with the year's first Thursday in it (the ISO 8601 definition) * the week with the Thursday in the period 1 – 7 January * the week starting with the Monday in the period 29 December – 4 January * the week starting with the Monday which is nearest in time to 1 January * the week ending with the Sunday in the period 4 – 10 January * the week with 4 January in it * the first week with the majority (four or more) of its days in the starting year * If 1 January is on a Monday, Tuesday, Wednesday or Thursday, it is in week 01. * If 1 January is on a Friday, Saturday or Sunday, it is part of week 52 or 53 of the previous year. * the week with the year's first working day in it (if Saturdays, Sundays, and 1 January are not working days). *** strftime has a conversion of struct tm to weeknumber. strptime fills in tm struct** * Code uses strptime, strftime functions. */ int WeekNo( int yr,int mon, int day, int iso) { struct tm tm; char format[32]; //memset(tm,0,sizeof(tm)); sprintf(format,"%d-%02d-%02d",yr,mon,day); strptime(format, "%Y-%m-%d", &tm); // structure tm is now filled in for strftime strftime(format, sizeof(format), iso? "%V":"%U", &tm); //puts(format); return atoi(format); } 

invocar como Weekno (2015,12,23,1); // Para el número de semana ISO. Weekno (2015,12,23,0) // Para el número de semana sin ISO