1. 获取系统时间
#define _TIMEBUFLEN (40) // 获取日志缓冲需要的大小
void GetSysTime(char* pTimeBuf, uint32_t dwSize)
{
if (pTimeBuf == NULL)
{
return;
}
#if defined(_MSC_VER)
SYSTEMTIME sys = { 0 };
GetLocalTime(&sys);
sprintf_s(pTimeBuf, dwSize, "%4d-%02d-%02d %02d:%02d:%02d", sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond);
#else
time_t timer;
struct tm* ptTm = NULL;
time(&timer);
ptTm = localtime(&timer);
if (ptTm)
{
snprintf(pTimeBuf, dwSize, "%4d-%02d-%02d %02d:%02d:%02d", ptTm->tm_year + 1900, ptTm->tm_mon + 1, ptTm->tm_mday,
ptTm->tm_hour, ptTm->tm_min, ptTm->tm_sec);
}
#endif
}
这段代码是一个用于获取系统时间的函数。它接受一个字符指针 pTimeBuf
和一个无符号整数 dwSize
作为参数。函数首先检查 pTimeBuf
是否为空,如果为空则直接返回。
接下来,根据不同的编译器环境,使用不同的方法来获取系统时间。在 Microsoft Visual C++ 环境下,使用 GetLocalTime
函数获取当前系统时间,并将其存储在 SYSTEMTIME
结构体中。然后使用 sprintf_s
函数将时间信息格式化为字符串,并存储在 pTimeBuf
指向的缓冲区中。
在其他编译器环境下,使用 time
函数获取当前时间戳,并使用 localtime
函数将其转换为本地时间。然后使用 snprintf
函数将时间信息格式化为字符串,并存储在 pTimeBuf
指向的缓冲区中。
最终,函数将格式化后的时间字符串存储在 pTimeBuf
指向的缓冲区中,长度不超过 dwSize
。
获取毫秒级的系统时间
以下是使用C语言中的clock_gettime
函数和CLOCK_REALTIME
参数来输出当前时间的年月日时分秒的示例代码:
#include <stdio.h>
#include <time.h>
int main() {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
time_t t = ts.tv_sec;
struct tm *tm = localtime(&t);
printf("当前时间: %04d-%02d-%02d %02d:%02d:%02d.%03ld\n", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, ts.tv_nsec/1000000);
return 0;
}
输出:
当前时间:2024-04-25 15:17:45.867
这段代码首先包含了<stdio.h>
和<time.h>
头文件,然后在main
函数中使用clock_gettime
函数获取当前时间,并将其存储在timespec
结构体中。CLOCK_REALTIME
参数表示获取自1970年1月1日以来的实时时间。通过将timespec
结构体的秒数部分转换为time_t
类型,然后使用localtime
函数将其转换为struct tm
结构体,最后使用printf
函数输出结果。同时将timespec
结构体的纳秒数部分转换成毫秒并打印出来。 注意,年份需要加上1900,月份需要加1,因为struct tm
结构体中年份是从1900年开始计算的,月份是从0开始计算的。
相关知识
GetLocalTime
是一个Windows API函数,用于获取当前系统的本地时间。
它的原型如下:
VOID WINAPI GetLocalTime(
LPSYSTEMTIME lpSystemTime
);
参数:
- lpSystemTime:指向一个SYSTEMTIME结构体的指针,该结构体将被填充为当前系统的本地时间。
返回值:无
示例代码:
#include <windows.h>
#include <stdio.h>
int main()
{
SYSTEMTIME st;
GetLocalTime(&st);
printf("当前本地时间: %04d-%02d-%02d %02d:%02d:%02d
", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
return 0;
}
SYSTEMTIME
是Windows操作系统中用于表示系统时间的一种数据结构。
SYSTEMTIME
的定义可以在 Windows 的头文件 winbase.h 中找到,它是一个结构体,具体定义如下:
typedef struct _SYSTEMTIME {
WORD wYear; // 年份
WORD wMonth; // 月份
WORD wDayOfWeek; // 一周中的第几天(从周一开始)
WORD wDay; // 日
WORD wHour; // 小时
WORD wMinute; // 分钟
WORD wSecond; // 秒
WORD wMilliseconds // 毫秒
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
这个结构体包含年、月、日、时、分、秒、毫秒等成员,每个成员都是 WORD 类型,即无符号短整型。通过这个结构体,可以很方便地在程序中操作和传递系统时间信息。
time
函数
在C语言中,time
函数用于获取当前的时间(自1970年1月1日00:00:00 UTC起的秒数),其函数原型如下:
time_t time(time_t *timer);
这里的time_t
是一个整数类型,通常等价于long
或long long
,用于表示时间的值。参数timer
是一个指向time_t
类型的指针,如果传递了非空指针,那么当前时间也会存储在timer
指向的位置。如果传递的是空指针,则只返回当前时间。
time
函数返回的是当前的原始时间戳,即从1970年1月1日00:00:00 UTC到现在的秒数。这个时间戳可以转换为本地时间或者GMT时间,转换可以通过localtime
和gmtime
函数实现,它们将原始时间戳转换为一个struct tm
结构体,该结构体包含了年、月、日、时、分、秒等信息。
需要注意的是,使用time
函数需要包含<time.h>
头文件,并且在使用前应确保对time_t
类型有足够的了解,以便正确处理返回的时间值。
-
time_t
是C语言中表示时间的数据类型,通常用于存储从1970年1月1日(称为UNIX纪元)到当前时间的秒数。它通常用于处理日期和时间相关的操作。 -
struct tm
是 C 语言中定义的一个结构体,用于表示日期和时间。它通常与time.h
库中的函数一起使用,例如localtime
或gmtime
,这些函数可以将一个时间戳转换为struct tm
结构体。
struct tm
的定义如下:
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代表1月) - 取值区间为[0,11]
int tm_year; // 年份,其值等于实际年份减去1900
int tm_wday; // 星期几(从0开始,0代表星期天) - 取值区间为[0,6]
int tm_yday; // 一年中的天数(从0开始) - 取值区间为[0,365]
int tm_isdst; // 夏令时标识符,正数代表使用夏令时,负数代表未使用,0代表不确定
};
当你需要处理日期和时间相关的操作时,可以使用 struct tm
结构体。例如,你可以将一个时间戳转换为本地时间,然后提取其中的年、月、日等信息。
localtime
和 gmtime
函数:
localtime
函数:
struct tm *localtime(const time_t *timep);
gmtime
函数:
struct tm *gmtime(const time_t *timep);
这两个函数都接受一个指向time_t
类型的指针作为参数,通常这个参数是使用time
函数获取的当前时间戳。它们返回一个指向struct tm
结构体的指针,该结构体包含了分解的时间信息,如年、月、日、时、分、秒等。
需要注意的是,gmtime
返回的是协调世界时(UTC),而localtime
返回的是本地时间。此外,还有线程安全的变体gmtime_r和localtime_r,它们接受一个额外的struct tm
指针参数,用于存储结果。
下面举例说明如何使用这两个函数:
#include <stdio.h>
#include <time.h>
int main() {
time_t rawtime = time(NULL); // 获取当前时间戳
struct tm *local_info = localtime(&rawtime); // 转换为本地时间
struct tm *gm_info = gmtime(&rawtime); // 转换为UTC时间
// 输出本地时间
printf("本地时间:%04d-%02d-%02d %02d:%02d:%02d\n",
local_info->tm_year + 1900, local_info->tm_mon + 1, local_info->tm_mday,
local_info->tm_hour, local_info->tm_min, local_info->tm_sec);
// 输出UTC时间
printf("UTC时间:%04d-%02d-%02d %02d:%02d:%02d\n",
gm_info->tm_year + 1900, gm_info->tm_mon + 1, gm_info->tm_mday,
gm_info->tm_hour, gm_info->tm_min, gm_info->tm_sec);
return 0;
}
输出:
本地时间:2024-04-25 14:30:52
UTC时间:2024-04-25 06:30:52
在这个例子中,首先使用time
函数获取当前的原始时间戳,并存储在rawtime
变量中。然后,通过调用localtime
和gmtime
函数分别将原始时间戳转换为本地时间和UTC时间,并存储在local_info
和gm_info
指针中。最后,通过访问struct tm
结构体的成员来获取年、月、日、时、分、秒,并使用printf
函数输出结果。注意,年份需要加上1900,月份需要加1,因为struct tm
结构体中年份是从1900年开始计算的,月份是从0开始计算的。
什么是UTC
UTC(Coodinated Universal Time),协调世界时,又称世界统一时间、世界标准时间、国际协调时间、国际标准时间。由于英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称UTC。
UTC 是现在全球通用的时间标准,全球各地都同意将各自的时间进行同步协调。UTC 时间是经过平均太阳时(以格林威治时间GMT为准)、地轴运动修正后的新时标以及以秒为单位的国际原子时所综合精算而成。
本地时间
在日常生活中所使用的时间我们通常称之为本地时间。这个时间等于我们所在(或者所使用)时区内的当地时间,它由与世界标准时间(UTC)之间的偏移量来定义。这个偏移量可以表示为 UTC- 或 UTC+,后面接上偏移的小时和分钟数。
例如,中国使用的 北京时间 = UTC+8
2. 获取时间戳
inline uint32_t GetTick()
{
#if defined(_MSC_VER)
LARGE_INTEGER tFrequency = { 0 };
LARGE_INTEGER tPerformanceCount = { 0 };
// 获取晶振频率
QueryPerformanceFrequency(&tFrequency);
// 获取晶振数
QueryPerformanceCounter(&tPerformanceCount);
// 计算获得时间片
return (uint32_t )((tPerformanceCount.QuadPart * 1000 / tFrequency.QuadPart) & 0x00000000FFFFFFFF);
#else
struct timespec tTimeTick = {0};
clock_gettime(CLOCK_MONOTONIC, &tTimeTick);
return (uint32_t )((tTimeTick.tv_sec * 1000) + (tTimeTick.tv_nsec / 1000000));
#endif
}
inline uint64_t GetTickU64()
{
#if defined(_MSC_VER)
LARGE_INTEGER tFrequency = { 0 };
LARGE_INTEGER tPerformanceCount = { 0 };
// 获取晶振频率
QueryPerformanceFrequency(&tFrequency);
// 获取晶振数
QueryPerformanceCounter(&tPerformanceCount);
// 计算获得时间片
return (uint64_t )(((tPerformanceCount.QuadPart * 1000 / tFrequency.QuadPart) & 0x00000000FFFFFFFF) * 1000);
#else
struct timespec tTimeTick = {0};
clock_gettime(CLOCK_MONOTONIC, &tTimeTick);
return (uint64_t )(((uint64_t)tTimeTick.tv_sec * 1000000) + ((uint64_t)tTimeTick.tv_nsec / 1000));
#endif
}
这段代码是一个名为GetTick的内联函数,用于获取当前的时间戳。它根据不同的编译器环境(_MSC_VER)使用不同的方法来获取时间戳。
在Windows系统下(_MSC_VER),它使用了QueryPerformanceFrequency和QueryPerformanceCounter函数来获取高精度的时间戳。首先,通过调用QueryPerformanceFrequency函数获取晶振频率tFrequency。然后,通过调用QueryPerformanceCounter函数获取当前的晶振数tPerformanceCount。最后,将tPerformanceCount乘以1000除以tFrequency,得到以毫秒为单位的时间戳,并将其转换为uint32_t类型返回。
在其他系统下,它使用了clock_gettime函数来获取时间戳。首先,定义一个timespec结构体变量tTimeTick。然后,调用clock_gettime函数,传入CLOCK_MONOTONIC参数表示获取单调递增的时间戳。最后,将tTimeTick的秒数乘以1000加上纳秒数除以1000000,得到以毫秒为单位的时间戳,并将其转换为uint32_t类型返回。
相关知识:
timespec
是C语言标准库中定义的一个结构体,用于表示时间。
在C语言的标准库中,time.h
头文件提供了多个与时间相关的函数和数据类型。timespec
结构体就是其中之一,它用于高精度的时间表示,包括秒和纳秒两个部分。这允许程序进行精细的时间测量和时间操作。
timespec
结构体通常与clock_gettime
函数一起使用,该函数能够获取当前时钟的时间,并将其存储在timespec
结构体中。这个结构体的定义如下:
struct timespec {
time_t tv_sec; /* 秒 */
long tv_nsec; /* 纳秒 */
};
tv_sec
字段是一个time_t
类型的成员,代表自1970年1月1日(UNIX纪元)以来的秒数。tv_nsec
字段是一个long
类型的成员,代表纳秒数,与tv_sec
结合使用,可以提供高达纳秒级别的时间精度。
在C11标准(ISO / IEC 9899:2011)中,timespec
结构体被正式定义,并且在time.h
头文件中进行声明。这意味着任何遵循C11或更高版本的C语言标准实现都应该支持timespec
结构体。
需要注意的是,在Linux编程中,timespec结构体通常与clock_gettime函数一起使用,该函数可以获取特定时钟的时间,并将其存储在timespec结构体中。CLOCK_REALTIME是常用的时钟类型之一,它表示当前的墙上时间。
总的来说,timespec
是C语言标准库中的一个重要组成部分,它为程序员提供了一个精确的时间表示方法,特别是在需要高精度时间测量的场景中。
CLOCK_MONOTONIC
和 CLOCK_REALTIME
是Linux系统中两种不同的时钟类型,它们在计时方面有一些不同的特点。
-
CLOCK_MONOTONIC
:这是一个不可调整的时钟,它从某个固定点开始计时,不会因为系统时间的改变而改变。它主要用于测量时间间隔,因为它不受系统时间的影响。它的精度通常比CLOCK_REALTIME
高,但可能受到系统负载等因素的影响。 -
CLOCK_REALTIME
:这是一个可调整的时钟,它表示的是实际的墙上时钟时间。当系统时间被修改时,它也会相应地改变。它的精度通常较低,但可以用于测量实际经过的时间。
CLOCK_MONOTONIC
是从系统启动时刻开始计时的。
CLOCK_MONOTONIC
是一种计时方式,它从系统启动的那一刻起开始计算时间,并且不受系统时间被用户改变的影响。即使用户更改了系统时间,CLOCK_MONOTONIC
也不会受到影响,因为它是单调递增的,只与系统启动后经过的时间有关。
CLOCK_MONOTONIC
与gettimeofday
函数之间的关系是,它们都可以用来获取当前时间,但CLOCK_MONOTONIC
提供的是从系统启动到现在的单调时间,而gettimeofday
返回的是自1970年1月1日以来的实时时间(墙上时间)。
在编写需要精确时间测量的代码时,选择使用CLOCK_MONOTONIC
可以确保时间的测量不受系统时间更改的影响,从而提供更加稳定和可靠的时间基准。
在使用clock_gettime
函数获取时间时,可以选择使用这两种时钟类型之一。例如:
#include <time.h>
#include <stdio.h>
int main() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
printf("CLOCK_MONOTONIC: %ld.%09ld seconds since the Epoch", ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_REALTIME, &ts);
printf("CLOCK_REALTIME: %ld.%09ld seconds since the Epoch", ts.tv_sec, ts.tv_nsec);
return 0;
}
这段代码分别获取了CLOCK_MONOTONIC
和CLOCK_REALTIME
两种时钟类型的当前时间,并将其打印出来。
QueryPerformanceFrequency
和 QueryPerformanceCounter
函数:
QueryPerformanceFrequency
函数:
BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
QueryPerformanceCounter
函数:
BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
这两个函数都返回一个布尔值,表示操作是否成功。它们都接受一个指向LARGE_INTEGER
类型的指针作为参数,用于存储结果。
QueryPerformanceFrequency和QueryPerformanceCounter不是C标准库定义的。
这两个函数是Windows操作系统提供的API,用于高精度的性能测量。它们要求计算机从硬件上支持高精度定时器,并且需要包含windows.h头文件才能使用。具体来说:
- QueryPerformanceFrequency:这个函数的作用是获取高精度性能计数器的频率,即每秒的计数次数。它的函数原型为
BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
,其中LARGE_INTEGER
是一个特殊的数据类型,用于存储64位整数值。 - QueryPerformanceCounter:这个函数的作用是获取高精度性能计数器的当前值。它的函数原型为
BOOL QueryPerformanceCounter(LARGE_INTEGER *lpCount);
,同样使用LARGE_INTEGER
类型的参数来接收当前的计数值。
需要注意的是,这两个函数的使用通常与LARGE_INTEGER
类型紧密相关,而LARGE_INTEGER
类型是Windows API中定义的,不是C标准库的一部分。此外,这些函数提供的时间精度非常高,适用于需要微秒级别或更高时间精度的场合。在多线程编程或者需要精确测量程序性能的场景中尤其有用。
下面举例说明如何使用这两个函数获取时间戳:
#include <stdio.h>
#include <windows.h>
int main() {
LARGE_INTEGER frequency; // 定义频率变量
LARGE_INTEGER counter; // 定义计数器变量
if (QueryPerformanceFrequency(&frequency)) { // 获取频率
printf("Frequency: %llu Hz\n", frequency.QuadPart);
} else {
printf("Failed to get frequency.\n");
}
if (QueryPerformanceCounter(&counter)) { // 获取计数器值
printf("Counter: %llu \n", counter.QuadPart);
} else {
printf("Failed to get counter value.\n");
}
// 计算时间戳
double timestamp = (double)counter.QuadPart / (double)frequency.QuadPart;
printf("Timestamp: %f seconds\n", timestamp);
return 0;
}
在这个例子中,首先定义了两个LARGE_INTEGER
类型的变量frequency
和counter
,分别用于存储频率和计数器的值。然后,通过调用QueryPerformanceFrequency
和QueryPerformanceCounter
函数来获取相应的值,并将结果存储在对应的变量中。接着,使用计数器的值除以频率的值,得到时间戳(单位为秒)。最后,使用printf
函数输出结果。需要注意的是,LARGE_INTEGER
类型是一个64位整数类型,可以使用QuadPart
成员来访问其值。
LARGE_INTEGER 不是C标准库定义的。
LARGE_INTEGER是一个Windows平台特有的数据类型,用于表示一个64位的整数。它是一个union(联合体),在32位和64位编程环境中都可以使用。其具体定义如下:
typedef union _LARGE_INTEGER {
struct {
DWORD LowPart;
LONG HighPart;
} DUMMYSTRUCTNAME;
LONGLONG QuadPart;
} LARGE_INTEGER;
这个结构的意义在于,当编译器支持64位整数时,可以直接使用QuadPart成员来存取64位整数值。如果编译器不支持64位整数,则可以通过LowPart和HighPart成员来分别存取低32位和高32位,其中HighPart的最高位用作符号位。
总的来说,LARGE_INTEGER是Windows API中的一部分,而不是C标准库的一部分。它的设计旨在帮助开发者在不同位数的编程环境中处理大整数,特别是在需要与硬件相关的性能计数器等高级功能交互时。