c++ - que - ¿Cómo calculo el número de semana dada una fecha?
semana actual (14)
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?
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).
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
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.
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;
}
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
Para convertir en ambas direcciones, consulte aquí: Artículo de Wikipedia sobre las fechas de la semana ISO
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í:
S M T W R F S
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.
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 programa de muestra que ilustra esto:
#include <stdio.h>
#include <string.h>
#include <time.h>
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 programa (compilado 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 here .
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.
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.
struct tm se usa para representar el "tiempo descompuesto" y tiene al menos los siguientes campos:
int tm_sec Seconds [0,60]. int tm_min Minutes [0,59]. int tm_hour Hour [0,23]. int tm_mday Day of month [1,31]. int tm_mon Month of year [0,11]. int tm_year Years since 1900. int tm_wday Day of week [0,6] (Sunday =0). int tm_yday Day of year [0,365]. int tm_isdst Daylight Savings flag.
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) ...
/**
* @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
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);