Linux時間的獲取與使用

oscarwin發表於2019-03-02

Linux系統時間有兩種。

(1)日曆時間。該值是自協調世界時(UTC)1970年1月1日00:00:00這個特定時間以來所經過的秒數累計值。基本資料型別用time_t儲存。最後通過轉換才能得到我們平時所看到的24小時制或者12小時間制的時間。

(2)程式時間。也被稱為CPU時間,用以度量程式使用的中央處理器資源。程式時間以時鐘滴答計算。

獲取時間戳

time()

#include <time.h>
time_t time(time_t *calptr)
複製程式碼
  • time返回當前時間的時間戳,也就是從世界時到現在的秒數;
  • time_t實際就是一個uint64_t
  • calptr不為空時,時間戳也會寫入到該指標中;

呼叫示例:

#include <time.h>
#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
	time_t curTime;
	curTime = time(NULL);
	cout << curTime << endl;
	return 0;
}
複製程式碼

結果:
返回一串數值,如1533287924

gettimeofday()和clock_gettime()

time函式只能得到秒精度的時間,為了獲得更高精度的時間戳,需要其他函式。gettimeofday函式可以獲得微秒精度的時間戳,用結構體timeval來儲存;clock_gettime函式可以獲得納秒精度的時間戳,用結構體timespec來儲存。

#include <sys/time.h>

int gettimeofday(struct timeval *tp, void *tzp);
因為歷史原因tzp的唯一合法值是NULL,因此呼叫時寫入NULL即可。

int clock_gettime(clockid_t clock_id, strcut timespec *tsp);
clock_id有多個選擇,當選擇為CLOCK_REALTIME時與time的功能相似,但是時間精度更高。
複製程式碼

兩個函式使用的結構體定義如下:

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

struct timespec
{
	time_t tv_sec;  //秒
	long tv_nsec;   //納秒
};
複製程式碼

呼叫示例:

#include <time.h>
#include <sys/time.h>
#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
	time_t dwCurTime1;
	dwCurTime1 = time(NULL);

    struct timeval stCurTime2;
    gettimeofday(&stCurTime2, NULL);

    struct timespec stCurTime3;
    clock_gettime(CLOCK_REALTIME, &stCurTime3);

    cout << "Time1: " << dwCurTime1 << "s" << endl;
    cout << "Time2: " << stCurTime2.tv_sec << "s, " << stCurTime2.tv_usec << "us" << endl;
    cout << "Time3: " << stCurTime3.tv_sec << "s, " << stCurTime3.tv_nsec << "ns" << endl;

	return 0;
}
複製程式碼

結果:

編譯時要在編譯命令最後加上-lrt連結Real Time動態庫,如
g++ -o time2 test_time_linux_2.cpp -lrt

Time1: 1533289490s
Time2: 1533289490s, 133547us
Time3: 1533289490s, 133550060ns
複製程式碼

視覺化時間

tm結構體

得到的時間戳不能直觀的展示現在的時間,為此需要使用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*/  1+p->tm_mon;
    int tm_year;  /*年, 從1900至今已經多少年*/  1900+ p->tm_year;
    int tm_wday; /*星期,一週中的第幾天, 從星期日算起,0-6*/
    int tm_yday; /*從今年1月1日到目前的天數,範圍0-365*/
    int tm_isdst; /*日光節約時間的旗標*/
};
複製程式碼

time_t轉成tm

gmtimelocaltime可以將time_t型別的時間戳轉為tm結構體,用法如下:

struct tm* gmtime(const time_t *timep);
//將time_t表示的時間轉換為沒有經過時區轉換的UTC時間,是一個struct tm結構指標

stuct tm* localtime(const time_t *timep);
//和gmtime功能類似,但是它是經過時區轉換的時間,也就是可以轉化為北京時間。
複製程式碼

固定格式列印時間

得到tm結構體後,可以將其轉為字串格式的日常使用的時間,或者直接從time_t進行轉換,分別可以使用以下兩個函式達到目的。不過這兩個函式只能列印固定格式的時間。

//這兩個函式已經被標記為棄用,儘量使用後面將要介紹的函式
char *asctime(const struct tm* timeptr);
char *ctime(const time_t *timep);
複製程式碼

呼叫示例:

#include <time.h>
#include <sys/time.h>
#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
	time_t dwCurTime1;
	dwCurTime1 = time(NULL);

    struct tm* pTime;
    pTime = localtime(&dwCurTime1);

    char* strTime1;
    char* strTime2;
    strTime1 = asctime(pTime);
    strTime2 = ctime(&dwCurTime1);

    cout << strTime1 << endl;
    cout << strTime2 << endl;

	return 0;
}
複製程式碼

結果:

Fri Aug  3 18:24:29 2018
Fri Aug  3 18:24:29 2018
複製程式碼

靈活安全的時間轉換函式strftime()

上述兩個函式因為可能出現緩衝區溢位的問題而被標記為棄用,因此更加安全的方法是採用strftime方法。

/*
** @buf:儲存輸出的時間
** @maxsize:快取區的最大位元組長度
** @format:指定輸出時間的格式
** @tmptr:指向結構體tm的指標
*/
size_t strftime(char* buf, size_t maxsize, const char *format, const struct tm *tmptr);
複製程式碼

