加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專(zhuān)業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 1 與時(shí)間相關(guān)的定義
    • 2 獲取ISO8601格式的時(shí)間
    • 3 計(jì)算兩個(gè)時(shí)間的間隔
    • 3 測(cè)試代碼
    • 4 總結(jié)
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

Linux-C++獲取當(dāng)前時(shí)間與計(jì)算時(shí)間間隔

11/04 16:40
1529
閱讀需 15 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

嵌入式軟件開(kāi)發(fā)中,有時(shí)會(huì)用到對(duì)日期時(shí)間的判斷與處理,比如記錄某個(gè)事件發(fā)生的時(shí)間,比較某個(gè)時(shí)刻已過(guò)去的時(shí)間等等。

記錄時(shí)間,可以使用ISO8601國(guó)際標(biāo)準(zhǔn)格式的時(shí)間,便于與其它軟件交互時(shí)做到統(tǒng)一。

本篇就來(lái)介紹ISO8601格式時(shí)間的生成以及兩個(gè)ISO8601格式的時(shí)間間隔的計(jì)算。

1 與時(shí)間相關(guān)的定義

在介紹具體的編程實(shí)現(xiàn)之前,需要先了解需要用到的一些與時(shí)間相關(guān)的類(lèi)型定義與函數(shù)接口

1.1 類(lèi)型與結(jié)構(gòu)體

1.1.1 timeval

存儲(chǔ)秒和微秒

struct timeval
{
    long tv_sec; /*秒*/
    long tv_usec; /*微秒*/
};

1.1.2 tm

表示日歷時(shí)間格式的時(shí)間

struct tm {
        int tm_sec;     /* seconds after the minute - [0,59] 秒*/
        int tm_min;     /* minutes after the hour - [0,59] 分鐘*/
        int tm_hour;    /* hours since midnight - [0,23] 小時(shí)*/
        int tm_mday;    /* day of the month - [1,31] 日*/
        int tm_mon;     /* months since January - [0,11],月,使用時(shí)一般會(huì)加1的偏移量 */
        int tm_year;    /* years since 1900,年,使用時(shí)一般會(huì)加1900的偏移量 */
        int tm_wday;    /* days since Sunday - [0,6] 周*/
        int tm_yday;    /* days since January 1 - [0,365] */
        int tm_isdst;   /* daylight savings time flag */
};

1.2 函數(shù)

1.2.1 time

time_t time (time_t *time);

參數(shù)可以為空,用于獲取時(shí)間戳。

將自1970年1月1日以來(lái)經(jīng)過(guò)的秒數(shù)存儲(chǔ)在時(shí)間戳指針time指向的位置(time為空則不做此處理),并返回相等值的臨時(shí)time變量;

1.2.2 gettimeofday

int gettimeofday(struct timeval *tv, struct timezone *tz);

參數(shù)tv不可為空,tz通常不寫(xiě)默認(rèn)為空,用于獲取系統(tǒng)時(shí)間結(jié)構(gòu)(struct tm)。

將自1970年1月1日以來(lái)經(jīng)過(guò)的精度為微秒的時(shí)間存儲(chǔ)于tv結(jié)構(gòu)。獲取時(shí)間成功返回0,失敗返回-1。

1.2.3 localtime

struct tm *localtime (const time_t *time);

參數(shù)time不可為空。將時(shí)間戳time轉(zhuǎn)換為tm結(jié)構(gòu);

1.2.4 localtime_r

struct tm *localtime_r(const time_t *timer, struct tm *buf);

將傳入?yún)?shù)timer表示的秒數(shù)轉(zhuǎn)換為日歷時(shí)間格式,保存結(jié)果在buf,同時(shí)也會(huì)保存結(jié)果一個(gè)全局靜態(tài)變量中,返回這全局靜態(tài)變量的指針。

注:

    localtime_r函數(shù)通過(guò)傳入tm參數(shù)指針保存轉(zhuǎn)換結(jié)果,使localtime_r函數(shù)線程安全。如果使用localtime_r函數(shù)返回值表示日歷,仍然是線程不安全的,通常僅通過(guò)返回值是否為空,判斷l(xiāng)ocaltime_r函數(shù)轉(zhuǎn)換時(shí)間是否成功。

2 獲取ISO8601格式的時(shí)間

2.1 ISO8601時(shí)間格式介紹

