C++ 日期和時間編程
日期和時間是編程中非常常用的功能。本文是對C++11到C++17中相關(guān)編程接口的介紹。
介紹
C++中可以使用的日期時間API主要分為兩類:
C-style 日期時間庫,位于 <ctime>頭文件中。這是原先<time.h>頭文件的C++版本。chrono庫:C++ 11中新增API,增加了時間點(diǎn),時長和時鐘等相關(guān)接口。
在C++11之前,C++編程只能使用C-style日期時間庫。其精度只有秒級別,這對于有高精度要求的程序來說,是不夠的。
但這個問題在C++11中得到了解決,C++11中不僅擴(kuò)展了對于精度的要求,也為不同系統(tǒng)的時間要求提供了支持。
另一方面,對于只能使用C-style日期時間庫的程序來說,C++17中也增加了timespec將精度提升到了納秒級別。
代碼示例
本文中所貼出的代碼示例可以到我的Github上獲?。?span style="outline: 0px;color: rgb(30, 107, 184);font-weight: bold;">paulQuei/cpp-date-time[1]。
或者,你也可以直接通過下面這條命令獲取所有源碼:
git clone https://github.com/paulQuei/cpp-date-time.git
為了簡化書寫,本文中給出的代碼都已經(jīng)默認(rèn)做了以下操作:
#include <chrono>
#include <ctime>
#include <iostream>
using namespace std;
C-style 日期時間庫
C-style 日期時間庫中包含的函數(shù)和數(shù)據(jù)類型說明如下:
函數(shù)
| 函數(shù) | 說明 |
|---|---|
std::clock_t clock() | 返回自程序啟動時起的處理器時鐘時間 |
std::time_t time(std::time_t* arg) | 返回自紀(jì)元起計的系統(tǒng)當(dāng)前時間 |
double difftime(std::time_t time_end, std::time_t time_beg) | 計算時間之間的差 |
int timespec_get(std::timespec* ts, int base)?? | 返回基于給定時間基底的日歷時間 |
char* ctime(const std::time_t* time) | 轉(zhuǎn)換 time_t 對象為文本表示 |
char* asctime(const std::tm* time_ptr) | 轉(zhuǎn)換 tm 對象為文本表示 |
std::size_t strftime(char* str, std::size_t count, const char* format, const std::tm* time) | 轉(zhuǎn)換 tm 對象到自定義的文本表示 |
std::size_t wcsftime( wchar_t* str, std::size_t count, const wchar_t* format, const std::tm* time) | 轉(zhuǎn)換 tm 對象為定制的寬字符串文本表示 |
std::tm* gmtime(const std::time_t* time) | 將time_t轉(zhuǎn)換成UTC表示的時間 |
std::tm* localtime(const std::time_t *time) | 將time_t轉(zhuǎn)換成本地時間 |
std::time_t mktime(std::tm* time) | 將tm格式的時間轉(zhuǎn)換成time_t表示的時間 |
數(shù)據(jù)類型
| 名稱 | 說明 |
|---|---|
| time_t | 從紀(jì)元起的時間類型 |
| tm | 日歷時間類型 |
| timespec?? | 以秒和納秒表示的時間 |
| clock_t | 進(jìn)程運(yùn)行時間 |
| size_t | sizeof 運(yùn)算符返回的無符號整數(shù)類型 |
結(jié)構(gòu)梳理
這里有不少的函數(shù)和數(shù)據(jù)類型,剛開始接觸的時候似乎不太容易記得住。
但實(shí)際上,如果我們把它們畫成一張圖就比較好理解了,如下所示:

