linux时间相关结构体和函数整理

二、时间数据结构

Linux下常用的时间类型有6个:time_t,struct timeb, struct timeval,struct timespec,clock_t, struct tm.

  • 标准C++:time_t,struct tm 和 std::chrono 库中的时间类型。
  • Linux特有:struct timeval,struct timespec 和 clock_t。

2.1 time_t 表示1970年以来的秒数(时区无关)

  • 用来存储自1970年1月1日(UTC)起经过的秒数(和时区无关)
  • time_t 是在 <time.h>(C语言)和 <ctime>(C++语言)头文件中定义的

定义和用途

  • time_t 是一个能够存储自1970年1月1日(UTC)以来经过的秒数的整数类型。这个时间点也被称为“纪元时间”或“UNIX纪元时间”。
  • 它主要用于存储和计算时间间隔,以及将时间戳与人类可读的日期和时间格式相互转换。

头文件

  • 在 C 语言中,time_t 通常在 <time.h> 头文件中定义。
  • 在 C++ 语言中,time_t 通常在 头文件中定义,并且相关的函数被包含在 std 命名空间中。

相关函数

在 C 和 C++ 标准库中,与 time_t 相关的函数包括但不限于:

  • time(): 获取当前时间的 time_t 值。
  • localtime(): 将 time_t 值转换为本地时间的 struct tm。
  • gmtime(): 将 time_t 值转换为协调世界时(UTC)的 struct tm。
  • mktime(): 将 struct tm 转换为本地时间的 time_t。
  • strftime(): 将 struct tm 格式化为字符串。

示例

#include <stdio.h>
#include <time.h>
 
int main ()
{
   time_t curtime1;
   time_t curtime2;
 
   time(&curtime1);			//方式一
   curtime2 = time(NULL);	//方式二
   printf("1970年到当前的秒数 curtime1 = %ld curtime1 = %ld\n", curtime1, curtime2);
   return(0);
}
#include <iostream>
#include <ctime>

int main() {
    // 获取当前时间的time_t值
    time_t current_time = time(NULL);

    // 将time_t转换为本地时间的struct tm
    struct tm *local_tm = localtime(&current_time);

    // 输出本地时间
    std::cout << "Local time: "
              << (local_tm->tm_year + 1900) << "-"  // tm_year是从1900年开始的
              << (local_tm->tm_mon + 1) << "-"  // tm_mon是从0开始的
              << local_tm->tm_mday << " "
              << local_tm->tm_hour << ":"
              << local_tm->tm_min << ":"
              << local_tm->tm_sec << std::endl;

    // 将time_t转换为UTC时间的struct tm
    struct tm *utc_tm = gmtime(&current_time);

    // 输出UTC时间
    std::cout << "UTC time: "
              << (utc_tm->tm_year + 1900) << "-"
              << (utc_tm->tm_mon + 1) << "-"
              << utc_tm->tm_mday << " "
              << utc_tm->tm_hour << ":"
              << utc_tm->tm_min << ":"
              << utc_tm->tm_sec << std::endl;

    return 0;
}

2.2 struct tm 表示分解的本地或 UTC 时间的结构体

  • struct tm 是 C 和 C++ 标准库中用于表示分解的本地或 UTC 时间的结构体。它将时间分成各个部分,如年、月、日、小时、分钟和秒,使得时间和日期更容易读取和处理。
  • struct tm 在 C 语言中定义于 <time.h> 头文件中,在 C++ 语言中则在 <ctime> 头文件中定义。
  • 注意:使能到秒(秒之下精度丢失)
struct tm {
   int tm_sec;         /* 秒,范围从 0 到 60                */
   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                    */
   int tm_isdst;       /* 夏令时                        */    
};

相关函数

  • man gmtime_r
  • localtime(): 将 time_t 转换为本地时间的 struct tm。
  • gmtime(): 将 time_t 转换为 UTC 时间的 struct tm。
  • mktime(): 将本地时间的 struct tm 转换为 time_t。
  • timelocal(): 将本地时间的 struct tm 转换为 time_t。
  • timegm():将 UTC时间的 struct tm 转换为 time_t。
 #include <time.h>

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

