strftime - преобразовать номер недели ISO8601 в григорианский стандарт C ++

Я получаю номер недели в формате ISO 8601 (% V) с помощью strftime и люблю преобразовывать в номер недели, показанный в моем прогнозе (думаю, это грегорианский).

Настройка календаря Outlook:

  • Первый день недели (воскресенье)
  • Первая неделя года начинается 1 января

В определенные годы strftime совпадает с номером недели, отображаемым в Outlook, но в некоторых случаях это не так.

Простая проверка кода:

#include ‹stdio.h›
#include ‹time.h›

int main ()
{
  time_t rawtime;
  struct tm *tmDate;
  char buffer [80];

    time (&rawtime);
    tmDate = localtime(&rawtime);
    tmDate->tm_hour  = 12;
    tmDate->tm_mday  = 1; //Day of the Month
    tmDate->tm_mon   = 8; //Month (0=Jan...8=Sep)
    tmDate->tm_year  = 2018-1900; //Year
    mktime(tmDate);
        
  strftime (buffer,80,"%Y-W%V",tmDate);
  puts (buffer);
  
  return 0;
}

Для приведенного выше кода с его входными данными вывод будет 2018-W35, что соответствует моему календарю Outlook (https://www.calendar-365.com/2018-calendar.html).

Принимая во внимание, что если вы измените год на 2019, результат будет 2019-W35, но, по моему мнению, он выпадет на 2019-W36 (https://www.calendar-365.com/2019-calendar.html).

Можно ли сопоставить номер недели ISO8601 с грегорианским стилем?

Любые предложения или образцы кода будут полезны!

Спасибо!


person Jay    schedule 28.07.2020    source источник
comment
Вы это читали? en.wikipedia.org/wiki/ISO_week_date   -  person n. 1.8e9-where's-my-share m.    schedule 28.07.2020


Ответы (1)


На веб-сайте, на который вы ссылаетесь, похоже, используется очень уникальная система нумерации недель. Его определение номера недели похоже на определение ISO, за исключением того, что первым днем ​​недели является воскресенье, а не понедельник. Это означает, что первый день года - это воскресенье перед первым четвергом года.

Нет флага strftime для указания номера недели с этим определением. Но вы можете легко вычислить это с помощью инструментов C ++ 20 <chrono>. К сожалению, они еще не отправлены, но вы можете использовать эту бесплатную библиотеку предварительного просмотра хронографов C ++ 20 с открытым исходным кодом, который работает с C ++ 11/14/17.

Помимо вычисления номера недели, вам также необходимо вычислить год, поскольку иногда грегорианский год не совпадает с годом, связанным с неделей. Например, согласно https://www.calendar-365.com/2019-calendar.html, 31 декабря 2018 г. приходится на первую неделю 2019 г.

Итак, вот функция, которая вычисляет год и номер недели по дате:

#include "date/date.h"
#include <chrono>
#include <iostream>
#include <utility>

// {year, week number}
std::pair<int, int>
outlook_weeknum(date::sys_days sd)
{
    using namespace date;
    auto y = year_month_day{sd + (Thursday - Sunday)}.year();
    auto year_start = sys_days{Thursday[1]/January/y} - (Thursday - Sunday);
    if (sd < year_start)
    {
        --y;
        year_start = sys_days{Thursday[1]/January/y} - (Thursday - Sunday);
    }
    return {int{y}, (sd - year_start)/weeks{1} + 1};
}

Логика немного хитрая. Сложнее всего найти первый день года, то есть воскресенье, предшествующее первому четвергу года. Это номинально:

auto year_start = sys_days{Thursday[1]/January/y} - (Thursday - Sunday);

где y - год в системе нумерации недель (обычно, но не всегда совпадает с грегорианским годом). Когда дата наступает очень поздно, то есть с 28 по 31 декабря, она может выпадать на первую неделю следующего года. Чтобы уловить эту возможность, сначала увеличьте дату на 4 дня (разница между воскресеньем и четвергом), а затем вычислите текущий год.

         S  M  T  W  T  F  S
y-1 WL  21 22 23 24 25 26 27
y   W1  28 29 30 31  1  2  3

После этого вычислите начало года. А если начало года наступает позже даты, значит, ваша дата принадлежит предыдущему году. В этом случае номер недели year может быть на единицу меньше, чем грегорианский год. Это может произойти, когда 1 января - пятница или суббота.

         S  M  T  W  T  F  S
y-1 WL  27 28 29 30 31  1  2
y   W1   3  4  5  6  7  8  9

Таким образом, даты 12/28 - 12/31 могут иметь номер года недели, равный их грегорианскому году, или на единицу больше. А даты 01/01 и 01/02 могут иметь номер года недели, равный их грегорианскому году, или на единицу меньше. - Все зависит от того, в какой день месяца первый четверг января приходится на [1 - 7].

Как только номер недели год (y) определен, тогда номер недели - это просто разница между датой и первым годом, разделенная на 7 дней (1 неделя), плюс единица для смещения первой недели к 1 вместо 0. .

Это можно сделать так:

int
main()
{
    using namespace date;

    auto [i, w] = outlook_weeknum(2019_y/9/1);
    std::cout << i << "-W" << w << '\n';
}

который выводит:

2019-W36

Чтобы перенести этот код на C ++ 20:

  1. Бросьте #include "date/date.h"
  2. Измените namespace date на namespace std::chrono
  3. Измените 2019_y на 2019y
person Howard Hinnant    schedule 28.07.2020
comment
наш env. в настоящее время у нас есть gcc-c ++ - 4.8.5, и мы использовали вашу логику для создания нашей собственной функции, и она отлично работает. Спасибо! - person Jay; 31.07.2020
comment
Большой! Если это поможет, вот несколько очень эффективных базовых алгоритмов в открытом доступе: howardhinnant.github.io/date_algorithms .html - person Howard Hinnant; 31.07.2020