C/C++ 日期時間整理備忘

在平時的開發過程中,難免會遇到時間相關的處理,比如心跳、定時任務、超時處理等等,總是很難在衆多時間處理函數中選擇一個。在假期這幾天把經常用到的一些時間處理函數整理了一下,算是個梳理也是備忘吧。

一、一些常用時間類型

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);

功能:返回time2time1之間相差的秒數,time2time1都是日曆時間,都表示距離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::chronoC++11boost 中引入的,用於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)、三個組件的介紹及使用

  1. 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
    請按任意鍵繼續…

  2. clock

    三種時鐘類型都是作爲 time_point 類型的參數,單獨看時鐘類型沒有意義。

    只是 system_clocksteady_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
    請按任意鍵繼續…

  3. 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 時間點的微秒級整型變量,主要的對外接口有以下幾個:

  1. 默認構造函數。使用 Poco::Timestamp 的默認構造函數會使用當前時間構造一個時間戳。例如:Poco::Timestamp ts;
  2. 更新時間戳。使用 void update() 方法會把時間戳更新到當前時間。
  3. 時間戳之間的算術比較運算符。==、!=、>、>=、<、<=
  4. 時間戳的增減。+、-、+=、-=,這些方法的參數有 Timestamp ,也有另外的 Poco::Timespan (時間間隔) ,如果需要深入瞭解這個類,可以看官網文檔: https://pocoproject.org/docs/Poco.Timespan.html
  5. 獲取超時時間以及判斷是否超過指定時間間隔。TimeDiff elapsed() const;bool isElapsed(TimeDiff interval) const;
  6. EpochUTC 時間的相互轉化。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在平時的開發中用的也不多,就不放到這裏了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章