实例—ctime、gmtime、localtime、mktime、asctime

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>

int main()  
{
	time_t curtime = time(NULL);  
	struct tm *pTm; 
	pTm = gmtime(&curtime);
    printf("gmtime :%04d/%02d/%02d %02d:%02d:%02d \n",\
        pTm->tm_year + 1900, pTm->tm_mon + 1, pTm->tm_mday, pTm->tm_hour, pTm->tm_min, pTm->tm_sec);
	pTm = localtime(&curtime);
    printf("localtime :%04d/%02d/%02d %02d:%02d:%02d \n",\
        pTm->tm_year + 1900, pTm->tm_mon + 1, pTm->tm_mday, pTm->tm_hour, pTm->tm_min, pTm->tm_sec);
	
	//修改tm时间后再转回time_t
	pTm->tm_year -= 1;
	pTm->tm_mon -= 1;
	
	time_t curtime1 = mktime(pTm);
	printf("curtime:%ld curtime1:%ld\n",curtime,curtime1);  
	
	printf("ctime curtime:%s\n",ctime(&curtime));  
	printf("ctime curtime1:%s\n",ctime(&curtime1)); 
	printf("asctime curtime:%s \n",asctime(pTm));  
}

输出

gmtime :2020/05/08 06:47:06
localtime :2020/05/08 14:47:06
curtime:1588920426 curtime1:1554706026

实例—gmtime_r、localtime_r、mktime

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>

int main()  
{
	char buf[1024];
    struct tm st;
    struct timeval tv;
    struct timezone tz;
    gettimeofday(&tv, &tz);			//gettimeofday(&tv, NULL);
    localtime_r(&tv.tv_sec, &st);	//st = localtime_r(&tv.tv_sec, NULL);
    memset(buf,0,sizeof(buf));
    snprintf(buf, sizeof(buf), "%02d-%02d %02d:%02d:%02d:%03ld", st.tm_mon+1, st.tm_mday, st.tm_hour, st.tm_min, st.tm_sec, tv.tv_usec / 1000);
    printf("localtime_r %s\n",buf); 
	
    gmtime_r(&tv.tv_sec, &st);		//st = gmtime_r(&tv.tv_sec, NULL);
    memset(buf,0,sizeof(buf));
    snprintf(buf, sizeof(buf), "%02d-%02d %02d:%02d:%02d:%03ld", st.tm_mon+1, st.tm_mday, st.tm_hour, st.tm_min, st.tm_sec, tv.tv_usec / 1000);
    printf("gmtime_r %s\n",buf);  
    return 0;  
}

输出

localtime_r 05-08 15:05:25:079
gmtime_r 05-08 07:05:25:079

问题

struct tm {
   int tm_sec;         /* 秒,范围从 0 到 60                */
   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                    */
   int tm_isdst;       /* 夏令时                        */    
};

struct tm
{
  int tm_sec;			/* Seconds.	[0-60] (1 leap second) */
  int tm_min;			/* Minutes.	[0-59] */
  int tm_hour;			/* Hours.	[0-23] */
  int tm_mday;			/* Day.		[1-31] */
  int tm_mon;			/* Month.	[0-11] */
  int tm_year;			/* Year	- 1900.  */
  int tm_wday;			/* Day of week.	[0-6] */
  int tm_yday;			/* Days in year.[0-365]	*/
  int tm_isdst;			/* DST.		[-1/0/1]*/

# ifdef	__USE_MISC
  long int tm_gmtoff;		/* Seconds east of UTC.  */
  const char *tm_zone;		/* Timezone abbreviation.  */
# else
  long int __tm_gmtoff;		/* Seconds east of UTC.  */
  const char *__tm_zone;	/* Timezone abbreviation.  */
# endif
};
哪个定义是对的

