C語言時間操作



UNIX操作系統根據計算機產生的年代和應用采用1970年1月1日作為UNIX的紀元時間,1970年1月1日0點作為計算機表示時間的是中間點,將從1970年1月1日開始經過的秒數用一個整數存放,這種高效簡潔的時間表示方法被稱為“Unix時間紀元”,向左和向右偏移都可以得到更早或者更后的時間。

在實際開發中,對日期和時間的操作場景非常多,例如程序啟動和退出的時間,程序執行任務的時間,數據生成的時間,數據處理的各環節的時間等,無處不在。

在學習時間之前,請把Linux操作系統的時區設置為中國上海時間。

一、time_t別名

在C語言中,用time_t來表示時間數據類型,它是一個long(長整數)類型的別名,在time.h文件中定義,表示一個日歷時間,是從1970年1月1日0時0分0秒到現在的秒數。

typedef long time_t;  

二、time庫函數

time函數的用途是返回一個值,也就是從1970年1月1日0時0分0秒到現在的秒數。

time函數是C語言標准庫中的函數,在time.h文件中聲明。

time_t time(time_t *t);

time函數有兩種調用方法:

time_t tnow;
tnow =time(0);     // 將空地址傳遞給time函數,並將time返回值賦給變量tnow

time(&tnow);       // 將變量tnow的地址作為參數傳遞給time函數

您可以寫代碼測試一下這兩種方式,效果完全相同。

三、tm結構體

time_t只是一個長整型,不符合我們的使用習慣,需要轉換成可以方便表示時間的結構體,即tm結構體,tm結構體在time.h中聲明,如下:

struct tm
{
  int tm_sec;     // 秒:取值區間為[0,59] 
  int tm_min;     // 分:取值區間為[0,59] 
  int tm_hour;    // 時:取值區間為[0,23] 
  int tm_mday;    // 日期:一個月中的日期:取值區間為[1,31]
  int tm_mon;     // 月份:(從一月開始,0代表一月),取值區間為[0,11]
  int tm_year;    // 年份:其值等於實際年份減去1900
  int tm_wday;    // 星期:取值區間為[0,6],其中0代表星期天,1代表星期一,以此類推 
  int tm_yday;    // 從每年的1月1日開始的天數:取值區間為[0,365],其中0代表1月1日,1代表1月2日,以此類推 
  int tm_isdst;   // 夏令時標識符,該字段意義不大,我們不用夏令時。
};

這個結構定義了年、月、日、時、分、秒、星期、當年中的某一天、夏令時。用這個結構體可以很方便的顯示時間。

四、localtime庫函數

localtime函數用於把time_t表示的時間轉換為struct tm結構體表示的時間,函數返回struct tm結構體的地址。

函數聲明:

struct tm * localtime(const time_t *);

struct tm結構體包含了時間的各要素,但還不是我們習慣的時間表達方式,我們可以用格式化輸出printf、sprintf或fprintf等函數,把struct tm結構體轉換為我們想要的結果。

示例(book128.c)

