c - 具有可变开始和锚定日期的一年中的一周

c - Week of year with variable start and anchor days

剧情简介:

我想查找给定日期的周数,该日期具有可变的起始日和可变的锚定日。

完整版:

我需要计算一年中任何给定的一天落在哪一周,但我需要能够更改定义一周的内容。定义一周归结为两个因素:

一周的开始和结束是不言自明的,它只是决定一周中的哪个星期日,前一个星期日或下一个星期日(或者如果我们正在查看的日期是星期日,则决定它属于哪一周)。锚定日将决定年轮转。假设星期三在 yr1 但第二天(星期四)在 yr2:

我正在使用 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 可以抛出周数 00-53,甚至可以选择星期日和星期一之间的星期几开始,但是无法更改锚定日周所以年轮转不能那样工作。

欢迎提出任何想法或建议,因为此问题不会自行消失!谢谢!

编辑:在 struct tm * 中捕获时间的代码是:

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

进入这些变量的数据都是正确的,正确的日期和时间、时区等。

Week of year with variable start and anchor days

这个问题相当直接:找到给定年月日的 "week-of-the-year" 及其 "week-of-the-year year"。


第 1 步:找到 DayOfTheWeek (0-6)。 mktime() 将采用 struct tm 并根据其他字段设置其 tm_yday (0-365) 和 tm_wday (0-6) 成员。对于 mktime(),一周从星期日开始。 调整 的值为 0 到 6 对于其他模型 星期几开始的时间 - 这部分相当 琐碎。确保 % 7 不适用于负数。

第 2 步,将日期调整为一周的开始 .tm_mday -= DayOfTheWeek;

第 3 步,通过添加 3 将日期调整为一周的周中。技巧:周中日始终与一年中的星期年在同一日历年中。

第 4 步:调用 mktime() 重置 .tm.tm_year.tm_yday 成员。将 .tm_yday 除以 7 得到一年中的第几周(并加 1,因为第一周是第 1 周,而不是 0)。

调用 mktime() 两次可以很好地处理所有边缘情况,如下所示。

#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;
}

一些测试代码

#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("");
}

输出

            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)