很久以前,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 自己應該是還用不到這些功能啦~但是如果有需要針對日期做計算的話,感覺應該算是可以考慮拿來用的。