在這幅圖中,以數(shù)據(jù)類型為中心,帶方向的實(shí)線箭頭表示該函數(shù)能返回相應(yīng)類型的結(jié)果。
clock函數(shù)是相對獨(dú)立的一個函數(shù),它返回進(jìn)程運(yùn)行的時間,具體描述見下文。 time_t描述了紀(jì)元時間,通過time函數(shù)可以獲得它。但它只能精確到秒級別。 timespec類型在time_t的基礎(chǔ)上,增加了納秒的精度,通過timespec_get獲取。這是C++17上新增的 tm是日歷類型,因?yàn)樗渲邪四暝氯盏刃畔?。通過gmtime,localtime和mktime函數(shù)可以將time_t和tm類型互相轉(zhuǎn)換。 考慮到時區(qū)的差異,因此存在gmtime和localtime兩個函數(shù)。 無論是time_t還是tm結(jié)構(gòu),都可以將其以字符串格式輸出。ctime和asctime輸出的格式是固定的。如果需要自定義格式,需要使用strftime或者wcsftime函數(shù)。
進(jìn)程運(yùn)行時間
clock函數(shù)返回進(jìn)程迄今為止所用的處理器時間。單獨(dú)調(diào)度該函數(shù)一次所返回的值是沒有意義的,只有兩次不同值的差才有意義。
該值表示了進(jìn)程從關(guān)聯(lián)到程序執(zhí)行的實(shí)現(xiàn)定義時期開始,所用的粗略處理器時間。而且這個值僅僅是處理器的時鐘周期。如果希望將其轉(zhuǎn)換為以秒為單位,還需要將它除以常量 CLOCKS_PER_SEC 。
下面是一段代碼示例:
clock_t time1 = clock();
double sum = 0;
for(int i = 0; i < 100000000; i++) {
sum += sqrt(i);
}
clock_t time2 = clock();
double t = ((double)(time2 - time1)) / CLOCKS_PER_SEC ;
cout << "CLOCKS_PER_SEC: " << CLOCKS_PER_SEC << endl;
cout << "Process running time: " << t << "s" << endl;
其輸出如下:
CLOCKS_PER_SEC: 1000000
Process running time: 0.80067s
你可能知道,現(xiàn)代的操作系統(tǒng)上進(jìn)程都是分時占用處理器的,所以程序的處理器時間會小于真實(shí)世界流逝的時間。但這僅僅是對于單處理器而言的。在多處理器系統(tǒng)上,如果你的進(jìn)程使用了多線程,那么其所用的處理器時間可能比真實(shí)世界流逝的時間值還要大。
關(guān)于紀(jì)元時間
紀(jì)元時間(Epoch time)又叫做Unix時間或者POSIX時間。它表示自1970 年 1 月 1 日 00:00 UTC 以來所經(jīng)過的秒數(shù)(不考慮閏秒)。它在操作系統(tǒng)和文件格式中被廣泛使用。
這個想法很簡單:以一個時間為起點(diǎn)加上一個偏移量便可以表達(dá)任何一個其他的時間。
如果你好奇為什么選這個時間作為起點(diǎn),可以點(diǎn)擊這里:Why is 1/1/1970 the “epoch time”?[2]。
下面是一個代碼示例:
time_t epoch_time = time(nullptr);
cout << "Epoch time: " << epoch_time << endl;
其輸出如下:
Epoch time: 1577433897
time函數(shù)接受一個指針,指向要存儲時間的對象,通??梢詡鬟f一個空指針,然后通過返回值來接受結(jié)果。
雖然標(biāo)準(zhǔn)中沒有給出定義,但time_t通常使用整形值來實(shí)現(xiàn)。
作為一個程序員,你可能馬上會意識到整形的位數(shù)和溢出的問題。事實(shí)也剛好是這樣,在一些歷史實(shí)現(xiàn)上使用了32位有符號整數(shù)來實(shí)現(xiàn)time_t,其造成的結(jié)果就是:在2038-01-19 03:14:07[3]這個時間點(diǎn),這個值會溢出。
不過不用擔(dān)心太多,這個時間距現(xiàn)在還有將近20年,到那個時候,估計那些有問題的系統(tǒng)已經(jīng)不會再繼續(xù)運(yùn)轉(zhuǎn)或者已經(jīng)被升級了。
計算時間差
在一些情況下,我們需要計算一個操作的時間長度。這自然的就需要計算兩個時間點(diǎn)的差分。這時就可以使用difftime函數(shù)。
事實(shí)上,我們知道time_t以秒級別表示紀(jì)元時間,并且它又是以整形實(shí)現(xiàn)的,直接將兩個time_t相減,可以得到相同的結(jié)果。
下面是一個代碼示例:
time_t time1 = time(nullptr);
double sum = 0;
for(int i = 0; i < 1000000000; i++) {
sum += sqrt(i);
}
time_t time2 = time(nullptr);
double time_diff = difftime(time2, time1);
cout << "time1: " << time1 << endl;
cout << "time2: " << time2 << endl;
cout << "time_diff: " << time_diff << "s" << endl;
其輸出如下,可以看到這正是time1和time2兩個整數(shù)相減的結(jié)果:
time1: 1577434406
time2: 1577434414
time_diff: 8s
注意:time_t只精確到秒,它無法描述毫秒級別的時間,所以在有更高精度要求的情況下,需要使用下文提到的其他方法。
輸出時間和日期
當(dāng)然,我們還希望將時間以字符串的形式打印出來。這時就可以使用ctime函數(shù)。不過該函數(shù)打印的格式是固定的:Www Mmm dd hh:mm:ss yyyy\n。如果你希望自定義輸出的格式,可以使用下文提到的其他方法。
下面是一個代碼示例:
time_t now = time(nullptr);
cout << "Now is: " << ctime(&now);
其輸出如下:
Now is: Fri Dec 27 16:17:45 2019
UTC時間與本地時間
對于一個具體的時刻來說,不同時區(qū)的具體時間是不一樣的,例如:東京時間就比北京時間快了一個小時。
我們既有可能需要知道無差別的標(biāo)準(zhǔn)時間(UTC時間),例如:計算一個航班的時長。也有可能需要獲取某個當(dāng)?shù)氐木唧w時間:gmtime用來將 std::time_t 的紀(jì)元時間轉(zhuǎn)換為UTC時間,而localtime則將紀(jì)元時間轉(zhuǎn)換為本地時區(qū)所代表的日歷時間。
日歷時間使用tm結(jié)構(gòu)描述。其結(jié)構(gòu)如下:
struct tm {
int tm_sec;
int tm_min
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
需要注意的是:
tm_mon 并非我們?nèi)祟惱斫獾脑路?,而是[0, 11]的范圍。因此在某些時候你可能要對其+1. tm_year 是自1900起之年,因此你可能也需要對其 +1900。
asctime可以直接將tm結(jié)構(gòu)轉(zhuǎn)換成人類理解的字符串格式,不過其格式是固定的:Www Mmm dd hh:mm:ss yyyy\n。對于有特定格式要求,可以使用下文提到的其他方法。
下面是一個代碼示例:
time_t now = time(nullptr);
tm* gm_time = gmtime(&now);
tm* local_time = localtime(&now);
cout << "gmtime: " << asctime(gm_time);
cout << "local_time: " << asctime(local_time);
其輸出如下:
gmtime: Fri Dec 27 08:36:14 2019
local_time: Fri Dec 27 16:36:14 2019
由于北京時間的時區(qū)是GMT+8,所以我的本地時間比UTC時間快8個小時。
自定義時間格式
無論是ctime函數(shù)還是asctime函數(shù),其輸出的格式都是固定的。在有格式要求的情況下,它們并不能完成需求。
當(dāng)然,你可以直接讀取tm結(jié)構(gòu)體中的字段來進(jìn)行輸出,例如:
time_t now = time(nullptr);
tm* t = localtime(&now);
cout << "Now is: " << t->tm_year + 1900 << "/" << t->tm_mon + 1<< "/" << t->tm_mday << " ";
cout << t->tm_hour << ":" << t->tm_min << ":" << t->tm_sec << endl;
請注意這段代碼中,需要對tm_year和tm_mon進(jìn)行轉(zhuǎn)換才是我們?nèi)粘@斫獾娜掌凇?/p>
很顯然,這樣寫太啰嗦了。
更好的方法是:使用strftime或者wcsftime函數(shù)來指定格式輸出。關(guān)于這兩個函數(shù)的格式,可以看這個鏈接:std::strftime format[4]。
想要輸出上面代碼同樣的格式,只要這樣就可以完成任務(wù)了:
char buffer[32];
strftime(buffer, 32, "%Y/%m/%d %H:%M:%S", t);
cout << "Now is: " << buffer << endl;
它們會輸出同樣的結(jié)果:
Now is: 2019/12/27 16:40:39
除了
<ctime>之外,為了方便時間的輸入輸出,從C++11開始,在<iomanip>頭文件中還增加了put_time[5]與get_time[6]兩個函數(shù)。
納秒精度的timespec
前面我們已經(jīng)說了,紀(jì)元時間的精度只有秒級別。這在很多時候是不夠用的。
為了解決這個問題,C++17上增加了timespec類型提供了納秒級別的精度。
以下是四個時間單位的換算和英文表示:
1秒(Second)=103毫秒(millisecond)=106微妙(microsecond)=109納秒(nanosecond)(1)(1)1秒(Second)=103毫秒(millisecond)=106微妙(microsecond)=109納秒(nanosecond)
timespec類型結(jié)構(gòu)如下:
struct timespec {
std::time_t tv_sec;
long tv_nsec;
};
可以看到,這里是在time_t之外,增加了一個tv_nsec來表達(dá)納秒的數(shù)量。為了獲取這個值,C++17新增了timespec_get函數(shù)。
以下是一個代碼示例:
timespec ts;
timespec_get(&ts, TIME_UTC);
char buff[100];
strftime(buff, sizeof buff, "%D %T", std::gmtime(&ts.tv_sec));
printf("Current time: %s.%09ld UTC\n", buff, ts.tv_nsec);
timespec_get的第二個參數(shù)是一個整形表達(dá)的base。目前標(biāo)準(zhǔn)只定義了TIME_UTC。
其輸出如下:
Current time: 12/27/19 09:03:29.497456000 UTC
說完了C-style日期時間庫,讓我們再來看看C++11新增的chrono庫。
chrono 庫
“chrono”是英文chronology的縮寫,其含義是“年表;年代學(xué)”。
時鐘
為了滿足不同類型的需求,C++11 chrono庫中包含了三種類型的時鐘,它們的說明如下:
| 名稱 | 說明 |
|---|---|
| system_clock | 系統(tǒng)時鐘 |
| steady_clock | 單調(diào)時鐘,不會被調(diào)整 |
| high_resolution_clock | 擁有可用的最短嘀嗒周期的時鐘 |
system_clock 的時間來源是系統(tǒng)時鐘,而系統(tǒng)時間隨時都可能被調(diào)整。所以如果你需要計算兩個時間點(diǎn)的時間差,這不是一個好的選擇。因?yàn)槿绻麅纱螘r間差中間系統(tǒng)時間被調(diào)整了,其結(jié)果就沒有意義了。
steady_clock會保證單調(diào)性。它就好像物理時間只會向前移動,無法減少。它最適合用來度量間隔。
high_resolution_clock 表示實(shí)現(xiàn)提供的擁有最小計次周期的時鐘。它可以是 system_clock 或 steady_clock 的別名,也可能是第三個獨(dú)立時鐘。
這三個時鐘類有一些共同的成員,如下所示:
| 名稱 | 說明 |
|---|---|
| now()now() | 靜態(tài)成員函數(shù),返回當(dāng)前時間,類型為clock::time_point |
| time_point | 成員類型,當(dāng)前時鐘的時間點(diǎn)類型,見下文“時間點(diǎn)” |
| duration | 成員類型,時鐘的時長類型,見下文“時長” |
| rep | 成員類型,時鐘的tick類型,等同于clock::duration::rep |
| period | 成員類型,時鐘的單位,等同于clock::duration::period |
| is_steady | 靜態(tài)成員類型:是否是穩(wěn)定時鐘,對于steady_clock來說該值一定是true |
每種時鐘類都有一個 now()now() 靜態(tài)函數(shù)來獲取當(dāng)前時間,返回的類型是由該時鐘類下的time_point描述。這是一個std::chrono::time_point模板類的具體實(shí)例,例如:std::chrono::time_pointstd::chrono::system_clock或者std::chrono::time_pointstd::chrono::steady_clock。是的,這個類型太長了,不過在C++11中,你可以用auto關(guān)鍵字來簡寫。
例如,下面是不使用和使用auto關(guān)鍵字的寫法:
std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now();
auto now2 = std::chrono::steady_clock::now();
與C-style轉(zhuǎn)換
system_clock與另外兩個clock不一樣的地方在于,它還提供了兩個靜態(tài)函數(shù)用來與std::time_t來回轉(zhuǎn)換:
| 名稱 | 說明 |
|---|---|
| to_time_t | 轉(zhuǎn)換系統(tǒng)時鐘時間點(diǎn)為 std::time_t |
| from_time_t | 轉(zhuǎn)換 std::time_t 到系統(tǒng)時鐘時間點(diǎn) |
由此,我們可以通過下面這幅圖來描述幾種時間類型的轉(zhuǎn)換:

