C++20 chrono 的日曆功能

很久以前,Heresy 曾經寫過 C++11 加入的 std::chrono 這個時間函式庫的介紹。而後來在 C++20 的時候,則又有在裡面追加「日曆」(calendar)喊「時區」(time zone)的功能,因為最近剛好有碰到這部分的東西,所以這邊就來稍微寫一下吧。

而這篇呢,就先針對日曆的部分來寫吧。

Chrono 提供的日曆的功能,主要針對了年、月、日都另外定義出了型別,而同時也提供了星期幾這樣的內容;進一步的,還有某個月的最後一天是幾號這類的訊息,對於某些印用來說,應該也算是有幫助的?


基本使用

下面是一個簡單的日期的操作方法:

#include <chrono>
#include <iostream>
 
int main()
{
  std::chrono::year y{ 2022 };
  std::chrono::month m{ 11 };
  std::chrono::day d{ 11 };
  
  // type: std::chrono::year_month_day
  auto ymd = y / m / d;
 
  // 2022-11-11
  std::cout << ymd << "\n";
}

這邊基本上就是可以各自去定義年、月、日,最後再組合起來。之後如果要在取得對應的數值,也可以透過成員函式來取得需要的資訊。

而他也可以寫成:

using namespace std::chrono;
auto ymd = 2022y/November/11;

如果要知道一個日期是否正確的話,也可以透過 ok() 這個函式來確認。例如:

std::chrono::month m{ 2 };
std::chrono::day d{ 30 };

// type: std::chrono::month_day
auto md = m / d;
if (!md.ok())
{
  std::cout << "the date is invaild\n";
}

這邊由於 2/30 不是一個有效的日期,所以 ok() 就會回傳 false


禮拜幾

而如果想要知道這天是禮拜幾的話,怎可以把 ymd 轉換成 weekday 來取得;下面就是簡單的例子:

std::chrono::weekday wd{ymd};
std::cout << wd << "\n"; // Fri
if (wd == std::chrono::Friday)
  std::cout << "TGIF!" << std::endl;

如果想要用數值的形式來處理禮拜幾的資訊的話,他有提供 c_encoding()iso_encoding() 兩個函式,可以取得不同的編碼結果(文件)。

下面是一個範例:

#include <chrono>
#include <iostream>
 
int main()
{
  std::cout << "i: C: ISO: Weekday:\n";
 
  for (unsigned i{ 0 }; i != 8; ++i) {
    const std::chrono::weekday w{ i };
    std::cout << i << "  "
      << w.c_encoding() << "  "
      << w.iso_encoding() << "    "
      << w << '\n';
  }
}

它的結果會是:

i: C: ISO: Weekday:
0  0  7    Sun
1  1  1    Mon
2  2  2    Tue
3  3  3    Wed
4  4  4    Thu
5  5  5    Fri
6  6  6    Sat
7  0  7    Sun

也就是說,c_encoding() 會把禮拜天視為 0、iso_encoding() 則是把禮拜天視為 7;而如果用整數來建立 weekday 的變數的時候,0 或 7 都會被當成禮拜天。

至於 weekday_indexed 則是可以指定是要當月的第幾個禮拜 x;像下面就是用來取得 2022/11 月的第三個禮拜五的日期方法:

std::chrono::weekday_indexed wdi = std::chrono::Friday[3];
auto ymwd = y / m / wdi;
std::cout << ymwd << "\n"; // 2022/Nov/Fri[3]
std::cout << std::chrono::year_month_day{ ymwd } << "\n";
//2022-11-18

這邊個人覺得比較有趣的,是他可以使用像是陣列存取的形式、透過 [] 來指定是第幾周。


last / 最後一天

由於每個月的天數不一樣,尤其是二月又更特別;再加上每個月到底有幾個禮拜也都很難直接判斷,所以 chrono 也提供了一個特別的 last
可以讓使用者方便地取得最後一天。

比如說,想要取得 2022/02 的最後一天的話,就可以用下面的方法來做:

using namespace std::chrono;
auto ymd = year_month_day{ 2022y / February / last };
std::cout << ymd << " " << weekday(ymd) <<"\n";
// 2022-02-28 Mon

此外,last 也可以和 weekday_indexed 一起使用,這樣就可以簡單地取得「某個月最後一個禮拜一是幾號」這樣的訊息了~

using namespace std::chrono;
auto ymd = year_month_day{ 2022y / February / Friday[last]};
std::cout << ymd <<"\n";
// 2022-02-25

time_point 的轉換

至於要怎麼把這邊的日期和本來的 time_point 來做轉換呢?這邊不能直接轉換,都得做一些處理;下面就是一個簡單的例子:

std::chrono::system_clock::time_point tpNow = std::chrono::system_clock::now();
std::cout << tpNow << "\n";
std::chrono::year_month_day ymd{ std::chrono::floor<std::chrono::days>(tpNow)};
std::cout << ymd << "\n";
std::chrono::system_clock::time_point tp2 = std::chrono::sys_days(ymd);
std::cout << tp2 << "\n";

要把 time_point 轉換成 year_month_day 之前,需要先透過 floor<>() 這個函式(參考)來做轉換,將它改成只以天來計算(型別還算是 time_point<>,但是只會計算到天、時間都被省略掉)。

而如果是反過來,要把 year_month_day 轉換成 time_point 的話,則可以透過 sys_days 來轉換。


這部分大概就是這樣了。

基本上,目前 Heresy 自己應該是還用不到這些功能啦~但是如果有需要針對日期做計算的話,感覺應該算是可以考慮拿來用的。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *