在平時的開發過程中,難免會遇到時間相關的處理,比如心跳、定時任務、超時處理等等,總是很難在衆多時間處理函數中選擇一個。在假期這幾天把經常用到的一些時間處理函數整理了一下,算是個梳理也是備忘吧。
一、一些常用時間類型
1、time_t
在VS2015中,time_t
的定義爲:
#ifndef _CRT_NO_TIME_T
#ifdef _USE_32BIT_TIME_T
typedef __time32_t time_t;
#else
typedef __time64_t time_t;
#endif
#endif
time_t的默認類型是 __time64_t
,而__time64_t
的定義爲 typedef __int64 __time64_t;
可以看到time_t就是一個64位的整型類型。
time_t 保存的是自1970年1月1日00:00:00以來的秒數。這個時間點就是常見的 Epoch 時間
2、tm
struct tm {
int tm_sec; // 秒,正常範圍從 0 到 59,但允許至 61
int tm_min; // 分,範圍從 0 到 59
int tm_hour; // 小時,範圍從 0 到 23
int tm_mday; // 一月中的第幾天,範圍從 1 到 31
int tm_mon; // 月,範圍從 0 到 11
int tm_year; // 自 1900 年起的年數
int tm_wday; // 一週中的第幾天,範圍從 0 到 6,從星期日算起
int tm_yday; // 一年中的第幾天,範圍從 0 到 365,從 1 月 1 日算起
int tm_isdst; // 夏令時
}
3、clock_t
在VS2015中,clock_t
的定義爲:
typedef long clock_t;
4、struct timespec, int clock_gettime(clockid_t clk_id, struct timespec* tp)僅在linux平臺使用
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
通過 clock_gettime()
函數獲取系統的當前時間。
參數 clk_id : 檢索和設置的clk_id指定的時鐘時間。可以設置如下:
CLOCK_REALTIME:系統實時時間,隨系統實時時間改變而改變,即從UTC1970-1-1 0:0:0開始計時,如果系統時間被用戶改成其他,則對應的時間相應改變
CLOCK_REALTIME_COARSE:和CLOCK_REALTIME類似,但是執行速度快,精度低
CLOCK_MONOTONIC:從系統啓動這一刻起開始計時,不受系統時間被用戶改變的影響
CLOCK_MONOTONIC_COARSE :和CLOCK_MONOTONIC類似,但是執行速度快,精度低
CLOCK_BOOTTIME:和 CLOCK_MONOTONIC 類似,但是包括了系統休眠的時間。
CLOCK_PROCESS_CPUTIME_ID:本進程到當前代碼系統CPU花費的時間
CLOCK_THREAD_CPUTIME_ID:本線程到當前代碼系統CPU花費的時間
參數:tp 返回時間值,返回值 0成功, 1失敗。
5、struct timeval, int gettimeofday(struct timeval* tv, struct timezone* tz) 僅在linux平臺使用
struct timeval {
long tv_sec; /* seconds */
long tv_nsec; /* microseconds */
};
通過 gettimeofday()
函數獲取系統的當前時間:
參數:
struct timeval *tv 將帶回當前的系統時間,從UTC1970-1-1 0:0:0開始計時
struct timezone *tz 帶回當前的時區信息,如果不需要刻意設置爲0
timezone結構描述如下:
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich 和格林威治 時間差了多少分鐘 */
int tz_dsttime; /* type of dst correction 日光節約時間的狀態(夏時制)*/
};
二、一些時間相關的函數
1、time_t time(time_t* time);
功能:獲取系統當前時間。
頭文件:
#include <time.h>
返回值:該函數返回系統當前的日曆時間,自1970年1月1日00:00 以來經過的秒數。如果系統沒有時間,返回-1
參數:如果time不爲nullptr,則返回值也存儲在time指向的對象中
(1)、簡單使用
#include <iostream>
#include <time.h>
int main()
{
time_t result = time(nullptr);
std::cout << "自Epoch以來的秒數:" << result << std::endl;
system("pause");
}
(2)、結果
自Epoch以來的秒數:1522943543
請按任意鍵繼續…
2、struct tm* localtime(const time_t* time);
功能:獲取一個指示本地時間的
struct tm
結構頭文件:
#include <time.h>
返回值:返回指向
struct tm
結構的指針,該結構帶有被填充的時間信息,使用本地時區表示。(struct tm
結構定義見上文)參數:當前時間
(1)、簡單使用
#include <iostream>
#include <time.h>
int main()
{
time_t result = time(nullptr);
struct tm* formatTime = localtime(&result);
std::cout
<< "tm_sec: " << formatTime->tm_sec << std::endl
<< "tm_min: " << formatTime->tm_min << std::endl
<< "tm_hour: " << formatTime->tm_hour << std::endl
<< "tm_mday: " << formatTime->tm_mday << std::endl
<< "tm_mon: " << formatTime->tm_mon << std::endl
<< "tm_year: " << formatTime->tm_year << std::endl
<< "tm_wday: " << formatTime->tm_wday << std::endl
<< "tm_yday: " << formatTime->tm_yday << std::endl
<< "tm_isdst: " << formatTime->tm_isdst << std::endl;
system("pause");
}
(2)、結果
tm_sec: 11
tm_min: 24
tm_hour: 0
tm_mday: 6
tm_mon: 3
tm_year: 118
tm_wday: 5
tm_yday: 95
tm_isdst: 0
請按任意鍵繼續…
3、char* ctime(const time_t* time);
功能:獲取表示當前時間的字符串指針
頭文件:
#include <time.h>
返回值:返回表示當前時間的字符串指針,字符串形式
Www Mmm dd hh:mm:ss yyyy\n\0
,其中,Www 表示星期幾,Mmm 是以字母表示的月份,dd 表示一月中的第幾天,hh:mm:ss 表示時間,yyyy 表示年份。參數:當前時間
(1)、簡單使用
#include <iostream>
#include <time.h>
int main()
{
time_t result = time(nullptr);
std::cout << "現在時間是:" << ctime(&result);
system("pause");
}
(2)、結果
現在時間是:Fri Apr 6 00:05:27 2018
請按任意鍵繼續…
4、char* asctime(const struct tm* time);
功能:獲取表示當前時間的字符串指針 (ps: 功能及返回值同
ctime
,但是參數不同)頭文件:
#include <time.h>
返回值:返回一個指向字符串的指針,字符串包含了 time 所指向結構中存儲的信息,字符串形式
Www Mmm dd hh:mm:ss yyyy\n\0
,其中,Www 表示星期幾,Mmm 是以字母表示的月份,dd 表示一月中的第幾天,hh:mm:ss 表示時間,yyyy 表示年份參數:指向
struct tm
結構的當前時間的指針
(1)、簡單使用
#include <iostream>
#include <time.h>
int main()
{
time_t result = time(nullptr);
struct tm* formatTime = localtime(&result);
std::cout << "現在時間是:" << asctime(formatTime);
system("pause");
}
(2)、結果
現在時間是:Fri Apr 6 00:41:41 2018
請按任意鍵繼續…
5、size_t strftime(char* strDest, size_t maxsize, const char* format, const struct tm* time);
作用:自定義日期時間輸出格式
頭文件:
#include <time.h>
返回值:
參數:
strDest
用來存放格式化後的字符串緩存,maxsize
指定最多可以輸出的字符數,format
格式化字符串,time
要轉換的時間
格式化字符串 | 含義 | 格式化字符串 | 含義 | 格式化字符串 | 含義 | 格式化字符串 | 含義 |
---|---|---|---|---|---|---|---|
%a | 星期幾的簡寫 | %F | 年 - 月 - 日 | %n | 新行符 | %V | 每年的第幾周,使用基於周的年 |
%A | 星期幾的全稱 | %g | 年份的後兩位數字,使用基於周的年 | %p | 本地的AM或PM的等價顯示 | %w | 十進制表示的星期幾(值從0到6,星期天爲0) |
%b | 月分的簡寫 | %G | 年分,使用基於周的年 | %r | 12小時的時間 | %W | 每年的第幾周,把星期一做爲第一天(值從0到53) |
%B | 月份的全稱 | %h | 簡寫的月份名 | %R | 顯示小時和分鐘:hh : mm | %x | 標準的日期串 |
%c | 標準的日期的時間串 | %H | 24小時制的小時 | %S | 十進制的秒數 | %X | 標準的時間串 |
%C | 年份的後兩位數字 | %I | 12小時制的小時 | %t | 水平製表符 | %y | 不帶世紀的十進制年份(值從0到99) |
%d | 十進制表示的每月的第幾天 | %j | 十進制表示的每年的第幾天 | %T | 顯示時分秒:hh : mm : ss | %Y | 帶世紀部分的十進制年份 |
%D | 月 / 天 / 年 | %m | 十進制表示的月份 | %u | 每週的第幾天,星期一爲第一天 (值從0到6,星期一爲0) | %z %Z | 時區名稱,如果不能得到時區名稱則返回空字符。 |
%e | 在兩字符域中,十進制表示的每月的第幾天 | %M | 十進制表示的分鐘數 | %U | 第年的第幾周,把星期日做爲第一天(值從0到53) | %% | 百分號 |
(1)、簡單使用
#include <iostream>
#include <time.h>
int main(void)
{
time_t result = time(nullptr);
struct tm *localTime = localtime(&result);
char outBuffer[40] = { 0 };
strftime(outBuffer, 40, "%Y-%m-%d %H:%M:%S", localTime);
std::cout << "now: " << result << " = " << outBuffer << std::endl;
system("pause");
}
(2)、結果
now: 1522951679 = 2018-04-06 02:07:59
請按任意鍵繼續…
5、struct tm* gmtime(const time_t* time);
功能:使用
time_t
的值來填充struct tm
結構,使用格林尼治標準時間(GMT)表示 。(ps: 可以對比localtime
函數)頭文件:
#include <time.h>
返回值:被填充了的
struct tm
結構參數:表示日曆時間的
time_t
類型的指針
#include <iostream>
#include <time.h>
int main()
{
time_t result = time(nullptr);
struct tm* formatTime = gmtime(&result);
std::cout
<< "tm_sec: " << formatTime->tm_sec << std::endl
<< "tm_min: " << formatTime->tm_min << std::endl
<< "tm_hour: " << formatTime->tm_hour << std::endl
<< "tm_mday: " << formatTime->tm_mday << std::endl
<< "tm_mon: " << formatTime->tm_mon << std::endl
<< "tm_year: " << formatTime->tm_year << std::endl
<< "tm_wday: " << formatTime->tm_wday << std::endl
<< "tm_yday: " << formatTime->tm_yday << std::endl
<< "tm_isdst: " << formatTime->tm_isdst << std::endl;
system("pause");
}
tm_sec: 30
tm_min: 57
tm_hour: 16
tm_mday: 5
tm_mon: 3
tm_year: 118
tm_wday: 4
tm_yday: 94
tm_isdst: 0
請按任意鍵繼續…
可以看到:日期時間較localtime不一樣了,格林尼治標準時間(GMT)比當地時間要晚8個小時。
6、time_t mktime(struct tm* time);
功能:與
gmtime
函數功能相反,使用一個struct tm
類型的指針獲取一個日曆時間 (ps,若參數time
指向的是格林尼治標準時間,返回的time_t
也是格林尼治標準時間,比當地時間晚8小時;若參數time
指向的是本地時間,返回的也是本地日期時間)頭文件:
#include <time.h>
返回值:日曆時間
參數:已經被填充的
struct tm
結構指針
舉例省略
7、double difftime(time_t time2, time_t time1);
功能:返回
time2
與time1
之間相差的秒數,time2
與time1
都是日曆時間,都表示距離1970年1月1日00::00:00的秒數頭文件:
#include <time.h>
返回值:兩個時間點之間相差的秒數
參數:兩個日曆時間
(1)、簡單使用
#include <iostream>
#include <windows.h>
#include <time.h>
int main()
{
time_t time1 = time(nullptr);
Sleep(3000); // 睡眠3秒
time_t time2 = time(nullptr);
std::cout << "程序運行的秒數:" << difftime(time2, time1) << std::endl;
system("pause");
}
(2)、結果
程序運行的秒數:3
請按任意鍵繼續…
8、clock_t clock(void);
功能:返回”進程開始啓動”到“程序中調用
clock()
函數”時之間的CPU時鐘計時單元(clock tick)數,通常使用兩次clock調用,返回之間CPU使用時間頭文件:
#include <time.h>
返回值:CPU時鐘計時單元數
參數:無
(1)、簡單使用
#include <iostream>
#include <windows.h>
#include <time.h>
int main()
{
clock_t start = clock();
Sleep(1234); // 睡眠1234毫秒
clock_t end = clock();
std::cout
<< "start: " << start << std::endl
<< "end: " << end << std::endl
<< "CLOCKS_PER_SEC: " << CLOCKS_PER_SEC << std::endl
<< "程序運行的秒數:" << (double)(end - start) / CLOCKS_PER_SEC << std::endl;
system("pause");
}
(2)、結果
start: 253
end: 1487
CLOCKS_PER_SEC: 1000
程序運行的秒數:1.234
請按任意鍵繼續…
可以看到,clock()
可以精確到毫秒
三、一些日期時間庫
1、std::chrono
std::chrono
是C++11
在boost
中引入的,用於C++的時間相關的處理。
(1)、std::chrono 庫主要實現了三個組件,分別是:
- duration // 持續時間或時間段
- time_point // 時間點
- clock // 時鐘類型,時鐘又分爲三類:
- system_clock // 系統的時鐘
- steady_clock // 永遠不會被調整的單調時鐘,始終以統一的速度在增長
- high_resolution_clock // 提供儘可能小的滴答週期以及更高的分辨率
(2)、使用方式
- 要使用
std::chrono
庫,需要#include <chrono>
std::chrono
庫的所有組件,實現都在std::chrono
命名空間下,所以各個組件的調用都要加上std::chrono
前綴
(3)、三個組件的介紹及使用
duration
持續時間對象通過諸如分鐘,兩小時或十毫秒的計數來表示時間跨度。例如:
“60秒”這個持續時間:
可以用由60個週期爲1s的時間長度表示;也可以由1個週期爲1min的時間長度表示。
在
std::chrono
中,duration
可表示六種持續時間,分別是: 小時、分鐘、秒、毫秒、微秒、納秒,定義如下:// duration TYPEDEFS typedef duration<long long, nano> nanoseconds; typedef duration<long long, micro> microseconds; typedef duration<long long, milli> milliseconds; typedef duration<long long> seconds; typedef duration<int, ratio<60> > minutes; typedef duration<int, ratio<3600> > hours;
duration
的聲明如下:// CLASS TEMPLATE duration template<class _Rep, class _Period = ratio<1> > class duration;
可以看到,
duration
是一個模板類。可以把_Rep
理解爲上面提到的多少個週期,比如60,把_Period
理解爲一個週期有多長時間。 六種持續時間都是duration
根據不同模板參數 typedef 而來的。
這裏簡單說一下週期的默認參數
ratio
這個類型。ratio
的聲明如下:template<intmax_t _Nx, intmax_t _Dx = 1> struct ratio;
模板類
ratio
有兩個數值型模板參數,可以簡單的把ratio
想象成一個比率
類型。_Nx
作爲分子,_Dx
作爲分母。根據_Nx
與_Dx
的相對大小區別不同的時間分辨率。幾個分辨率的定義:
typedef ratio<1, 1000000000> nano; typedef ratio<1, 1000000> micro; typedef ratio<1, 1000> milli;
以上對
duration
進行了簡單介紹,下面看下duration
的使用吧!#include <iostream> #include <chrono> using namespace std::chrono; int main() { // 定義 '100毫秒的持續時間' duration<long long, std::milli> one_handurd_millis(100); // 或者這樣定義: milliseconds one_handurd_millis(100); std::cout << "分辨率(一個週期長): " << duration<long long, std::milli>::period::num << "/" << duration<long long, std::milli>::period::den << " s" << std::endl << "週期數: " << one_handurd_millis.count() << std::endl; one_handurd_millis += milliseconds(30); std::cout << "加上30ms後,原時間長度變爲: " << one_handurd_millis.count() << std::endl; system("pause"); }
結果:
分辨率(一個週期長): 1/1000 s
週期數: 100
加上30ms後,原時間長度變爲: 130
請按任意鍵繼續…clock
三種時鐘類型都是作爲
time_point
類型的參數,單獨看時鐘類型沒有意義。只是
system_clock
和steady_clock
都有一個靜態成員函數:now()
可用。#include <iostream> #include <chrono> using namespace std::chrono; int main() { std::cout << system_clock::now().time_since_epoch().count() << std::endl; std::cout << steady_clock::now().time_since_epoch().count() << std::endl; time_t t = system_clock::to_time_t(system_clock::now()); std::cout << ctime(&t); system("pause"); }
結果:
15230306967929631
90472482570038
Sat Apr 7 00:04:56 2018
請按任意鍵繼續…time_point
時間點需要以某一個時鐘的計算爲基準,使用
time_point
需要一個時鐘類型。#include <iostream> #include <chrono> using namespace std::chrono; int main() { system_clock::time_point tp = system_clock::now(); // 或者這樣定義:time_point<system_clock> tp; time_t t = system_clock::to_time_t(tp); std::cout << "距離Epoch時間點已經過去: " << duration_cast<milliseconds>(tp.time_since_epoch()).count() << "ms" << std::endl << "現在時間是: " << ctime(&t); tp += hours(24); time_t t1 = system_clock::to_time_t(tp); std::cout << "24小時後: " << ctime(&t1); system("pause"); }
結果:
距離Epoch時間點已經過去: 1523032933770ms
現在時間是: Sat Apr 7 00:42:13 2018
24小時後: Sun Apr 8 00:42:13 2018
請按任意鍵繼續…
2、Poco::Timestamp 和 Poco::DateTime
(1)、Poco::Timestamp
Poco::Timestamp
內部存儲了一個距離Epoch
時間點的微秒級整型變量,主要的對外接口有以下幾個:
- 默認構造函數。使用
Poco::Timestamp
的默認構造函數會使用當前時間構造一個時間戳。例如:Poco::Timestamp ts;
。 - 更新時間戳。使用
void update()
方法會把時間戳更新到當前時間。 - 時間戳之間的算術比較運算符。
==、!=、>、>=、<、<=
。 - 時間戳的增減。
+、-、+=、-=
,這些方法的參數有Timestamp
,也有另外的Poco::Timespan
(時間間隔) ,如果需要深入瞭解這個類,可以看官網文檔: https://pocoproject.org/docs/Poco.Timespan.html 。 - 獲取超時時間以及判斷是否超過指定時間間隔。
TimeDiff elapsed() const;
和bool isElapsed(TimeDiff interval) const;
。 - 與
Epoch
和UTC
時間的相互轉化。std::time_t epochTime() const;
和UtcTimeVal utcTime() const;
靜態函數static Timestamp fromEpochTime(std::time_t t);
和static Timestamp fromUtcTime(UtcTimeVal val);
。
在服務器裏面,要維護多個客戶端的心跳,對每個客戶端都用一個定時器維護心跳是不可能的,這時候 Poco::TImestamp
用來判斷心跳是否超時以及是否到了發送心跳的時間就比較合適.
(2)、Poco::DateTime
Poco::DateTime
內部存儲了 short 類型的年、月、日、時、分、秒、毫秒、微秒
以及一個 int64 類型的分辨率爲100納秒的utc時間戳
。跟Poco::Timestamp
相比,Poco::DateTime
主要是對 ”年月日時分秒“ 進行操作與獲取,接口類型也與Poco::Timestamp
類似,而這些在程序中較少用到,代碼也不多,就不花篇幅講述了。Poco::DateTime
的接口文檔可以從官網獲取 https://pocoproject.org/docs/Poco.DateTime.html 。
今天就先寫到這兒,本來準備把 boost::date_time
也一塊比較下,看了下書,發現 date_time
庫很雜,而且 date_time
庫還需要編譯安裝,boost
在平時的開發中用的也不多,就不放到這裏了。