/*
 * 程序名:book128.c,此程序演示獲取操作系統時間
 * 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <time.h>

int main(int argc,char *argv[])
{
  time_t tnow;
  tnow=time(0);      // 獲取當前時間
  printf("tnow=%lu\n",tnow);   // 輸出整數表示的時間

  struct tm *sttm;  
  sttm=localtime(&tnow);  // 把整數的時間轉換為struct tm結構體的時間

  // yyyy-mm-dd hh24:mi:ss格式輸出,此格式用得最多
  printf("%04u-%02u-%02u %02u:%02u:%02u\n",sttm->tm_year+1900,sttm->tm_mon+1,\
          sttm->tm_mday,sttm->tm_hour,sttm->tm_min,sttm->tm_sec);

  printf("%04u年%02u月%02u日%02u時%02u分%02u秒\n",sttm->tm_year+1900,\
          sttm->tm_mon+1,sttm->tm_mday,sttm->tm_hour,sttm->tm_min,sttm->tm_sec);

  // 只輸出年月日
  printf("%04u-%02u-%02u\n",sttm->tm_year+1900,sttm->tm_mon+1,sttm->tm_mday); 
}

運行效果

在這里插入圖片描述

五、mktime庫函數

mktime函數的功能與localtime函數相反。

localtime函數用於把time_t表示的時間轉換為struct tm表示的時間。

mktime 函數用於把struct tm表示的時間轉換為time_t表示的時間。

time_t mktime(struct tm *tm);

函數返回time_t的值。

示例(book130.c)

/*
 * 程序名:book130.c,此程序演示時間操作的mktime庫函數。
 * 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <time.h>
#include <string.h>

int main(int argc,char *argv[])
{
  // 2019-12-25 15:05:03整數表示是1577257503
  struct tm sttm;  
  memset(&sttm,0,sizeof(sttm));

  sttm.tm_year=2019-1900; // 注意,要減1900
  sttm.tm_mon=12-1;        // 注意,要減1
  sttm.tm_mday=25;
  sttm.tm_hour=15;
  sttm.tm_min=5;
  sttm.tm_sec=3;
  sttm.tm_isdst = 0;
  printf("2019-12-25 15:05:03 is %lu\n",mktime(&sttm));
}

運行效果

在這里插入圖片描述

六、程序睡眠

在實際開發中,我們經常需要把程序掛起一段時間,可以使用sleep和usleep兩個庫函數,需要包含unistd.h頭文件中。函數的聲明如下:

unsigned int sleep(unsigned int seconds);
int usleep(useconds_t usec);

sleep函數的參數是秒,usleep函數的參數是微秒,1秒=1000000微秒。

sleep(1);           // 程序睡眠1秒。
sleep(10);          // 程序睡眠10秒。
usleep(100000);    // 程序睡眠十分之一秒。
usleep(1000000);   // 程序睡眠一秒。

程序員不關心sleep和usleep函數的返回值。

七、精確到微秒的計時器

1、精確到微秒的timeval結構體

timeval結構體在sys/time.h文件中定義,聲明為:

struct timeval
{
  long  tv_sec;  // 1970年1月1日到現在的秒。
  long  tv_usec; // 當前秒的微妙,即百萬分之一秒。
};

2、時區timezone 結構體

timezone 結構體在sys/time.h文件中定義,聲明為:

struct timezone
{
  int tz_minuteswest;  // 和UTC(格林威治時間)差了多少分鍾。
  int tz_dsttime;      // type of DST correction,修正參數據,忽略
};

3、gettimeofday庫函數

gettimeofday是獲得當前的秒和微秒的時間,其中的秒是指1970年1月1日到現在的秒,微秒是指當前秒已逝去的微秒數,可以用於程序的計時。調用gettimeofday函數需要包含sys/time.h頭文件。

函數聲明:

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

當前的時間存放在tv 結構體中,當地時區的信息則放到tz所指的結構體中,tz可以為空。

函數執行成功后返回0,失敗后返回-1。

在使用gettimeofday()函數時,第二個參數一般都為空,我們一般都只是為了獲得當前時間,不關心時區的信息。

示例(book132.c)

/*
 * 程序名:book132.c,此程序演示精確到微秒的計時器。 
 * 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <sys/time.h>   // 注意,不是time.h

int main()
{
  struct timeval begin,end;  // 定義用於存放開始和結束的時間

  gettimeofday(&begin,0);    // 計時器開始
  printf("begin time(0)=%d,tv_sec=%d,tv_usec=%d\n",time(0),begin.tv_sec,begin.tv_usec);

  sleep(2);
  usleep(100000);     // 程序睡眠十分之一秒。

  gettimeofday(&end,0);      // 計時器結束
  printf("end   time(0)=%d,tv_sec=%d,tv_usec=%d\n",time(0),end.tv_sec,end.tv_usec);

  printf("計時過去了%d微秒。\n",\
         (end.tv_sec-begin.tv_sec)*1000000+(end.tv_usec-begin.tv_usec));
}

運行效果

在這里插入圖片描述

各位,book132.c程序采用usleep睡眠十分之一秒,但是計時器顯示的實際時間大於十分之一秒,為何?原因很簡單,因為程序執行需要時間,雖然這個時間很短,在千分之一秒內,那也是需要時間。

還有一個要注意的問題,time.h 是ISO C99 標准日期時間頭文件。sys/time.h 是Linux系統的日期時間頭文件,也就是說,timeval、timezone結構體和gettimeofday函數在windows平台中不能使用,真是麻煩。

八、應用經驗

在實際開發中,除了當前的時間,還經常需要一個偏移量的時間,例如獲取十分鍾之后的時間,方法是采用time函數得到一個整數后,再加上10*60秒,再用localtime函數轉換為結構體。

九、課后作業

繼續豐富您的函數庫。

1)編寫一個通用函數,把整數的時間轉換為字符串格式的時間,格式如:"2019-02-08
12:05:08",如果轉換成功函數返回0,失敗返回-1,函數的聲明如下:

int timetostr(const time_t ti,char *strtime);

2)編寫一個通用函數,把字符串格式的時間轉換為整數的時間,函數的聲明如下:

int strtotime(const char *strtime,time_t *ti);

3)編寫一個通用函數,獲取操作系統的時間,函數的聲明如下:

void LocalTime(char *out_stime,const char *in_fmt,const int in_interval);

out_stime是輸出結果,格式由fmt決定。

in_interval是偏移常量,單位是秒。

in_fmt指定了out_stime的格式,取值如下:

  /* 常用的時間格式
  yyyy-mm-dd hh24:mi:ss
  yyyymmddhh24miss
  yyyy-mm-dd
  yyyymmdd
  hh24:mi:ss
  hh24miss
  hh24:mi
  hh24mi
  hh24
  mi
  */

調用示例:

 char strLocalTime[21];

  // 獲取當前的時間,以yyyy-mm-dd hh24:mi:ss格式返回
  memset(strLocalTime,0,sizeof(strLocalTime));
  LocalTime(strLocalTime,"yyyy-mm-dd hh24:mi:ss",0);

  // 獲取比現在晚10分鍾的時間,以yyyy-mm-dd hh24:mi:ss格式返回
  memset(strLocalTime,0,sizeof(strLocalTime));
  LocalTime(strLocalTime,"yyyy-mm-dd hh24:mi:ss",10*60);

  // 獲取當前的時間,以hh24:mi:ss格式返回
  memset(strLocalTime,0,sizeof(strLocalTime));
  LocalTime(strLocalTime,"hh24:mi:ss",0);

十、版權聲明

C語言技術網原創文章,轉載請說明文章的來源、作者和原文的鏈接。
來源:C語言技術網(www.freecplus.net)
作者:碼農有道

如果文章有錯別字,或者內容有錯誤,或其他的建議和意見,請您留言指正,非常感謝!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM