Linux 下的時間編程總結

jopen 10年前發布 | 33K 次閱讀 Linux

      在嵌入式編程中中,經常需要輸出系統的當前時間、計算程序的執行時間、使用計時器等。最近也做了不少關于時間的操作,今天就認真總結一下,部分內容是在網上看到的。自己經過驗證總結出來。

1、時間的類型

1.格林威治標準時間
   coordinated universal time(UTC)是世界標準時間,即常說的格林威治標準時間(greenwich mean time,GMT)。
2.日歷時間
   日歷時間(calendar time)是用"一個標準時間點(如1970年1月1日0點)到此時經過的秒數"來表示的時間。

2、常用時間函數的API

1、獲取日歷時間

   #include <time.h>
   time_t time(time_t *tloc)
   函數功能 : 獲取日歷時間,即從1970年1月1日0點到現在所經歷的秒數.
   參數 : 通常設置為NULL

 (time_t在time.h中定義:typedef long int time_t)time_t記錄自1970年1月1日凌晨以來的秒數,在Linux/Unix上定義為long int類型,在32位系統上,time_t最多只能記錄2,147,483,647秒,也就是說到了2038年將會產生溢出(這就是為什么很多嵌入式設備日期只能夠設置到2038年的原因),但在64位系統上不會出現此問題。函數double difftime(time_t time1, time_t time0);用于比較兩個time_t類型的值。

#include <time.h>
 void main()
{
      long lSeconds = 0;
      lSeconds = time(NULL);
      printf("seconds = %ld\n", lSeconds);
}

2、將日歷時間轉換為格林威治標準時間和本地時間

      通常用戶得到日歷時間的秒數后可以將這些秒數轉化為更容易接受的時間表示方式,這些表示時間的方式有格林威治時間、本地時間等首先介紹一個表示時間常用的數據結構。

 struct tm
 {
      int tm_sec;   //秒值
      int tm_min;   //分鐘值
      int tm_hour;  //小時值
      int tm_mday;  //本月第幾日
      int tm_mon;   //本年第幾月
      int tm_year;  //tm_year+1900=哪一年
      int tm_wday;  //本周第幾日
      int tm_yday;  //本年第幾日
      int tm_isdst; //日光節約時間
 }

-----------------------------------------------------------------
 tm_sec         |       秒,范圍是0~59。                        
 tm_min         |       分,范圍是0~59。                        
 tm_hour        |       時,范圍是0~23。                        
 tm_mday        |       日,范圍是1~31。                        
 tm_mon         |       月,范圍是0~11,注意是0到11。           
 tm_year        |       年,自1900以來的年數。                  
 tm_wday        |       星期幾,從星期天開始計算,范圍是0~6。
 tm_yday        |       一年中的哪一天,0~365。                 
 tm_isdst       |       夏令時間(DST)的一個標志。             
-----------------------------------------------------------------
函數介紹:
</div>

struct tm *gmtime(const time_t *timep)

 函數功能  : 將日歷時間轉化為格林威治標準時間,并保存在tm結構
參數:日歷時間的返回值

struct tm* localtime(const time_t *timep)

 函數功能:將日歷時間轉化為本地時間,并保存至tm結構
 參數:日歷時間的返回值。

例:

#include <stdio.h>

include <time.h>