两个结构体定义实际上都是正确的,但它们来自不同的标准和环境:

  1. POSIX(Portable Operating System Interface)标准下的 struct tm
    这是第一个定义,它在 POSIX 标准下定义了 struct tm,这个标准被广泛用于类 Unix 系统,包括 Linux 和 macOS。POSIX 标准定义了 struct tm 以包含基本的时间字段,如 tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, 和 tm_isdst

  2. GNU C Library(glibc)扩展的 struct tm
    第二个定义是在 GNU C Library(glibc)中扩展的 struct tm,它除了 POSIX 定义的字段外,还添加了 tm_gmtofftm_zone 字段。tm_gmtoff 存储相对于 UTC 的偏移量(以秒为单位),而 tm_zone 存储时区的缩写字符串。这些额外的字段提供了更详细的时区信息,但在非 glibc 环境中可能不可用。

哪个定义是对的?

  • 如果你正在编写遵循 POSIX 标准的代码,那么第一个定义(不包含 tm_gmtofftm_zone)是适用的,它应该在所有 POSIX 兼容的系统上工作。
  • 如果你使用的是 glibc(GNU C Library),那么第二个定义(包含 tm_gmtofftm_zone)是正确的。在 glibc 中,struct tm 包含了这些额外的字段。

注意事项

  • 在编写跨平台代码时,建议只使用 POSIX 标准中定义的字段,以确保代码的兼容性。
  • 如果你确定你的代码只运行在使用 glibc 的系统上(如大多数 Linux 发行版),那么可以利用 tm_gmtofftm_zone 字段来获取更详细的时区信息。
  • 在 C++ 中,标准库 <ctime> 包含了与 C 标准库 <time.h> 中相同的 struct tm 定义,因此上述区别同样适用于 C++ 程序。

总之,选择哪个定义取决于你的目标平台和你使用的库。在大多数情况下,POSIX 定义的 struct tm 应该足够满足需求。

2.3 struct timeval有两个成员,一个是秒,一个是微妙

The tv argument is a struct timeval (as specified in <sys/time.h>):

struct timeval {
     time_t      tv_sec;     /* seconds */
     suseconds_t tv_usec;    /* microseconds 微妙 */
 };

and gives the number of seconds and microseconds since the Epoch (see time(2)).

The tz argument is a struct timezone:

struct timezone {
    int tz_minuteswest;     /* minutes west of Greenwich */
    int tz_dsttime;         /* type of DST correction */
};

相关函数

  • gettimeofday / settimeofday

    #include <sys/time.h>
    
    int gettimeofday(struct timeval *tv, struct timezone *tz);
    
    int settimeofday(const struct timeval *tv, const struct timezone *tz);
    
  • select

    int select(int nfds, fd_set *readfds, fd_set *writefds,
                      fd_set *exceptfds, struct timeval *timeout);
    

实例

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>

int main()  
{  
	char buf[1024];
    struct tm st;
    struct timeval    tv;
    struct timezone tz;
    gettimeofday(&tv, &tz);
    localtime_r(&tv.tv_sec, &st);
    memset(buf,0,sizeof(buf));
    snprintf(buf, sizeof(buf), "%02d-%02d %02d:%02d:%02d:%03ld", st.tm_mon+1, st.tm_mday, st.tm_hour, st.tm_min, st.tm_sec, tv.tv_usec / 1000);
    printf("localtime_r %s\n",buf);  
    return 0;  
}

2.4 struct timespec有两个成员,一个是秒,一个是纳秒

struct timespec 是一个在 <time.h> 头文件中定义的数据结构,用于表示时间点或时间间隔,它在需要高精度时间控制的场景中特别有用,因为它支持纳秒级别的精度。struct timespec 主要由两个成员组成:tv_sec 和 tv_nsec,分别代表秒和纳秒。

struct timespec {
   	time_t   tv_sec;        /* seconds */
	long     tv_nsec;       /* nanoseconds */
};

