Como faço para calcular o número da semana com uma data?

Se eu tiver uma data, como posso calcular o número da semana para essa data nesse ano?

Por exemplo, em 2008, de 1º de janeiro a 6 de janeiro, estão na semana 1 e de 7 a 13 de janeiro, na semana 2, portanto, se minha data fosse 10 de janeiro de 2008, o número da minha semana seria 2.

Um algoritmo seria ótimo para me iniciar e o código de exemplo também ajudaria – estou desenvolvendo em C ++ no Windows.

Relacionado:

Obtendo número de semana fora de uma data no MS SQL Server 2005?

Pseudo-có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 esclarecer, este algoritmo assume que você numera suas semanas assim:

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

getDayOfWeek () e getDayOfYear () são operações padrão de object de data na maioria dos idiomas. Se o seu não tiver, você pode contar a partir de alguma data conhecida (1º de janeiro de 1970 é comum), depois de olhar para cima para ver que dia da semana foi.

Se você for implementar suas próprias rotinas de contagem de datas, lembre-se de que anos que são divisíveis por 100 NÃO são anos bissextos, a menos que eles também sejam divisíveis por 400. Portanto, 1900 não foi um ano bissexto, mas 2000 foi. Se você vai trabalhar muito no passado, você tem que mexer com calendars gregorianos x julianos, etc., veja a Wikipedia para ver informações sobre isso.

Este link fala sobre funções de data / hora no Windows / C ++ em maior detalhe.

Esteja ciente de que, embora sua definição de última semana do ano seja sustentável, ela também não é a “padrão”.

A ISO 8601 define um padrão para a representação de datas, horas e fusos horários. Define semanas que começam em uma segunda-feira. Também diz que a semana 1 de um ano é aquela que contém pelo menos 4 dias do ano dado. Conseqüentemente, os dias 29, 30 e 31 de dezembro de 20xx poderiam ser na semana 1 de 20xy (onde xy = xx + 1), e os dias 1, 2 e 3 de janeiro de 20xy poderiam ser todos na última semana de 20xx. Além disso, pode haver uma semana 53.

