c++ - strftime - 将周数 ISO8601 转换为公历标准 C++

标签 c++ iso8601 strftime week-number gregorian-calendar

我使用 strftime 获取 ISO 8601 格式 (%V) 的周数,并希望将其转换为 Outlook 中显示的周数(猜测是公历)。

Outlook 日历设置为:

  • 一周的第一天(星期日)
  • 一年的第一周从 1 月 1 日开始

对于某些年份,strftime 与 Outlook 中显示的周数匹配,但对于某些年份则不然。

简单的代码检查:

#include
#include

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 周数映射到公历风格?

任何建议或代码示例都会有帮助!

谢谢!

最佳答案

您引用的网站似乎使用了非常独特的周编号系统。它对周数的定义似乎反射(reflect)了 ISO 定义,但一周的第一天是星期日而不是星期一。这意味着一年的第一天是一年中第一个星期四之前的星期日。

没有 strftime 标志来给出此定义的周数。但您可以使用 C++20 <chrono> 轻松计算它工具。不幸的是,他们还没有发货,但你可以使用这个 free, open-source C++20 chrono preview library适用于 C++11/14/17。

除了计算周数之外,您还需要计算年份,因为有时公历年份与与周相关的年份不匹配。例如根据https://www.calendar-365.com/2019-calendar.html ,2018 年 12 月 31 日恰逢 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是周编号系统中的年份(通常但并不总是与公历年份相同)。如果日期在一年中很晚,即 12 月 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

完成此操作后,计算年初。如果年初恰好在该日期之后,那么您的日期就属于上一年。在这种情况下,周数年份可能比公历年份小1。当 1 月 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 datenamespace std::chrono
  3. 更改2019_y2019y

关于c++ - strftime - 将周数 ISO8601 转换为公历标准 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63133926/

相关文章:

python - 转换日期格式 python - 不寻常的日期格式 - 提取 %Y%M%D

Pandas : How to get timestamp. 天和 timestamp.month 填充零

r - 通过 strftime() 将 POSIXct 转换为字符会产生错误的结果

c++ - 比较常规选择和准备选择性能

c++ - C++中指向成员函数的函数指针

c++ - 不可复制、不可移动、显式构造类型的成员数组的初始化

java - Amazon Alexa : AMAZON. DATE 到 Java 日期/持续时间

json - NodaTime 间隔 JSON 序列化

c++ - Eigen::Map 未命名?

linux - 将 ISO8601 转换为 BASH 变量中的本地时间