c - Week of year with variable start and anchor days

左心房为你撑大大i 提交于 2019-12-10 22:54:09

问题


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, week 52 (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, week 01.

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!