int main(void) {    struct tm *local; time_t t; t = time(null); //獲取日歷時間 local = localtime(&t);//將日歷時間轉化為本地時間,并保存在struct tm結構中 printf("local hour is :%d\n",local->tm_hour); local = gmtime(&t); //將日歷時間轉化為格林威治時間,并保存在struct tm結構中 printf("utc hour is :%d\n",local->tm_hour); return 0; }</pre>

3、時間顯示

      利用函數gmtime()、localtime()可以將日歷時間轉化為格林威治時間和本地時間,雖然用戶可通過結構體tm來獲取這些時間值,但看起來還不方便,最好是將所有的信息,如年、月、日、星期、時、分、秒以字符串的形式顯示出來。

char *asctime(const struct tm *tm)
函數功能:將tm格式的時間轉化為字符串

參數:日歷時間的返回值

例如: SAT Jul 30 08:43:03 2005

該函數必須按照下面3個步驟來進行.
   <1>使用函數time()來獲取日歷時間
   <2>使用函數gmtime()將日歷時間轉化為格林威治標準時間
   <3>使用函數asctime()將tm格式的時間轉化為字符串
例:

#include <time.h>

include <stdio.h>

int main(void) { struct tm ptr; time_t lt; lt = time(null); //獲取日歷時間 ptr = gmtime(<); //轉化為格林威治時間/ printf(asctime(ptr)); //以格林威治時間的字符串方式打印 printf(ctime(<)); //以本地時間的字符串方式打印*/ return 0; }</pre>

char *ctime(const time_t *timep)
 函數功能:將日歷時間轉化為本地時間的字符串形式
 參數:日歷時間的返回值。

   該函數.必須按照下面2個步驟來進行.
   <1>使用函數time()來獲取日歷時間
   <2>使用函數ctime()將日歷時間直接轉化為字符串

PS:有time函數將time_t值轉換為struct tm類型值,函數mktime 可以將struct tm類型值轉換為time_t類型的值,其原型為:

time_t mktime(struct tm *tm);

4、計算事件耗時

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

函數功能 : 獲取從今日凌晨(0:0:0)到現在的時間差,常用于計算事件耗時
參數1 : 存放從今日凌晨(0:0:0)到現在的時間差,時間差以秒或微秒為單位,以結構體形式存放

參數2 : 常設置為null</strong></span>

函數用法:可以在做某件事情之前調用gettimeofday(),在做完該件事情之后調用gettimeofday(),兩個函數的參數1的差就是做該事情所消耗的時間。也經常在程序中作為定時器處理,比如每隔多長時間時間執行一次什么事件。

struct timeval
{
           int tv_sec;    //秒數
           int tv_usec;   //微秒數
}

// 計算時間差,單位毫秒
int elapsed_time(struct timeval *ct, struct timeval *lt)
{
    int elapsed = (ct->tv_sec - lt->tv_sec) * 1000 + (ct->tv_usec - lt->tv_usec) / 1000;
    return elapsed;
}

5、設置系統時鐘

這里要介紹兩個函數,同time和gettimeofday對應,stime和settimeofday,原型如下:

int stime(time_t *t);
int settimeofday(const struct timeval *tv, const struct timezone *tz);
只是settimeofday設置的時間比stime更精確罷了。

6、延時函數

(1)unsigned int sleep(unsigned int seconds)
函數功能 : 使程序睡眠seconds秒
參數 : 需要休眠的秒數
(2)void usleep(unsigned long usec)
函數功能 : 使程序睡眠usec微秒
參數 : 需要休眠的秒數

7、定時器

unsigned alarm(unsigned seconds);</strong></span>

#include <stdio.h>

include <unistd.h>

include <signal.h>

void alarm_handler(int signum) { rintf("Five seconds passed!\n"); } void func(void) { signal(SIGALRM, alarm_handler); alarm(5); pause(); }
int main(void) { func(); return 0; }</pre>

      程序將在5秒之后執行alarm_handler函數,這里還使用了pause函數,用于掛起進程直到捕捉到一個信號時才退出。注意alarm一次只能發送發送一個信號,如果要再次發送信號,需要重新調用alarm函數。

除了alarm外,還可以使用setitimer來設置定時器,使用getitimer來獲取定時器的狀態,原型如下:
int setitimer(int which, const struct itimerval *restrict value, struct itimerval *restrict ovalue);
int getitimer(int which, struct itimerval *value);
說明:

需要包含頭文件sys/time.h
which參數有三種取值:
ITIMER_REAL 按實際時間記時,時間到了之后發送SIGALRM信號,相當于alarm。
ITIMER_VIRTUAL 僅當進程執行時才進行記時,發送SIGVTALRM信號。
ITIMER_PROF 當進程執行時和系統執行該進程時都記時,發送的是SIGPROF信號。

struct itimerval用來指定定時時間,定義如下:


struct itimerval 
{
struct timerval it_interval; / next value /
struct timerval it_value; / current value /
};
</span>

在setitimer函數中,ovalue如果不為空,則保留上次調用設置的值。

3、時間函數的安全用法

      在寫代碼的時候,經常會用到讀取系統時間的函數。localtime函數不是線程安全的。如果在多線程里調用localtime函數,很可能會出現問題。
多線程應用里面,應該用localtime_r函數替代localtime函數,因為localtime_r是線程安全的。同樣gmtime函數和gmtime_r函數


char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);


char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);

struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);

struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);