相关函数
struct timespec 是在 <time.h> 头文件中定义的一个数据结构,用于表示一个时间点或时间间隔,它在许多现代系统调用和函数中被用来提高时间精度至纳秒级。以下是一些使用 struct timespec 的常见函数:

  1. clock_gettime()

    • 用于获取指定时钟的当前时间。
    • 示例调用:clock_gettime(CLOCK_REALTIME, &ts); 其中 ts 是一个 struct timespec 变量。
  2. clock_settime()

    • 用于设置指定时钟的时间。
    • 示例调用:clock_settime(CLOCK_REALTIME, &ts);
  3. nanosleep()

    • 用于实现延迟或休眠,可以精确到纳秒。
    • 示例调用:nanosleep(&req, &rem); 其中 req 是请求的 struct timespec 时间,rem 是剩余未睡眠的时间。
  4. timerfd_create(), timerfd_settime(), timerfd_gettime()

    • 这些函数用于创建和管理基于文件描述符的定时器,可以设置和查询定时器的到期时间。
    • 示例调用:timerfd_settime(fd, 0, &ts, NULL);
  5. futimens(), utimensat(), 和 utimes()

    • 用于设置文件或目录的访问和修改时间。
    • 示例调用:futimens(fd, &times); 其中 times 是一个 struct timespec 数组,包含访问时间和修改时间。
  6. pthread_cond_timedwait()

    • 用于线程间带超时的条件变量等待。
    • 示例调用:pthread_cond_timedwait(&cond, &mutex, &ts);
  7. sem_timedwait()

    • 用于信号量的带超时等待。
    • 示例调用:sem_timedwait(&sem, &ts);
  8. futex()

    • 虽然 futex() 函数本身不一定直接使用 struct timespec,但在实现高级同步原语时,它经常与基于时间的阻塞配合使用,这时会间接使用到 struct timespec
  9. ppoll(), pselect()

    • 这些函数提供了带超时的多路复用 I/O,可以使用 struct timespec 来指定超时时间。
  10. clock_nanosleep()

    • nanosleep() 类似,但提供了更多的灵活性,如相对和绝对时间模式。

通过使用 struct timespec,这些函数可以提供比传统的 struct timeval 更高的时间精度,因为 tv_nsec 字段可以表示 1 纳秒(十亿分之一秒)的时间单位。在需要高精度时间控制的应用场景中,struct timespec 成为了首选的时间表示方式。

示例

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>

int main()  
{
	int num = 0;
	for(int i=0; i< 10000; i++)
	{
		num += i;
	}	
	struct timespec tpNew;
	clock_gettime(CLOCK_MONOTONIC, &tpNew);
	printf ("%s %ld\n", ctime(&tpNew.tv_sec), tpNew.tv_nsec);
	
	clock_gettime(CLOCK_REALTIME, &tpNew);
	printf ("%s %ld\n", ctime(&tpNew.tv_sec), tpNew.tv_nsec);
	
	clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tpNew);
	printf ("%s %ld\n", ctime(&tpNew.tv_sec), tpNew.tv_nsec);
    return 0;  
}      

2.5 struct timeb结构: 主要有两个成员, 一个是秒, 另一个是毫秒, 精确度为毫秒

ftime() 函数和 struct timeb 主要用于获取当前时间,并提供了一种比 time() 更详细的时间信息,包括秒和毫秒。ftime() 是在早期的 Unix 和 POSIX 系统中用于获取当前时间的一种方式,它在 <sys/timeb.h> 头文件中定义。

struct timeb {
    time_t time;    /* 秒数,从 Unix 纪元开始 */
    unsigned short millitm; /* 毫秒数 */
    short timezone; /* 东偏西时间区域 */
    short dstflag;  /* 夏令时标志 */
};

int ftime(struct timeb *tp);

原始用途

ftime()struct timeb 最初设计用于需要毫秒级时间精度的场景,如网络编程、计时和调试。然而,随着 struct timespecclock_gettime() 的出现,ftime()struct timeb 的使用逐渐减少,因为 struct timespec 提供了更细粒度的时间精度(纳秒级),并且 clock_gettime() 提供了更多时钟的选择。

注意

  • ftime()struct timeb 在现代系统中已经不常使用,特别是在 C++11 引入 <chrono> 头文件后,提供了更为现代和灵活的时间处理方式。
  • ftime()struct timeb 在某些现代系统中可能已被弃用或不推荐使用。

尽管如此,在一些遗留系统或旧代码库中,你仍然可能会遇到 ftime()struct timeb 的使用。在新的项目中,推荐使用 <chrono> 或者 <time.h> 中的 clock_gettime()struct timespec 来获取时间信息。

2.6 clock_t类型, 由clock_t clock(); 返回获取

