如果我有date,那么我怎样计算当年的那个星期的数字?
例如,2008年1月1日到1月6日是第1周,1月7日到13日是第2周,如果我的date是2008年1月10日,那么我的周数是2。
一个algorithm将是伟大的,让我开始和示例代码也将有所帮助 – 我在Windows上的C ++开发。
在MS SQL Server 2005中取得一个date的星期号码?
伪代码:
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)
为了澄清,这个算法假定你这样的数周:
SMTWRFS 1 2 3 <-- week #1 4 5 6 7 8 9 10 <-- week #2 [etc.]
getDayOfWeek()和getDayOfYear()是大多数语言的标准日期对象操作。 如果你没有,那么你可以从某个已知的日期(1970年1月1日是一个普通的日期)向前看,看看星期几是哪一天。
如果你要实现你自己的日期计数程序,请记住,可以被100整除的年份不是闰年,除非它们也可以被400整除。所以1900年不是闰年,而是2000年。 如果你打算工作的时间很久,你必须惹恼格里高利和朱利安日历等,参见维基百科了解大量的信息。
这个链接更详细地介绍了Windows / C ++中的日期/时间函数。
请注意,虽然你一年的第十周的定义是成立的,但它也不是“标准”。
ISO 8601定义了表示日期,时间和时区的标准。 它定义星期一开始的星期。 它也说一年的第一周是至少包含给定年份4天的那一天。 因此,20xx年12月29日,30日和31日可能在20xy的第1周(其中xy = xx + 1),而1月20日的1日,2日和3日可能都在20xx的最后一周。 此外,可能有一周53。
[ 补充 :注意,C标准和strftime()函数提供了星期天开始的星期以及周一开始的星期。 C标准规定星期日为周的第0周的年数不明确。 另见Emerick Rogul的回答。]
然后是有趣的测试阶段 – 你什么时候获得第53周? 一个答案是在2010年1月1日星期五,这是在2009年-W53(实际上是2010年1月3日星期日)。 同样,2005年1月1日星期六是2004年-W53,而2006年1月1日星期日是2005年-W52。
这是来自以下代码中的注释的摘录,它实际上是Informix SPL(存储过程语言),但是可读 – 尽管可能不可写 – 没有更多的解释。 '||' 运算符是SQL字符串连接操作,星期日是星期几,星期一是星期一,星期六是星期六。 评论中有大量的注释,包括标准的相关文字。 一行评论开始“ --
”; 可能多行注释以“ {
”开始,并在下一个“ }
”结束。
-- @(#)$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;
为了完整性,反函数也很容易用上面的day_one_week_one()
函数来编写:
-- @(#)$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;
正如评论中指出的那样,即使年份只接受52周,代码也会接受第53周的日期。
我强烈建议使用C标准库的时间函数来计算星期数。 具体来说, strftime
函数具有说明符来打印星期数(在许多其他值中)给定日期的分解( struct tm
)格式。 这里有一个小例子来说明这一点:
#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; }
该程序的输出(在Linux上的GCC和Windows上的Microsoft Visual Studio 2005 SP1编译)是:
Week number is: 44
你可以在这里了解更多关于strftime。
struct tm用于表示“细分时间”,并至少包含以下字段:
int tm_sec秒[0,60]。 int tm_min分钟[0,59]。 int tm_hour小时[0,23]。 int tm_mday一个月中的某天[1,31]。 int tm_mon一年中的月份[0,11]。 int tm_year自1900年起。 int tm_wday星期几[0,6](星期日= 0)。 int tm_yday每年的一天[0,365]。 int tm_isdst夏令时标志。
您可以使用localtime()函数从time_t创建一个struct tm。
您可以使用mktime()函数从struct tm创建一个time_t。
关于struct tm的最好的部分是,你可以做一些事情,比如将24添加到年份成员中,当你调用mktime()时,你会得到一个在未来2年的time_t(这与其任何成员一起工作,所以例如,您可以将小时增加1000,然后在将来获得41天的时间)。
对不起,我在这里是新来的,不能评论答案本身,但从复选标记的伪代码不能强制正确。
伪代码:
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)
你不应该去看“今年的头一天”,而是去年的最后一天。
getDayOfWeek("12/31/" + thisYear-1)
会是正确的,而不是
getDayOfWeek("1/1/" + thisYear)
如果不这样做,去年的最后一个工作日(如星期一)总是会提前一个星期。
要在两个方向转换,请参阅此处: 关于ISO周日期的维基百科文章
我的定义是非ISO 8601(足够我的目的和快速):
// 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; }
使用gmtime或localtime来计算从星期天以来的天数(即星期几)和从1月1日以来的天数(请注意,1月1日是后者的“0”)。
这个任意位决定了第一周的第几天是什么时候开始的:通常这只取决于1月1日这一周的哪一天,这当然可以从gmtime的两条信息中计算出来。 然后使用表查找7个可能性,这可能比编码规则更容易。
例如,我认为Outlook使用的标准是第一周是包含星期四的第一周。 因此,如果1月1日是星期天,则第一周的第一天是1月1日或第0天。其余的可能性是星期一,-1; 星期二,-2; 星期三,-3; 星期四,四; 星期五,2; 星期六1。
注意负数:“第一周的星期天”实际上并不存在于七个案例中的四个,但如果我们假设这是在前一年的一天,我们会得到正确的答案。
一旦你有了,它和你的日期之间的天数告诉你星期的数字:除以7并加1。
这就是说,我想有一个Windows API的地方,会给你与Outlook使用同一周的数字。 我只是不知道它是什么,当然,如果你的第一周的规则不同于Outlook,那么它可能没有多大用处。
未经测试的代码:
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;
无论如何,就是这样的。
Boost提供了gregorian :: date :: week_number(),参见http://www.boost.org/doc/libs/1_38_0/doc/html/boost/gregorian/date.html和http://www.boost.org/ doc / libs / 1_38_0 / boost / date_time / gregorian / greg_date.hpp 。
然而,我不能看到一个方法来获得与周数相匹配的年份数字(可能与该日期的日历年不同)。
我的假设是,如Olie的答案所示,一年中的第一周可能包含最多7天。 代码不能处理星期一以后那一周开始的文化,这是世界的一大部分。
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); }
只需将当前日期作为参数传递给此函数。 那么你会得到当前的星期编号。 希望它能解决你的问题。 任何建议都非常受欢迎。
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);
/** * @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); }
作为Weekno调用(2015,12,23,1); //对于ISO周编号。 Weekno(2015,12,23,0)//非ISO周数