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

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

下面是一個(gè)代碼示例:
auto now = chrono::system_clock::now();
time_t time = chrono::system_clock::to_time_t(now);
cout << "Now is: " << ctime(&time) << endl;
時(shí)長(zhǎng)
ratio
人類對(duì)于精度的要求是沒(méi)有止境的。雖然目前的精度已經(jīng)從秒到毫秒,微妙甚至納秒。但很難說(shuō)今后還會(huì)不會(huì)有更高精度的要求。
前面我們已經(jīng)看到,為了將精度從秒變成納秒,C++17增加了timespec_get函數(shù)和timespec類型。如果以后還有更高的精度要求,C++標(biāo)準(zhǔn)還需要添加更多的函數(shù)和類型嗎?老實(shí)說(shuō),這不是一個(gè)好的設(shè)計(jì)。
為了解決這個(gè)問(wèn)題,C++11中添加了一個(gè)新的頭文件和類型,那就是:ratio。
std::ratio描述了編譯時(shí)的有理數(shù),這是一個(gè)模板類。有了這個(gè)類型之后,我們就可以表示任意精度的值了。
例如:相對(duì)于秒來(lái)說(shuō),毫秒是11,00011,000,微妙是11,000,00011,000,000,納秒是11,000,000,00011,000,000,000。通過(guò)ratio可以這樣表達(dá):
std::ratio<1, 1000> milliseconds;
std::ratio<1, 1000000> microseconds;
std::ratio<1, 1000000000> nanoseconds;
其實(shí)上,<ratio>頭文件中包含了很多以10為基數(shù)的各種分?jǐn)?shù),具體可以見(jiàn)這里:編譯時(shí)有理數(shù)算術(shù)[7]。
我們只需要通過(guò):std::milli,std::micro,std::nano使用就好了。
當(dāng)然,ratio能表達(dá)的數(shù)值不僅僅是以10為基底的。對(duì)于任意的分?jǐn)?shù)都可以表達(dá),例如: 5757,591023591023等等。
對(duì)于一個(gè)具體的ratio來(lái)說(shuō),可以通過(guò)den獲取分母的值,num獲取分子的值。
不僅僅如此,<ratio>頭文件還包含了:ratio_add,ratio_subtract,ratio_multiply,ratio_divide來(lái)完成分?jǐn)?shù)的加減乘除四則運(yùn)算。
例如,想要計(jì)算57+59102357+591023,可以這樣寫(xiě):
ratio_add<ratio<5, 7>, ratio<59, 1023>> result;
double value = ((double) result.num) / result.den;
cout << result.num << "/" << result.den << " = " << value << endl;
請(qǐng)注意:無(wú)論是分子還是分母,都是一個(gè)整形。在C++中,整形的除法結(jié)果仍然是整形,多余部分會(huì)被丟棄,因此想要獲取double類型的結(jié)果,需要先將其轉(zhuǎn)換成double。
這段代碼輸出的結(jié)果是:
5528/7161 = 0.771959
時(shí)長(zhǎng)類型
類模板 std::chrono::duration 表示時(shí)間間隔。有了ratio之后,表達(dá)時(shí)長(zhǎng)就很方便了,下面是chrono庫(kù)中提供的很常用的幾個(gè)時(shí)長(zhǎng)單位:
| 類型 | 定義 |
|---|---|
| std::chrono::nanoseconds | duration</至少 64 位的有符號(hào)整數(shù)類型/, std::nano> |
| std::chrono::microseconds | duration</至少 55 位的有符號(hào)整數(shù)類型/, std::micro> |
| std::chrono::milliseconds | duration</至少 45 位的有符號(hào)整數(shù)類型/, std::milli> |
| std::chrono::seconds | duration</至少 35 位的有符號(hào)整數(shù)類型/> |
| std::chrono::minutes | duration</至少 29 位的有符號(hào)整數(shù)類型/, std::ratio<60? |
| std::chrono::hours | duration</至少 23 位的有符號(hào)整數(shù)類型/, std::ratio<3600? |
duration類的count()成員函數(shù)返回具體數(shù)值。
時(shí)長(zhǎng)運(yùn)算
時(shí)長(zhǎng)之間最常用的運(yùn)算自然是相加或者相減,這個(gè)通過(guò)“+”,“-”就可以完成。
除此之外,chrono庫(kù)中還提供了下面幾個(gè)常用的函數(shù):
| 函數(shù) | 說(shuō)明 |
|---|---|
| duration_cast | 進(jìn)行時(shí)長(zhǎng)的轉(zhuǎn)換 |
| floor(C++17) | 以向下取整的方式,將一個(gè)時(shí)長(zhǎng)轉(zhuǎn)換為另一個(gè)時(shí)長(zhǎng) |
| ceil(C++17) | 以向上取整的方式,將一個(gè)時(shí)長(zhǎng)轉(zhuǎn)換為另一個(gè)時(shí)長(zhǎng) |
| round(C++17) | 轉(zhuǎn)換時(shí)長(zhǎng)到另一個(gè)時(shí)長(zhǎng),就近取整,偶數(shù)優(yōu)先 |
| abs(C++17) | 獲取時(shí)長(zhǎng)的絕對(duì)值 |
例如:想要知道2個(gè)小時(shí)零5分鐘一共是多少秒,可以這樣寫(xiě):
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開(kāi)始,你甚至可以用字面值來(lái)描述常見(jiàn)的時(shí)長(zhǎng)。這包括:
h表示小時(shí)min表示分鐘s表示秒ms表示毫秒us表示微妙ns表示納秒
這些字面值位于std::chrono_literals命名空間下。于是,可以這樣表達(dá)2個(gè)小時(shí)以及5分鐘:
using namespace std::chrono_literals;
auto two_hours = 2h;
auto five_minutes = 5min;
時(shí)間點(diǎn)
時(shí)間點(diǎn)中包含了時(shí)鐘和時(shí)長(zhǎng)兩個(gè)信息,類模板 std::chrono::time_point 表示時(shí)間中的一個(gè)點(diǎn)。
時(shí)鐘的now()now()函數(shù)返回的值就是一個(gè)時(shí)間點(diǎn)。time_point中的time_since_epoch()返回從其時(shí)鐘起點(diǎn)開(kāi)始的時(shí)長(zhǎng)。
時(shí)間點(diǎn)運(yùn)算
時(shí)間點(diǎn)有加法操作和減法操作,與我們的常識(shí)相一致:
時(shí)間點(diǎn) + 時(shí)長(zhǎng) = 時(shí)間點(diǎn)
時(shí)間點(diǎn) - 時(shí)間點(diǎn) = 時(shí)長(zhǎng)
例如:可以通過(guò)兩個(gè)時(shí)間點(diǎn)相減計(jì)算一個(gè)時(shí)間間隔,下面是一個(gè)代碼示例:
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;
上面這個(gè)代碼很好的說(shuō)明了:有了duration和duration_cast,我們可以以任意的精度來(lái)描述結(jié)果的值。
除了相加和相減,兩個(gè)時(shí)間點(diǎn)還有比較操作:判斷一個(gè)時(shí)間點(diǎn)在另外一個(gè)時(shí)間點(diǎn)之前還是之后。對(duì)于這些操作,通過(guò)運(yùn)算符:==,!=,<,<=,>,>=來(lái)完成。
展望 C++ 20
通過(guò)上面的講解我們可以看到,自C++11以來(lái),日期時(shí)間庫(kù)功能獲得了極大的提升。并且,在C++14和C++17上也有一些小改進(jìn)。
但如果你使用過(guò)Java類庫(kù),就會(huì)覺(jué)得差距還是比較大。Java中的相關(guān)API早就有對(duì)于日歷,時(shí)區(qū)等方面更好的支持。
對(duì)于這一點(diǎn), 在C++下一個(gè)標(biāo)準(zhǔn)C++20中,將有相應(yīng)的支持。這些改進(jìn)包括:
更多類型的時(shí)鐘,例如:utc_clock,tai_lock,gps_clock等。 時(shí)鐘以及時(shí)間點(diǎn)的轉(zhuǎn)換 Calendar支持 時(shí)區(qū)支持
關(guān)于這一點(diǎn),你可以看這個(gè)鏈接: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ù)算術(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/