國(guó)際標(biāo)準(zhǔn)化組織的國(guó)際標(biāo)準(zhǔn)ISO 8601是日期和時(shí)間的表示方法,全稱(chēng)為《數(shù)據(jù)存儲(chǔ)和交換形式·信息交換·日期和時(shí)間的表示方法》。

最新為ISO8601:2019 ,第一版為ISO8601:1988,第二版為ISO8601:2000。

根據(jù)ISO8601標(biāo)準(zhǔn),北京時(shí)間2024年11月3日16點(diǎn)37分可以表示為:

2024-11-03T16:37:00+08:00

2.1.1 日期格式

標(biāo)準(zhǔn)日期格式為 YYYY-MM-DD,其中:

    YYYY 代表四位數(shù)的年份,如2024;MM 代表兩位數(shù)的月份,范圍01~12;DD 代表兩位數(shù)的日,范圍01~31。

2.1.2 時(shí)間格式

完整時(shí)間表示為:HH:MM:SS

    HH表示兩位數(shù)的小時(shí),24小時(shí)制;MM表示兩位數(shù)的分鐘;SS表示兩位數(shù)的秒;

可進(jìn)一步精確到毫秒,表示為:HH:MM:SS.sss

2.1.3 日期時(shí)間格式

日期和時(shí)間的組合表示為:YYYY-MM-DDTHH:MM:SS

    T是日期和時(shí)間之間的分隔符

注:

“T” 是一個(gè)全球統(tǒng)一且不常見(jiàn)的字符,使用 “T” 可以清楚地區(qū)分日期和時(shí)間這兩個(gè)不同的概念,避免混淆。

2.1.4 時(shí)區(qū)表示

ISO8601支持對(duì)時(shí)區(qū)的標(biāo)準(zhǔn)化表示,使用Z表示協(xié)調(diào)世界時(shí)(UTC),或者使用±hh:mm格式表示與UTC的偏移,例如:

    Z表示 UTC 時(shí)間;+08:00表示比 UTC 快8小時(shí)的時(shí)區(qū);-05:00表示比 UTC 慢5小時(shí)的時(shí)區(qū);

例如:

    2024-03-19T15:26:00Z表示UTC時(shí)間下午3點(diǎn)26分0秒;2024-03-19T15:26:00+08:00表示北京時(shí)間下午3點(diǎn)26分0秒;

2.2 編程實(shí)現(xiàn)ISO8601時(shí)間的獲取

代碼思路如下:

    獲取自1970年1月1日以來(lái)經(jīng)過(guò)的秒和微秒,存儲(chǔ)在timeval中將秒數(shù)通過(guò)localtime_r轉(zhuǎn)換為日歷時(shí)間格式結(jié)合日歷時(shí)間和微妙數(shù),格式化為ISO8601格式的時(shí)間
std::string GetISO8601NowTime()
{
    timeval tv{}; //存儲(chǔ)自1970年1月1日以來(lái)經(jīng)過(guò)的秒和微秒
    gettimeofday(&tv, nullptr); //獲取自1970年1月1日以來(lái)經(jīng)過(guò)的秒和微秒
    
    tm stTM{}; //存儲(chǔ)日歷時(shí)間格式的時(shí)間
    localtime_r(&tv.tv_sec, &stTM); //將傳入?yún)?shù)的秒數(shù)轉(zhuǎn)換為日歷時(shí)間格式
    
    char sTmp[64]{}; //格式化為ISO8601格式的時(shí)間
    sprintf(sTmp, "%04d-%02d-%02dT%02d:%02d:%02d.%03ld",
                   stTM.tm_year + 1900, stTM.tm_mon + 1, stTM.tm_mday,
                   stTM.tm_hour, stTM.tm_min, stTM.tm_sec, tv.tv_usec/1000);
                   
    return std::string(sTmp) + "+08:00"; //這里時(shí)區(qū)暫使用固定的東八區(qū)  
}

3 計(jì)算兩個(gè)時(shí)間的間隔

前面實(shí)現(xiàn)了ISO8601時(shí)間的獲取,如果有兩個(gè)ISO8601格式的時(shí)間,如何計(jì)算這兩個(gè)時(shí)間的間隔呢。

在實(shí)現(xiàn)該功能前,需要再來(lái)介紹需要用到的兩個(gè)函數(shù)。

3.1 函數(shù)

3.1.1 strptime

string parse time。parse,解析,用于將string格式的時(shí)間解析為tm格式