clock_t 是 C 标准库中的一个类型,用来表示时间,它在 <time.h> 头文件中定义。clock() 函数是 C 标准库中的一个函数,使用 clock_t 类型来返回程序的 CPU 时间,即程序执行过程中所占用的 CPU 时钟周期数。以下是 clock() 函数的原型:

clock_t clock(void);

clock() 函数原本的用途:

  1. 测量执行时间
    clock() 函数最常用的场景是测量程序或代码段的执行时间。通过在代码段的开始和结束时调用 clock() 函数,可以计算出该代码段运行所需的 CPU 时间。

  2. 性能分析
    开发者可以使用 clock() 来分析程序的性能,比如确定程序的哪一部分需要优化以减少 CPU 使用时间。

  3. 基准测试
    clock() 可用于基准测试,比较不同算法或实现的性能。

  4. 时间延迟
    clock() 还可以用于实现简单的时间延迟,尽管这不是它设计的主要目的。

  5. CLOCKS_PER_SEC 结合使用
    clock() 返回的值与宏 CLOCKS_PER_SEC 相关,CLOCKS_PER_SEC 定义了 clock_t 每秒的时钟数。通过这个宏,可以将 clock_t 值转换为秒或其他时间单位。

示例用法:

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>

int main()  
{
	int num = 0;
	clock_t start, stop;
	start = clock();				// 记录开始时间
	for(int i=0; i< 10000; i++)
	{
		num += i;
	}
	stop = clock();					// 记录结束时间
	printf ("num %d clock ticks per second (start=%ld,stop=%ld)\n",
	   num, start, stop);
    double cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC; // 计算CPU时间
    printf("CPU time used: %.2f seconds\n", cpu_time_used);
   
    return 0;  
}

请注意,clock() 函数测量的是 CPU 时间,而不是实际的墙钟时间(wall-clock time)。这意味着它只计算程序在 CPU 上执行的时间,不包括 I/O 等待或线程阻塞的时间。如果需要测量实际经过的时间,应使用如 gettimeofday()clock_gettime() 等其他函数。

三、时间相关函数

3.4 clock_gettime / clock_settime

3.5 strftime

strftime 函数用于将时间数据格式化为可读的字符串。下面是一个综合性示例,展示如何使用 strftime 函数来生成不同格式的日期时间字符串。在这个示例中,我们将使用 std::chrono 库来获取当前系统时间,并将其转换为 struct tm 格式,最后使用 strftime 函数进行格式化。

#include <iostream>
#include <iomanip>
#include <ctime>
#include <string>
#include <chrono>

// 将 std::chrono::system_clock::time_point 转换为 struct tm
struct tm convertToTm(const std::chrono::system_clock::time_point& tp) {
    auto in_time_t = std::chrono::system_clock::to_time_t(tp);
    struct tm tm;
#if defined(_WIN32)
    localtime_s(&tm, &in_time_t);
#else
    localtime_r(&in_time_t, &tm);
#endif
    return tm;
}

int main() {
    // 获取当前系统时间
    auto now = std::chrono::system_clock::now();
    struct tm local_tm = convertToTm(now);

    // 创建一个 char 数组用于存放格式化后的日期时间字符串
    char buffer[100];

    // 不同的格式化选项
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &local_tm);
    std::cout << "Full format: " << buffer << std::endl;

    strftime(buffer, sizeof(buffer), "%Y/%m/%d", &local_tm);
    std::cout << "Date only (slash): " << buffer << std::endl;

    strftime(buffer, sizeof(buffer), "%Y%m%d", &local_tm);
    std::cout << "Date only (compact): " << buffer << std::endl;

    strftime(buffer, sizeof(buffer), "%H:%M:%S", &local_tm);
    std::cout << "Time only: " << buffer << std::endl;

    strftime(buffer, sizeof(buffer), "%a, %b %d %Y %H:%M:%S %Z", &local_tm);
    std::cout << "RFC 2822 format: " << buffer << std::endl;

    strftime(buffer, sizeof(buffer), "%FT%T", &local_tm);
    std::cout << "ISO 8601 format: " << buffer << std::endl;

    strftime(buffer, sizeof(buffer), "%x", &local_tm);
    std::cout << "Locale's date representation: " << buffer << std::endl;

    strftime(buffer, sizeof(buffer), "%X", &local_tm);
    std::cout << "Locale's time representation: " << buffer << std::endl;

    return 0;
}

