二、时间数据结构
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(¤t_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(¤t_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
};
哪个定义是对的
两个结构体定义实际上都是正确的,但它们来自不同的标准和环境:
-
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
。 -
GNU C Library(glibc)扩展的
struct tm
:
第二个定义是在 GNU C Library(glibc)中扩展的struct tm
,它除了 POSIX 定义的字段外,还添加了tm_gmtoff
和tm_zone
字段。tm_gmtoff
存储相对于 UTC 的偏移量(以秒为单位),而tm_zone
存储时区的缩写字符串。这些额外的字段提供了更详细的时区信息,但在非 glibc 环境中可能不可用。
哪个定义是对的?
- 如果你正在编写遵循 POSIX 标准的代码,那么第一个定义(不包含
tm_gmtoff
和tm_zone
)是适用的,它应该在所有 POSIX 兼容的系统上工作。 - 如果你使用的是 glibc(GNU C Library),那么第二个定义(包含
tm_gmtoff
和tm_zone
)是正确的。在 glibc 中,struct tm
包含了这些额外的字段。
注意事项
- 在编写跨平台代码时,建议只使用 POSIX 标准中定义的字段,以确保代码的兼容性。
- 如果你确定你的代码只运行在使用 glibc 的系统上(如大多数 Linux 发行版),那么可以利用
tm_gmtoff
和tm_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
的常见函数:
-
clock_gettime()
:- 用于获取指定时钟的当前时间。
- 示例调用:
clock_gettime(CLOCK_REALTIME, &ts);
其中ts
是一个struct timespec
变量。
-
clock_settime()
:- 用于设置指定时钟的时间。
- 示例调用:
clock_settime(CLOCK_REALTIME, &ts);
-
nanosleep()
:- 用于实现延迟或休眠,可以精确到纳秒。
- 示例调用:
nanosleep(&req, &rem);
其中req
是请求的struct timespec
时间,rem
是剩余未睡眠的时间。
-
timerfd_create()
,timerfd_settime()
,timerfd_gettime()
:- 这些函数用于创建和管理基于文件描述符的定时器,可以设置和查询定时器的到期时间。
- 示例调用:
timerfd_settime(fd, 0, &ts, NULL);
-
futimens()
,utimensat()
, 和utimes()
:- 用于设置文件或目录的访问和修改时间。
- 示例调用:
futimens(fd, ×);
其中times
是一个struct timespec
数组,包含访问时间和修改时间。
-
pthread_cond_timedwait()
:- 用于线程间带超时的条件变量等待。
- 示例调用:
pthread_cond_timedwait(&cond, &mutex, &ts);
-
sem_timedwait()
:- 用于信号量的带超时等待。
- 示例调用:
sem_timedwait(&sem, &ts);
-
futex()
:- 虽然
futex()
函数本身不一定直接使用struct timespec
,但在实现高级同步原语时,它经常与基于时间的阻塞配合使用,这时会间接使用到struct timespec
。
- 虽然
-
ppoll()
,pselect()
:- 这些函数提供了带超时的多路复用 I/O,可以使用
struct timespec
来指定超时时间。
- 这些函数提供了带超时的多路复用 I/O,可以使用
-
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 timespec
和 clock_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()
函数原本的用途:
-
测量执行时间:
clock()
函数最常用的场景是测量程序或代码段的执行时间。通过在代码段的开始和结束时调用clock()
函数,可以计算出该代码段运行所需的 CPU 时间。 -
性能分析:
开发者可以使用clock()
来分析程序的性能,比如确定程序的哪一部分需要优化以减少 CPU 使用时间。 -
基准测试:
clock()
可用于基准测试,比较不同算法或实现的性能。 -
时间延迟:
clock()
还可以用于实现简单的时间延迟,尽管这不是它设计的主要目的。 -
与
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;
}
解释
-
convertToTm
函数:这个辅助函数用于将std::chrono::system_clock::time_point
转换为struct tm
。std::chrono::system_clock::to_time_t
将时间点转换为time_t
类型,然后使用localtime_s
或localtime_r
(取决于平台)将其转换为本地时间的struct tm
。 -
strftime
函数:strftime
函数用于将struct tm
格式的时间数据格式化为字符串。它接受一个指向字符数组的指针、数组的大小、格式字符串和指向struct tm
的指针。 -
格式化字符串:
strftime
函数的第三个参数是格式字符串,它决定了输出字符串的格式。例如:%Y
代表四位数的年份。%m
代表月份。%d
代表一个月中的某一天。%H
代表小时(24小时制)。%M
代表分钟。%S
代表秒。%a
代表星期几的缩写。%b
代表月份的缩写。%Z
代表时区名称。
-
输出:示例代码中包含了多种格式化选项,包括全日期时间、仅日期、仅时间、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调整的影响 |
RTC | 低 | 慢 | Yes | Yes |
xtime | 高 | 快 | Yes | Yes |
monotonic | 高 | 快 | No | Yes |
raw monotonic | 高 | 快 | No | No |
boot time | 高 | 快 | Yes | Yes |
三、综合实例
#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);
}
- 零时区时,local和UTC相关函数结果均一致
- 非零时区,比如东8区时
- 非0时区,可以看出mktime要给本地时间,才能得出UTC时间戳。(若输入UTC时间的tm,算出来就是错误的)
- localtime 输入UTC秒,得出本地时间的tm
- mktime 和 timelocal 输入本地时间tm,得出UTC秒
- gmtime 输入UTC秒,得出UTC时间的tm
- timegm 输入UTC格式的tm,得出UTC秒
- 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