问题
Synopsis:
I want to find the week number of a given date with variable starting days and variable anchoring days for the week.
Full version:
I need to calculate the week any given day lands on out of a year, but I need to be able to change what defines a week. Defining a week boils down to two factors:
Choosing whether the week goes from Sunday to Saturday or Monday to Sunday.
Choosing what day anchors a week.
The start and end of the week is self-explanatory, it simply will decide which Sunday to count in a week, the preceding Sunday or the following Sunday (or if the date we are looking at is a Sunday, decide to which week it belongs). The anchor day will determine year rollover. Assume Wednesday is in yr1
but the next day (Thursday) is in yr2
:
- Case 1: Wednesday defines the week, and therefore the following portion of the week is also part of year
yr1
, week52
(sometimes 53 if the year has 53 of the anchor day). - Case 2: Thursday defines the week, and therefore the previous portion of the week is also in year
yr2
, week01
.
I am using a struct tm * to capture the date and time I want to convert, so I have lots of data to work with I simply don't know what manipulations to make in order to calculate this correctly. I know that the function strftime can toss out a week number 00-53 and can even pick which day the week starts on between Sunday and Monday, but there is no way to alter the anchor day of a week so year rollover doesn't work that way.
Any ideas or suggestions are appreciated as this issue won't just go away on its own! Thanks!
Edit: The code to capture the time in a struct tm *
is:
time_t time_stamp;
struct tm *time_local;
time(&time_stamp); // fills in the current time in seconds, GMT
time_local = localtime(&time_stamp); // translates seconds GMT to tm in local time zone
The data coming in to these variables is all correct, the right date and time, timezone, etc.
回答1:
Week of year with variable start and anchor days
This issue is fairly direct: find the "week-of-the-year" and its "week-of-the-year year" for a given year-month-day.
Step 1: Find the DayOfTheWeek
(0-6). mktime()
will take a struct tm
and set its tm_yday
(0-365) and tm_wday
(0-6) members base on the other fields. For mktime()
, the week starts on Sunday. Adjust with a value of 0 to 6 for other models of when the day-of-the-week begins - this part is fairly trivial. Insure % 7
is not applied to negative numbers.
Step 2, Adjust the date to week's beginning with .tm_mday -= DayOfTheWeek;
Step 3, Adjust the date to week's mid-week by adding 3. Trick: The mid-week day is always in the same calendar year as the week-of-the-year year.
Step 4: Call mktime()
to reset the .tm.tm_year
and .tm_yday
members. Divide .tm_yday
by 7 to get the week-of- the year (and add 1 as the first week is week 1, not 0).
Calling mktime()
twice well handles all edge cases as shown below.
#include <stdio.h>
#include <time.h>
// return 1 on failure, 0 on success
int tm_YearWeek(int y, int m, int d, int FirstDOW, int *year, int *week) {
// Set to noon to avoid DST issues.
struct tm tm = { .tm_year = y - 1900, .tm_mon = m - 1, .tm_mday = d, .tm_hour = 12};
// Calculate tm_wday.
if (mktime(&tm) == -1) {
return 1;
}
// Find day-of-the-week: 0 to 6.
// Week starts on Monday per ISO 8601
// 0 <= DayOfTheWeek <= 6, (Monday, Tuesday ... Sunday)
int DayOfTheWeek = (tm.tm_wday + (7 - 1) - FirstDOW%7) % 7;
// Offset the month day to the 1st day of the week (Monday).
// This may make tm.tm_mday <= 0 or > EndOfMonth
tm.tm_mday -= DayOfTheWeek;
// Offset the month day to the mid-week (Thursday)
// tm.tm_mday <= 0 or > EndOfMonth may be true
tm.tm_mday += 3;
// Re-evaluate tm_year and tm_yday (local time)
if (mktime(&tm) == -1) {
return 1;
}
*year = tm.tm_year + 1900;
// Convert yday to week of the year, stating with 1.
//printf("doy %4d %4d\n", tm.tm_yday, tm.tm_yday/7 + 1);
*week = tm.tm_yday / 7 + 1;
return 0;
}
Some test code
#define FirstDOW_Monday 0
#define FirstDOW_Sunday 6
const char *FirstDOW_Ddd[7] =
{ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
void TestHarness_YearWeek(int y, int m, int d, int FirstDOW) {
int ywd_year, ywd_week;
int e = tm_YearWeek(y,m,d, FirstDOW, &ywd_year, &ywd_week);
if (e) {
fprintf(stderr, "Fail\n");
exit(EXIT_FAILURE);
}
struct tm tm = { .tm_year = y - 1900, .tm_mon = m - 1, .tm_mday = d, .tm_hour = 12};
mktime(&tm);
printf("%s %4d-%2d-%2d --> Year/Week %04d-W%02d (week starts: %s)\n",
FirstDOW_Ddd[(tm.tm_wday + 6) % 7], y, m, d, ywd_year, ywd_week,
FirstDOW_Ddd[FirstDOW]);
}
void TestHarness_2012(int year, int FirstDOW) {
printf(" Jan %d\n", year);
puts(" S M T W T F S");
if (year == 2014)
puts(" 1 2 3 4");
if (year == 2015)
puts(" 1 2 3");
for (int i = 28; i <= 31; i++)
TestHarness_YearWeek(year-1, 12, i, FirstDOW);
for (int i = 1; i <= 6; i++)
TestHarness_YearWeek(year, 1, i, FirstDOW);
puts("");
}
Output
Jan 2014
S M T W T F S
1 2 3 4
Sat 2013-12-28 --> Year/Week 2013-W52 (week starts: Mon)
Sun 2013-12-29 --> Year/Week 2013-W52 (week starts: Mon)
Mon 2013-12-30 --> Year/Week 2014-W01 (week starts: Mon)1st 2014 week start:Dec 30,2013
Tue 2013-12-31 --> Year/Week 2014-W01 (week starts: Mon)
Wed 2014- 1- 1 --> Year/Week 2014-W01 (week starts: Mon)
Thu 2014- 1- 2 --> Year/Week 2014-W01 (week starts: Mon)
Fri 2014- 1- 3 --> Year/Week 2014-W01 (week starts: Mon)
Sat 2014- 1- 4 --> Year/Week 2014-W01 (week starts: Mon)
Sun 2014- 1- 5 --> Year/Week 2014-W01 (week starts: Mon)
Mon 2014- 1- 6 --> Year/Week 2014-W02 (week starts: Mon)
Jan 2014
S M T W T F S
1 2 3 4
Sat 2013-12-28 --> Year/Week 2013-W52 (week starts: Sun)
Sun 2013-12-29 --> Year/Week 2014-W01 (week starts: Sun)1st 2014 week start:Dec 29,2013
Mon 2013-12-30 --> Year/Week 2014-W01 (week starts: Sun)
Tue 2013-12-31 --> Year/Week 2014-W01 (week starts: Sun)
Wed 2014- 1- 1 --> Year/Week 2014-W01 (week starts: Sun)
Thu 2014- 1- 2 --> Year/Week 2014-W01 (week starts: Sun)
Fri 2014- 1- 3 --> Year/Week 2014-W01 (week starts: Sun)
Sat 2014- 1- 4 --> Year/Week 2014-W01 (week starts: Sun)
Sun 2014- 1- 5 --> Year/Week 2014-W02 (week starts: Sun)
Mon 2014- 1- 6 --> Year/Week 2014-W02 (week starts: Sun)
Jan 2015
S M T W T F S
1 2 3
Sun 2014-12-28 --> Year/Week 2014-W52 (week starts: Mon)
Mon 2014-12-29 --> Year/Week 2015-W01 (week starts: Mon)1st 2015 week start:Dec 29,2014
Tue 2014-12-30 --> Year/Week 2015-W01 (week starts: Mon)
Wed 2014-12-31 --> Year/Week 2015-W01 (week starts: Mon)
Thu 2015- 1- 1 --> Year/Week 2015-W01 (week starts: Mon)
Fri 2015- 1- 2 --> Year/Week 2015-W01 (week starts: Mon)
Sat 2015- 1- 3 --> Year/Week 2015-W01 (week starts: Mon)
Sun 2015- 1- 4 --> Year/Week 2015-W01 (week starts: Mon)
Mon 2015- 1- 5 --> Year/Week 2015-W02 (week starts: Mon)
Tue 2015- 1- 6 --> Year/Week 2015-W02 (week starts: Mon)
Jan 2015
S M T W T F S
1 2 3
Sun 2014-12-28 --> Year/Week 2014-W53 (week starts: Sun)
Mon 2014-12-29 --> Year/Week 2014-W53 (week starts: Sun)
Tue 2014-12-30 --> Year/Week 2014-W53 (week starts: Sun)
Wed 2014-12-31 --> Year/Week 2014-W53 (week starts: Sun)
Thu 2015- 1- 1 --> Year/Week 2014-W53 (week starts: Sun)
Fri 2015- 1- 2 --> Year/Week 2014-W53 (week starts: Sun)
Sat 2015- 1- 3 --> Year/Week 2014-W53 (week starts: Sun)
Sun 2015- 1- 4 --> Year/Week 2015-W01 (week starts: Sun)1st 2015 week start:Jan 1, 2016
Mon 2015- 1- 5 --> Year/Week 2015-W01 (week starts: Sun)
Tue 2015- 1- 6 --> Year/Week 2015-W01 (week starts: Sun)
来源:https://stackoverflow.com/questions/48215997/c-week-of-year-with-variable-start-and-anchor-days