解释

  1. convertToTm 函数:这个辅助函数用于将 std::chrono::system_clock::time_point 转换为 struct tmstd::chrono::system_clock::to_time_t 将时间点转换为 time_t 类型,然后使用 localtime_slocaltime_r(取决于平台)将其转换为本地时间的 struct tm

  2. strftime 函数strftime 函数用于将 struct tm 格式的时间数据格式化为字符串。它接受一个指向字符数组的指针、数组的大小、格式字符串和指向 struct tm 的指针。

  3. 格式化字符串strftime 函数的第三个参数是格式字符串,它决定了输出字符串的格式。例如:

    • %Y 代表四位数的年份。
    • %m 代表月份。
    • %d 代表一个月中的某一天。
    • %H 代表小时(24小时制)。
    • %M 代表分钟。
    • %S 代表秒。
    • %a 代表星期几的缩写。
    • %b 代表月份的缩写。
    • %Z 代表时区名称。
  4. 输出:示例代码中包含了多种格式化选项,包括全日期时间、仅日期、仅时间、RFC 2822 格式、ISO 8601 格式以及本地化的时间和日期表示。

这个综合示例展示了如何使用 strftime 函数生成各种格式的日期时间字符串,可以根据具体需求选择合适的格式化选项。

时间的种类

内核管理着多种时间,它们分别是:

  • RTC时间
  • wall time:墙上时间
  • monotonic time
  • raw monotonic time
  • boot time:总启动时间

RTC时间 在PC中,RTC时间又叫CMOS时间,它通常由一个专门的计时硬件来实现,软件可以读取该硬件来获得年月日、时分秒等时间信息,而在嵌入式系统中,有使用专门的RTC芯片,也有直接把RTC集成到Soc芯片中,读取Soc中的某个寄存器即可获取当前时间信息。一般来说,RTC是一种可持续计时的,也就是说,不管系统是否上电,RTC中的时间信息都不会丢失,计时会一直持续进行,硬件上通常使用一个后备电池对RTC硬件进行单独的供电。因为RTC硬件的多样性,开发者需要为每种RTC时钟硬件提供相应的驱动程序,内核和用户空间通过驱动程序访问RTC硬件来获取或设置时间信息。

xtime xtime和RTC时间一样,都是人们日常所使用的墙上时间,只是RTC时间的精度通常比较低,大多数情况下只能达到毫秒级别的精度,如果是使用外部的RTC芯片,访问速度也比较慢,为此,内核维护了另外一个wall time时间:xtime,取决于用于对xtime计时的clocksource,它的精度甚至可以达到纳秒级别,因为xtime实际上是一个内存中的变量,它的访问速度非常快,内核大部分时间都是使用xtime来获得当前时间信息。xtime记录的是自1970年1月1日24时到当前时刻所经历的纳秒数。

monotonic time 该时间自系统开机后就一直单调地增加,它不像xtime可以因用户的调整时间而产生跳变,不过该时间不计算系统休眠的时间,也就是说,系统休眠时,monotoic时间不会递增。

raw monotonic time 该时间与monotonic时间类似,也是单调递增的时间,唯一的不同是:raw monotonic time“更纯净”,他不会受到NTP时间调整的影响,它代表着系统独立时钟硬件对时间的统计。

boot time 与monotonic时间相同,不过会累加上系统休眠的时间,它代表着系统上电后的总时间。

时间种类精度(统计单位)访问速度累计休眠时间受NTP调整的影响
RTCYesYes
xtimeYesYes
monotonicNoYes
raw monotonicNoNo
boot timeYesYes

三、综合实例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>   // for gettimeofday settimeofday
#include <iostream>

using namespace std; 
 