[ Adicionado : observe que o padrão C e a function `strftime () fornecem semanas que começam no domingo, bem como semanas que começam na segunda-feira. Não está claro se o padrão C fornece o número do ano da semana 0 para as semanas de domingo. Veja também a resposta de Emerick Rogul.]

Depois vem a fase de teste interessante – quando você recebe a semana 53? Uma resposta é na sexta-feira 1 de janeiro de 2010, que é em 2009-W53 (como, de fato, é domingo 03 de janeiro de 2010). Da mesma forma, sábado 1 de janeiro de 2005 é em 2004-W53, mas domingo 01 de janeiro de 2006 é em 2005-W52.

Isso é um extrato de um comentário no código a seguir, que na verdade está no Informix SPL (Stored Procedure Language), mas é legível – embora provavelmente não seja gravável – sem muita explicação adicional. O ‘||’ operador é a operação de concatenação de cadeia SQL, e domingo é dia 0, segunda-feira é dia 1, … Sábado é o dia 6 da semana. Há notas extensas nos comentários, incluindo texto relevante do padrão. Comentários de uma linha iniciam ‘ -- ‘; possivelmente os comentários de várias linhas começam com ‘ { ‘ e terminam no próximo ‘ } ‘.

 -- @(#)$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, a function inversa também é fácil de escrever com a function day_one_week_one() acima:

 -- @(#)$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; 

Conforme observado nos comentários, o código aceitará uma data de 53 semanas mesmo que o ano aceite apenas 52 semanas.

Eu recomendo fortemente usar as funções de hora da Biblioteca Padrão C para calcular o número da semana. Especificamente, a function strftime tem especificadores para imprimir o número da semana (entre muitos outros valores) dada uma data no formato desdobrado ( struct tm ). Aqui está um pequeno programa de amostra que ilustra isso:

 #include  #include  #include  

A saída deste programa (compilada com o GCC no Linux e no Microsoft Visual Studio 2005 SP1 no Windows) é:

 Week number is: 44 

Você pode aprender mais sobre o strftime aqui .

struct tm é usado para representar o “tempo de inatividade” e tem pelo menos os seguintes campos:

 int tm_sec Segundos [0,60]. 
 int tm_min Minutos [0,59]. 
 int hora_hora_hora [0,23]. 
 int tm_mday Dia do mês [1,31]. 
 int tm_mon Mês do ano [0,11]. 
 int tm_year Anos desde 1900. 
 int tm_wday Dia da semana [0,6] (domingo = 0). 
 int tm_yday Dia do ano [0,365]. 
 int tm_isdst Sinalizador de horário de verão. 

Você pode criar um struct tm a partir de um time_t com a function localtime ().

Você pode criar um time_t a partir de uma struct tm com a function mktime ().

A melhor parte sobre o struct tm é que você pode fazer coisas como adicionar 24 ao membro do mês do ano e quando você chamar mktime (), você terá um time_t que é 2 anos no futuro (isso funciona com qualquer um dos seus membros, então você pode, por exemplo, incrementar a hora em 1000 e, em seguida, obter um time_t 41 dias no futuro) …

Desculpe, eu sou novo aqui e não posso comentar sobre a resposta em si, mas o pseudo código da resposta com a marca de seleção não está completamente correto.

Pseudo-có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) 

você não deve procurar pelo "primeiro dia do ano", mas pelo último dia do ano passado.

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

seria correto em vez de

 getDayOfWeek("1/1/" + thisYear) 

Se você não fizer isso, o último dia da semana do ano passado (como segunda-feira) seria sempre uma semana antes.

Para converter em ambas as direções, veja aqui: Artigo da Wikipédia sobre datas da semana ISO

Minha definição que não é ISO 8601 (bom o suficiente para meus propósitos e rápido):

 // 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 ou localtime para calcular os dias desde o domingo (ou seja, dia da semana) e dias desde 1º de janeiro (observe que 1º de janeiro é “0” no último).

O bit arbitrário é decidir em que dia do ano a Semana 1 começa: geralmente só depende de qual dia da semana 01 de janeiro foi, o que obviamente você pode calcular a partir das duas informações do gmtime. Em seguida, use uma consulta de tabela para as 7 possibilidades, é provavelmente mais fácil do que codificar as regras.

Por exemplo, acho que o Outlook usa o padrão que semana 1 é a primeira semana que contém uma quinta-feira. Portanto, se 1º de janeiro for um domingo, o primeiro dia da semana 1 será 1º de janeiro ou dia 0. As possibilidades restantes são segunda-feira, -1; Terça-feira -2; Quarta-feira, -3; Quinta-feira, -4; Sexta-feira, 2; Sábado, 1

Observe os números negativos: “Domingo da semana 1” na verdade não existe em 4 de 7 casos, mas se fingirmos que foi um dia atrás no ano anterior, obteremos a resposta correta.

Uma vez que você o tenha, o número de dias entre ele e sua data informa o número da semana: dividir por 7 e adicionar 1.

Dito isso, imagino que exista uma API do Windows em algum lugar que forneça o mesmo número da semana que o Outlook usa. Eu simplesmente não sei o que é, e claro, se as regras da sua Semana 1 são diferentes das do Outlook, provavelmente não é muito útil.

Código não testado:

 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 assim, de qualquer maneira.

Boost fornece gregorian :: date :: week_number () veja http://www.boost.org/doc/libs/1_38_0/doc/html/boost/gregorian/date.html e http://www.boost.org/ doc / libs / 1_38_0 / boost / date_time / gregorian / greg_date.hpp .

No entanto, não consigo encontrar uma maneira de obter o número do ano que corresponde ao número da semana (que pode ser diferente do ano civil dessa data).

Minha suposição era que a primeira semana do ano pode conter até 7 dias, conforme ilustrado na resposta de Olie. O código não lida com as culturas em que a semana começa em outro dia além do domingo e isso é uma grande parte do 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); } 

Apenas passe a data atual como o parâmetro para esta function. Então você receberá o número da semana atual. Espero que resolva o seu problema. Qualquer sugestão é muito bem vinda.

 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 é a minha solução, mas não está em 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 o número da semana ISO. Weekno (2015,12,23,0) // Para número de semana não ISO