extern char *strptime (__const char *__restrict __s,
                       __const char *__restrict __fmt, 
					 struct tm *__tp);
    參數(shù)1: 輸入一個(gè)char 的指針,可通過(guò)c_str()兼容參數(shù)2: 統(tǒng)一為一個(gè)char的指針, 用于格式控制的字符串指針,可通過(guò)c_str()兼容參數(shù)3: 分解時(shí)間的存儲(chǔ),struct tm類(lèi)型的指針,可定義一個(gè)struct tm類(lèi)型,然后&實(shí)現(xiàn)

strftime:string format time。format,格式。把 time 格式化為 string

3.1.2 mktime

time_t mktime(struct tm *timeptr);

用于將結(jié)構(gòu)體 struct tm 表示的日歷時(shí)間轉(zhuǎn)換為對(duì)應(yīng)的秒數(shù)時(shí)間戳。

3.2 編程實(shí)現(xiàn)

代碼思路如下:

    將string格式的時(shí)間解析為tm格式的日歷時(shí)間再將日歷時(shí)間轉(zhuǎn)換為對(duì)應(yīng)的秒數(shù)時(shí)間戳比較兩個(gè)時(shí)間戳 的差值即可
time_t ISO8601ToTimeT(std::string &dateTime)
{
    tm stTM{}; 
    //%F是一個(gè)代表完整日期的標(biāo)記,等同于%Y-%m-%d; %T是一個(gè)代表完整時(shí)間的標(biāo)記,等同于%H:%M:%S
    strptime(dateTime.c_str(), "%FT%T", &stTM); //將string格式的時(shí)間解析為tm格式
    
    time_t t = mktime(&stTM); //將日歷時(shí)間轉(zhuǎn)換為對(duì)應(yīng)的秒數(shù)時(shí)間戳
    return t;
}

uint64_t TimeDurationSec(std::string &oldT, std::string &newT)
{
    auto oldPoint = std::chrono::system_clock::from_time_t(ISO8601ToTimeT(oldT));
    auto newPoint = std::chrono::system_clock::from_time_t(ISO8601ToTimeT(newT));
    return std::chrono::duration_cast<std::chrono::seconds>(newPoint - oldPoint).count();
}

3 測(cè)試代碼

來(lái)編寫(xiě)一個(gè)測(cè)試代碼來(lái)驗(yàn)證剛才實(shí)現(xiàn)的功能。

    先定義一個(gè)ISO8601格式的已過(guò)去的時(shí)間,作為測(cè)試時(shí)間間隔的old數(shù)據(jù)調(diào)用編寫(xiě)的GetISO8601NowTime獲取當(dāng)前的ISO8601格式的時(shí)間調(diào)用TimeDurationSec來(lái)計(jì)算兩個(gè)時(shí)間的差值,間隔的秒數(shù)以天、小時(shí)、分鐘、秒的形式打印出來(lái)過(guò)去的時(shí)間間隔
#include <stdio.h>
#include <ctime>
#include <sys/time.h>
#include <string>
#include <chrono>

//函數(shù)實(shí)現(xiàn)參考前面代碼

int main()
{
    std::string t1 = "2024-11-01T17:31:09.000";
    std::string t2 = GetISO8601NowTime();
    printf("t1(old):%snt2(now):%sn", t1.c_str(), t2.c_str());
    
    uint64_t deltaTotalSec = TimeDurationSec(t1, t2);
    uint64_t deltaDay = deltaTotalSec / (3600*24);
    uint32_t deltaHour = deltaTotalSec % (3600*24) / 3600;
    uint32_t deltaMin = deltaTotalSec % 3600 / 60;
    uint32_t deltaSec = deltaTotalSec % 60;
    printf("delta sec:%lu(%lu day, %u hour, %u min, %u sec)n", deltaTotalSec, deltaDay, deltaHour, deltaMin, deltaSec);
    
    return 0;
}

運(yùn)行結(jié)果如下:

4 總結(jié)

本篇介紹了ISO8601格式時(shí)間的生成以及兩個(gè)ISO8601格式的時(shí)間間隔的計(jì)算。首先介紹需要用到的一些函數(shù),然后介紹編程實(shí)現(xiàn)的思路,編寫(xiě)代碼,實(shí)現(xiàn)所需的功能,最后進(jìn)行編譯運(yùn)行測(cè)試。

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

控制科學(xué)與工程碩士,日常分享單片機(jī)、嵌入式、C/C++、Linux等學(xué)習(xí)經(jīng)驗(yàn)干貨~