//获取本年的分钟数:零时区没问题,非零时区有问题
uint64_t getMinOfYear()
{
    struct tm tp;
    uint64_t minuteOfYear = 0;

    time_t currUtcTimeStamp = time(NULL);       // 和时区无关
    localtime_r(&currUtcTimeStamp, &tp);

    tp.tm_mon = 0;
    tp.tm_mday = 1;
    tp.tm_yday = 0;
    tp.tm_wday = 0;
    tp.tm_hour = 0;
    tp.tm_min = 0;
    tp.tm_sec = 0;

    minuteOfYear = (abs((long)currUtcTimeStamp - (long)mktime(&tp))) / 60;  
    // 东8区问题:
    // currUtcTimeStamp 是 UTC
    // mktime 要按本地时间给参数,这里UTC 0点、北京时间tm_hour要给8,才能计算出UTC秒数---->所以这类差了8小时

    cout << "currS = " << currUtcTimeStamp << endl;
    cout << "yearStartS = " << (uint64_t)mktime(&tp) << endl;
    cout << "minOfYear = " << minuteOfYear << endl;

    return minuteOfYear;
}

int main ()
{
    time_t curtime1;
    time_t curtime2;

    // test time()  和时区无关
    time(&curtime1);			//方式一
    curtime2 = time(NULL);	    //方式二
    printf("1970年到当前的秒数 curtime1 = %ld curtime1 = %ld\n", curtime1, curtime2);

    // test gettimeofday 时区?
    struct timeval tv;
    struct timezone tz;
    gettimeofday(&tv, &tz);			//gettimeofday(&tv, NULL);
    printf("test gettimeofday: tv.tv_sec = %ld tv.tv_usec = %ld, tz.tz_minuteswest = %d, tz.tz_dsttime = %d \n", tv.tv_sec, tv.tv_usec, tz.tz_minuteswest, tz.tz_dsttime);

    
    // test localtime_r 时区有关 and gmtime_r 时区无关
    char buf[1024];
    struct tm st;
    localtime_r(&tv.tv_sec, &st);	//struct tm * = localtime_r(&tv.tv_sec, NULL);
    memset(buf,0,sizeof(buf));
    snprintf(buf, sizeof(buf), "%02d-%02d %02d:%02d:%02d:%03ld", st.tm_mon+1, st.tm_mday, st.tm_hour, st.tm_min, st.tm_sec, tv.tv_usec / 1000);
    printf("test localtime_r:%s, mktime:%ld\n",buf, mktime(&st)); 
	
    // test timelocal and timegm
    printf("test timelocal %ld\n",timelocal(&st)); 
    printf("test timegm %ld\n",timegm(&st));


    gmtime_r(&tv.tv_sec, &st);		//st = gmtime_r(&tv.tv_sec, NULL);
    memset(buf,0,sizeof(buf));
    snprintf(buf, sizeof(buf), "%02d-%02d %02d:%02d:%02d:%03ld", st.tm_mon+1, st.tm_mday, st.tm_hour, st.tm_min, st.tm_sec, tv.tv_usec / 1000);
    printf("test gmtime_r:%s, mktime:%ld\n",buf, mktime(&st)); 

    // test timelocal and timegm
    printf("test timelocal %ld \n",timelocal(&st)); 
    printf("test timegm %ld\n",timegm(&st)); 

//    (void)getMinOfYear();
   
   return(0);
}

在这里插入图片描述

  1. 零时区时,local和UTC相关函数结果均一致
  2. 非零时区,比如东8区时
    1. 非0时区,可以看出mktime要给本地时间,才能得出UTC时间戳。(若输入UTC时间的tm,算出来就是错误的)
    2. localtime 输入UTC秒,得出本地时间的tm
    3. mktime 和 timelocal 输入本地时间tm,得出UTC秒
    4. gmtime 输入UTC秒,得出UTC时间的tm
    5. timegm 输入UTC格式的tm,得出UTC秒
    6. UTC和local不要混用,比如localtime参数按UTC时间赋值、又给到mktime

(4) struct timespec有两个成员,一个是秒,一个是纳秒, 所以最高精确度是纳秒.

struct timespec
{
	time_t tv_sec; /* seconds */
	long tv_nsec; /* nanoseconds */
};

一般由函数long clock_gettime (clockid_t which_clock, struct timespec *tp); 获取.