下面是一個代碼示例:
auto now = chrono::system_clock::now();
time_t time = chrono::system_clock::to_time_t(now);
cout << "Now is: " << ctime(&time) << endl;
時長
ratio
人類對于精度的要求是沒有止境的。雖然目前的精度已經(jīng)從秒到毫秒,微妙甚至納秒。但很難說今后還會不會有更高精度的要求。
前面我們已經(jīng)看到,為了將精度從秒變成納秒,C++17增加了timespec_get函數(shù)和timespec類型。如果以后還有更高的精度要求,C++標(biāo)準(zhǔn)還需要添加更多的函數(shù)和類型嗎?老實(shí)說,這不是一個好的設(shè)計。
為了解決這個問題,C++11中添加了一個新的頭文件和類型,那就是:ratio。
std::ratio描述了編譯時的有理數(shù),這是一個模板類。有了這個類型之后,我們就可以表示任意精度的值了。
例如:相對于秒來說,毫秒是11,00011,000,微妙是11,000,00011,000,000,納秒是11,000,000,00011,000,000,000。通過ratio可以這樣表達(dá):
std::ratio<1, 1000> milliseconds;
std::ratio<1, 1000000> microseconds;
std::ratio<1, 1000000000> nanoseconds;
其實(shí)上,<ratio>頭文件中包含了很多以10為基數(shù)的各種分?jǐn)?shù),具體可以見這里:編譯時有理數(shù)算術(shù)[7]。
我們只需要通過:std::milli,std::micro,std::nano使用就好了。
當(dāng)然,ratio能表達(dá)的數(shù)值不僅僅是以10為基底的。對于任意的分?jǐn)?shù)都可以表達(dá),例如: 5757,591023591023等等。
對于一個具體的ratio來說,可以通過den獲取分母的值,num獲取分子的值。
不僅僅如此,<ratio>頭文件還包含了:ratio_add,ratio_subtract,ratio_multiply,ratio_divide來完成分?jǐn)?shù)的加減乘除四則運(yùn)算。
例如,想要計算57+59102357+591023,可以這樣寫:
ratio_add<ratio<5, 7>, ratio<59, 1023>> result;
double value = ((double) result.num) / result.den;
cout << result.num << "/" << result.den << " = " << value << endl;
請注意:無論是分子還是分母,都是一個整形。在C++中,整形的除法結(jié)果仍然是整形,多余部分會被丟棄,因此想要獲取double類型的結(jié)果,需要先將其轉(zhuǎn)換成double。
這段代碼輸出的結(jié)果是:
5528/7161 = 0.771959
時長類型
類模板 std::chrono::duration 表示時間間隔。有了ratio之后,表達(dá)時長就很方便了,下面是chrono庫中提供的很常用的幾個時長單位:
| 類型 | 定義 |
|---|---|
| std::chrono::nanoseconds | duration</至少 64 位的有符號整數(shù)類型/, std::nano> |
| std::chrono::microseconds | duration</至少 55 位的有符號整數(shù)類型/, std::micro> |
| std::chrono::milliseconds | duration</至少 45 位的有符號整數(shù)類型/, std::milli> |
| std::chrono::seconds | duration</至少 35 位的有符號整數(shù)類型/> |
| std::chrono::minutes | duration</至少 29 位的有符號整數(shù)類型/, std::ratio<60? |
| std::chrono::hours | duration</至少 23 位的有符號整數(shù)類型/, std::ratio<3600? |
duration類的count()成員函數(shù)返回具體數(shù)值。
時長運(yùn)算
時長之間最常用的運(yùn)算自然是相加或者相減,這個通過“+”,“-”就可以完成。
除此之外,chrono庫中還提供了下面幾個常用的函數(shù):
| 函數(shù) | 說明 |
|---|---|
| duration_cast | 進(jìn)行時長的轉(zhuǎn)換 |
| floor(C++17) | 以向下取整的方式,將一個時長轉(zhuǎn)換為另一個時長 |
| ceil(C++17) | 以向上取整的方式,將一個時長轉(zhuǎn)換為另一個時長 |
| round(C++17) | 轉(zhuǎn)換時長到另一個時長,就近取整,偶數(shù)優(yōu)先 |
| abs(C++17) | 獲取時長的絕對值 |
例如:想要知道2個小時零5分鐘一共是多少秒,可以這樣寫:
chrono::hours two_hours(2);
chrono::minutes five_minutes(5);
auto duration = two_hours + five_minutes;
auto seconds = chrono::duration_cast<chrono::seconds>(duration);
cout << "02:05 is " << seconds.count() << " seconds" << endl;
我們可以得到:
02:05 is 7500 seconds
從C++14開始,你甚至可以用字面值來描述常見的時長。這包括:
h表示小時min表示分鐘s表示秒ms表示毫秒us表示微妙ns表示納秒
這些字面值位于std::chrono_literals命名空間下。于是,可以這樣表達(dá)2個小時以及5分鐘:
using namespace std::chrono_literals;
auto two_hours = 2h;
auto five_minutes = 5min;
時間點(diǎn)
時間點(diǎn)中包含了時鐘和時長兩個信息,類模板 std::chrono::time_point 表示時間中的一個點(diǎn)。
時鐘的now()now()函數(shù)返回的值就是一個時間點(diǎn)。time_point中的time_since_epoch()返回從其時鐘起點(diǎn)開始的時長。
時間點(diǎn)運(yùn)算
時間點(diǎn)有加法操作和減法操作,與我們的常識相一致:
時間點(diǎn) + 時長 = 時間點(diǎn)
時間點(diǎn) - 時間點(diǎn) = 時長
例如:可以通過兩個時間點(diǎn)相減計算一個時間間隔,下面是一個代碼示例:
auto start = chrono::steady_clock::now();
double sum = 0;
for(int i = 0; i < 100000000; i++) {
sum += sqrt(i);
}
auto end = chrono::steady_clock::now();
auto time_diff = end - start;
auto duration = chrono::duration_cast<chrono::milliseconds>(time_diff);
cout << "Operation cost : " << duration.count() << "ms" << endl;
上面這個代碼很好的說明了:有了duration和duration_cast,我們可以以任意的精度來描述結(jié)果的值。
除了相加和相減,兩個時間點(diǎn)還有比較操作:判斷一個時間點(diǎn)在另外一個時間點(diǎn)之前還是之后。對于這些操作,通過運(yùn)算符:==,!=,<,<=,>,>=來完成。
展望 C++ 20
通過上面的講解我們可以看到,自C++11以來,日期時間庫功能獲得了極大的提升。并且,在C++14和C++17上也有一些小改進(jìn)。
但如果你使用過Java類庫,就會覺得差距還是比較大。Java中的相關(guān)API早就有對于日歷,時區(qū)等方面更好的支持。
對于這一點(diǎn), 在C++下一個標(biāo)準(zhǔn)C++20中,將有相應(yīng)的支持。這些改進(jìn)包括:
更多類型的時鐘,例如:utc_clock,tai_lock,gps_clock等。 時鐘以及時間點(diǎn)的轉(zhuǎn)換 Calendar支持 時區(qū)支持
關(guān)于這一點(diǎn),你可以看這個鏈接:Date and time utilities[8]。
如果想獲取更多關(guān)于C++20的信息,可以直接看這里:N4842:《Working Draft, Standard for Programming Language C++》[9]。
參考資料與推薦讀物
cppreference: Date and time utilities[10] cppreference: Standard library header [11] [The C++ Standard Library: A Tutorial and Reference, 2nd Edition](
參考資料
paulQuei/cpp-date-time: https://github.com/paulQuei/cpp-date-time
[2]Why is 1/1/1970 the “epoch time”?: https://stackoverflow.com/questions/1090869/why-is-1-1-1970-the-epoch-time
[3]2038-01-19 03:14:07: https://en.wikipedia.org/wiki/Year_2038_problem
[4]std::strftime format: https://zh.cppreference.com/w/cpp/chrono/c/strftime
[5]put_time: https://zh.cppreference.com/w/cpp/io/manip/put_time
[6]get_time: https://zh.cppreference.com/w/cpp/io/manip/get_time
[7]編譯時有理數(shù)算術(shù): https://zh.cppreference.com/w/cpp/numeric/ratio
[8]Date and time utilities: https://en.cppreference.com/w/cpp/chrono
[9]N4842:《Working Draft, Standard for Programming Language C++》: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4842.pdf
[10]cppreference: Date and time utilities: https://en.cppreference.com/w/cpp/chrono
[11]cppreference: Standard library header : https://en.cppreference.com/w/cpp/header/ctime
文章鏈接:https://paul.pub/cpp-date-time/