time_t mktime(struct tm *tm);

         gmtime() 函數將日歷時間timep轉換為用UTC時間表示的時間。它可能返回NULL,比如年份不能放到一個整數中。返回值指向一個靜態分配的結構,該結構可能會被接下來的任何日期和時間函數調用覆蓋。gmtime_r()函數功能與此相同,但是它可以將數據存儲到用戶提供的結構體中。


        localtime() 函數將日歷時間timep轉換為用戶指定的時區的時間。這個函數的行為好像是它調用了tzset(3) 并且將外部變量tzname設置為當前時區的信息,將timezone設為UTC和本地標準時間的差值,并且,如果在一年的部分時間使用日光節約規則時將daylight設置為非空值。返回值指向一個靜態分配的結構,該結構可能會被接下來的任何日期和時間函數調用覆蓋。localtime_r()函數功能與此相同,但是它可以將數據存儲到用戶提供的結構體中。它不需要設置tzname


4、時間函數功能測試

例1:

#include <stdio.h>

include <time.h>

int main() { time_t cur_time = time(NULL); if (cur_time < 0) { perror("time"); return -1; } struct tm utc_tm; if (NULL == gmtime_r(&cur_time, &utc_tm)) { perror("gmtime"); return -1; } struct tm local_tm; if (NULL == localtime_r(&cur_time, &local_tm)) { perror("localtime" ); return -1; } printf("UTC = %s", asctime(&utc_tm)); printf("LOC = %s", asctime(&local_tm)); printf("LOC = %s", ctime(&cur_time)); return 0; }</pre>輸出結果:

xzg@byxc-PDSML:~/test$ gcc -o time time.c 
xzg@byxc-PDSML:~/test$ ./time 
UTC = Tue Jul 15 01:41:08 2014
LOC = Tue Jul 15 09:41:08 2014
LOC = Tue Jul 15 09:41:08 2014
系統時間使用了UTC,可以看到“本地時間= UTC時間 + 8”,輸出正確。

例2:

#include <stdio.h>

include <time.h>

int main() { time_t cur_time = time(NULL); if (cur_time < 0) { perror("time"); return -1; }

struct tm *utc_tm = gmtime( &cur_time );
if( NULL == utc_tm )
{
    perror("gmtime" );
    return -1;
}

<strong>printf("UTC = %s", asctime(utc_tm) );</strong>

struct tm *local_tm = localtime( &cur_time );
if( NULL == local_tm )
{
    perror("localtime" );
    return -1;
}

printf("LOC = %s", asctime(local_tm) );
printf("LOC = %s", ctime(&cur_time) );
return 0;

}</pre>輸出結果:

xzg@byxc-PDSML:~/test$ gcc -o time1 time1.c 
xzg@byxc-PDSML:~/test$ ./time1
UTC = Tue Jul 15 02:00:38 2014
LOC = Tue Jul 15 10:00:38 2014
LOC = Tue Jul 15 10:00:38 2014
xzg@byxc-PDSML:~/test$ 

例3:

#include <stdio.h>

include <time.h>

int main() { time_t cur_time = time(NULL); if (cur_time < 0) { perror("time"); return -1; }

struct tm *utc_tm = gmtime( &cur_time );
if( NULL == utc_tm )
{
    perror("gmtime" );
    return -1;
}


struct tm *local_tm = localtime( &cur_time );
if( NULL == local_tm )
{
    perror("localtime" );
    return -1;
}
    <strong>printf("UTC = %s", asctime(utc_tm) );</strong>
printf("LOC = %s", asctime(local_tm) );
printf("LOC = %s", ctime(&cur_time) );
return 0;

}</pre>輸出結果:

xzg@byxc-PDSML:~/test$ gcc -o time1 time1.c 
xzg@byxc-PDSML:~/test$ ./time1              
UTC = Tue Jul 15 10:03:26 2014
LOC = Tue Jul 15 10:03:26 2014
LOC = Tue Jul 15 10:03:26 2014

         這程序輸出有錯,UTC時間和本地時間相同了“可能會被接下來的任何日期和時間函數調用覆蓋”造成的。

總結:

        使用gmtimelocaltime后要立即處理結果,否則返回的指針指向的內容可能會被覆蓋,一個好的方法是使用gmtime_rlocaltime_r,由于使用了用戶分配的內存,這兩個函數是不會出錯的。

來自:http://blog.csdn.net/sin0803/article/details/37767865

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!