获取特定时钟的时间,时间通过tp结构传回,目前定义了6种时钟,分别是

CLOCK_REALTIME                统当前时间,从1970年1.1日算起
CLOCK_MONOTONIC               系统的启动时间,不能被设置
CLOCK_PROCESS_CPUTIME_ID      进程运行时间
CLOCK_THREAD_CPUTIME_ID       线程运行时间
CLOCK_REALTIME_HR             CLOCK_REALTIME的高精度版本
CLOCK_MONOTONIC_HR            CLOCK_MONOTONIC的高精度版本

实例---------没太懂怎样应用

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>

int main()  
{
	int num = 0;
	for(int i=0; i< 10000; i++)
	{
		num += i;
	}	
	struct timespec tpNew;
	clock_gettime(CLOCK_MONOTONIC, &tpNew);
	printf ("%s %ld\n", ctime(&tpNew.tv_sec), tpNew.tv_nsec);
	
	clock_gettime(CLOCK_REALTIME, &tpNew);
	printf ("%s %ld\n", ctime(&tpNew.tv_sec), tpNew.tv_nsec);
	
	clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tpNew);
	printf ("%s %ld\n", ctime(&tpNew.tv_sec), tpNew.tv_nsec);
    return 0;  
}      

获取特定时钟的时间精度:

long clock_getres(clockid_t );

设置特定时钟的时间:

 long clock_settime(clockid_t ,struct timespec*);

休眠time中指定的时间,如果遇到信号中断而提前返回,则由left_time返回剩余的时间:

long clock_nanosleep(clockid_t ,int flag,timespec* time,timespec* left_time);

(7) 综合实例

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>

  
int main()  
{
    time_t curtime;         // 系统秒数
    struct tm stru_curtime; // 年月日时分秒结构体
    char buf[1024];

    curtime = time(NULL);
    printf("curtime = time(NULL) = %ld\n",curtime);  

    //utc时间戳转tm结构体
    (void)gmtime_r(&curtime, &stru_curtime);
    memset(buf,0,sizeof(buf));
    snprintf(buf, sizeof(buf), "[%02d-%02d %02d:%02d:%02d]", (stru_curtime.tm_mon + 1),
            stru_curtime.tm_mday, stru_curtime.tm_hour, stru_curtime.tm_min, stru_curtime.tm_sec);
    printf("gmtime_r %s\n",buf);  

    //修改tm时间后再转到utc时间戳
    stru_curtime.tm_sec++;
    curtime = mktime(&stru_curtime);
    printf("curtime = time(NULL) = %ld\n",curtime);  

    struct tm st;
    struct timeval    tv;
    struct timezone tz;
    gettimeofday(&tv, &tz);
    localtime_r(&tv.tv_sec, &st);
    memset(buf,0,sizeof(buf));
    snprintf(buf, sizeof(buf), "%02d-%02d %02d:%02d:%02d:%03ld", st.tm_mon+1, st.tm_mday, st.tm_hour, st.tm_min, st.tm_sec, tv.tv_usec / 1000);
    printf("localtime_r %s\n",buf);  
    return 0;  
}

延迟函数

主要的延迟函数有:sleep(),usleep(),nanosleep(),select(),pselect().

unsigned int sleep(unsigned int seconds);
void usleep(unsigned long usec);
int nanosleep(const struct timespec *req, struct timespec *rem);
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout);
int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);

alarm函数是信号方式的延迟,这种方式不直观,这里不说了。

仅通过函数原型中时间参数类型,可以猜测sleep可以精确到秒级。

usleep/select可以精确到微妙级,nanosleep和pselect可以精确到纳秒级。

而实际实现中,linux上的nanosleep和alarm相同,都是基于内核时钟机制实现,受linux内核时钟实现的影响,并不能达到纳秒级的精度,man nanosleep也可以看到这个说明,man里给出的精度是:Linux/i386上是10 ms ,Linux/Alpha上是1ms。

参考资料

http://www.cnblogs.com/my_life/articles/5340108.html
http://blog.chinaunix.net/uid-14617649-id-3058661.html
http://blog.csdn.net/droidphone/article/details/7989566

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值