我們可以根據format指向字串中格式,將timeptr中儲存的時間資訊按照format指定的形式輸出到buf中,最多向緩衝區buf中存放maxsize個字元。該函式返回向buf指向的字串中放置的字元數。

函式strftime()的操作有些類似於sprintf():識別以百分號(%)開始的格式命令集合,格式化輸出結果放在一個字串中。格式化命令說明串 strDest中各種日期和時間資訊的確切表示方法。格式串中的其他字元原樣放進串中。格式命令列在下面,它們是區分大小寫的。

%a 星期幾的簡寫
%A 星期幾的全稱
%b 月分的簡寫
%B 月份的全稱
%c 標準的日期的時間串
%C 年份的後兩位數字
%d 十進位制表示的每月的第幾天
%D 月/天/年
%e 在兩字元域中,十進位制表示的每月的第幾天
%F 年-月-日
%g 年份的後兩位數字,使用基於周的年
%G 年分,使用基於周的年
%h 簡寫的月份名
%H 24小時制的小時
%I 12小時制的小時
%j 十進位制表示的每年的第幾天
%m 十進位制表示的月份
%M 十時製表示的分鐘數
%n 新行符
%p 本地的AM或PM的等價顯示
%r 12小時的時間
%R 顯示小時和分鐘:hh:mm
%S 十進位制的秒數
%t 水平製表符
%T 顯示時分秒:hh:mm:ss
%u 每週的第幾天,星期一為第一天 (值從0到6,星期一為0)
%U 第年的第幾周,把星期日做為第一天(值從0到53)
%V 每年的第幾周,使用基於周的年
%w 十進位制表示的星期幾(值從0到6,星期天為0)
%W 每年的第幾周,把星期一做為第一天(值從0到53)
%x 標準的日期串
%X 標準的時間串
%y 不帶世紀的十進位制年份(值從0到99)
%Y 帶世紀部分的十制年份
%z,%Z 時區名稱,如果不能得到時區名稱則返回空字元。
%% 百分號
複製程式碼

呼叫示例:

#include <time.h>
#include <sys/time.h>
#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
	time_t dwCurTime1;
	dwCurTime1 = time(NULL);

    struct tm* pTime;
    pTime = localtime(&dwCurTime1);

    char buf[100];

    strftime(buf, 100, "time: %r, %a %b %d, %Y", pTime);

    cout << buf << endl;

	return 0;
}
複製程式碼

結果:

time: 08:18:12 PM, Fri Aug 03, 2018
複製程式碼

時間函式之間的關係圖

Linux時間的獲取與使用

程式時間

程式時間是程式被建立後使用CPU的時間 ,程式時間被分為以下兩個部分:

  • 使用者CPU時間:在使用者態模式下使用CPU的時間
  • 核心CPU時間:在核心態模式下使用CPU的時間。這是執行核心呼叫或其他特殊任務所需要的時間。

clock函式

clock函式提供了一個簡單的介面用於取得程式時間,它返回一個值描述程式使用的總的CPU時間(包括使用者時間和核心時間),該函式定義如下:

#include <time.h>
clock_t clock(void)
//if error, return -1
複製程式碼

clock函式返回值得計量單位是CLOCKS_PER_SEC,將返回值除以這個計量單位就得到了程式時間的秒數

times函式

times函式也是一個程式時間函式,有更加具體的程式時間表示,函式定義如下:

#include <sys/times.h>
clock_t times(struct tms* buf);

struct tms{
	clock_t tms_utime;
	clock_t tms_stime;
	clock_t tms_cutime;
	clock_t tms_cstime;
};
複製程式碼

times函式雖然返回型別還是clock_t,但是與clock函式返回值的計量單位不同。times函式的返回值得計量單位要通過sysconf(SC_CLK_TCK)來獲得。

Linux系統程式設計手冊上一個完整的使用案例如下:

#include <time.h>
#include <sys/times.h>
#include <unistd.h>
#include <stdio.h>

static void displayProcessTime(const char* msg)
{
	struct tms t;
	clock_t clockTime;
	static long clockTick = 0;

	if (msg != NULL) 
	{
		printf("%s
", msg);
	}

	if (clockTick == 0)
	{
		clockTick = sysconf(_SC_CLK_TCK);
		if (clockTick < 0) return;
	}

	clockTime = clock();
	printf("clock return %ld CLOCKS_PER_SEC (%.2f seconds)
", (long)clockTime, (double)clockTime/CLOCKS_PER_SEC);
	
	times(&t);
	printf("times return user CPU = %.2f; system CPU = %.2f
", (double)t.tms_utime / clockTick, (double)t.tms_stime / clockTick);
}

int main()
{
	printf("CLOCKS_PER_SEC = %ld, sysconf(_SC_CLK_TCK) = %ld
", (long)CLOCKS_PER_SEC, sysconf(_SC_CLK_TCK));

	displayProcessTime("start:");
	for (int i = 0; i < 1000000000; ++i)
	{
		getpid();
	}
	printf("
");
	displayProcessTime("end:");

	return 0;
}
複製程式碼

參考

[1] www.runoob.com/w3cnote/cpp…

[2] Unix高階環境程式設計(第三版)

[3] Unix系統程式設計手冊

相關文章