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)