UTC时间戳是从格林威治时间1970-01-01 00:00:00至今的秒数。
UTC是国际时,UTC+8是东八区时间,即北京时间。东八区(UTC/GMT+08:00)是比世界协调时间(UTC)/格林尼治时间(GMT)快8小时的时区。东八区的标准时间为8:00。
NTP时间戳是从1900-01-01 00:00:00至今的秒数。
ntp时间戳的计算方式:
typedef UINT64 ntp64_t
ntp64_t ntp64_now(void)
{
unsigned int seconds;
unsigned int fraction;
#if defined(_WIN32) || defined(_WIN64)
FILETIME ft;
unsigned __int64 t;
GetSystemTimeAsFileTime((FILETIME*)&ft); // 100-nanosecond intervals since January 1, 1601 (UTC)
t = ((unsigned __int64)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
t -= 11642264611200L; // January 1, 1601 (UTC) -> January 1, 1900 (UTC).
// 100-nanosecond -> second
seconds = (unsigned int)(t / 10000000);
// ns * 1000 * 2^32 / 10^12
// 10^6 = 2^6 * 15625
// => ns * 2^26 / (15625 * 1000)
fraction = (unsigned int)((t % 10000000) / 156250.0 * 0x4000000);
#else
// seconds and microseconds since the Epoch(1970-01-01 00:00:00 +0000 (UTC))
struct timeval tv;
gettimeofday(&tv, 0);
seconds = (unsigned int)(tv.tv_sec + 0x83AA7E80); // 1/1/1970 -> 1/1/1900
fraction = (unsigned int)(tv.tv_usec / 15625.0 * 0x4000000);
#endif
return ((ntp64_t)seconds << 32) | fraction;
}
一般用于存储系统UTC时间到1970-01-01 00:00:00的秒数。time_t 上的存储值总是描述格林威治时间。
time_t在32位系统下最大只能表示到2038-01-19 03:14:07,在64位系统下则完全够用
通过结构体存储年月日时分秒。
struct tm
{
int tm_sec; /*秒,正常范围0-59, 但允许至61*/
int tm_min; /*分钟,0-59*/
int tm_hour; /*小时, 0-23*/
int tm_mday; /*日,即一个月中的第几天,1-31*/
int tm_mon; /*月, 从一月算起,0-11 1+p->tm_mon; */
int tm_year; /*年, 从1900至今已经多少年 1900+ p->tm_year;*/
int tm_wday; /*星期,一周中的第几天, 从星期日算起,0-6*/
int tm_yday; /*从今年1月1日到目前的天数,范围0-365*/
int tm_isdst; /*日光节约时间的旗标*/
};
#include
#include
time_t time(time_t* t);
获取系统UTC时间到1970-01-01 00:00:00的秒数。
struct tm *tm0 = NULL;
time_t tm;
time(&tm); //取得从1970年1月1日至今的秒数
tm0 = localtime(&tm); //返回经过时区转换的时间
time_t mktime(struct tm *tm);
输入值为struct tm结构,输出值为time_t。
将 struct tm 结构的时间转换为从 1970-01-01 年至今的秒数。
struct tm *stm = nullptr;
time_t tm;
time(&tm);
printf("tm: %lld\n", tm);
stm = localtime(&tm);
time_t tm1 = mktime(stm);
printf("tm1: %lld\n", tm1);
char *asctime(const struct tm *tm);
将 struct tm 结构中的信息转换为真实世界的时间(不经过时区转换的 UTC 时间),以字符串的形式显示。
time_t tm;
time(&tm);
printf("%s", asctime(gmtime(&tm)));
打印结果如下:
[root@localhost ~]# ./TimeTest
Tue Jan 3 19:11:07 2023
char *ctime(const time_t *timep);
将 time_t 时间秒表示的时间转换为真实世界的时间(经时区转换的 UTC 时间),以字符串显示。
time_t tm;
time(&tm);
printf("asctime: %s", asctime(gmtime(&tm)));
printf("ctime: %s", ctime(&tm));
打印结果如下:
asctime: Wed Jan 4 08:44:32 2023
ctime: Wed Jan 4 16:44:32 2023
#include
double difftime(time_t time1, time_t time0);
返回两个时间相差的秒数。
time_t start_t, end_t;
double diff_t;
time(&start_t);
sleep(5);
time(&end_t);
diff_t = difftime(end_t, start_t);
printf("difftime: %f\n", diff_t);
打印结果如下:
[root@localhost ~]# ./TimeTest
difftime: 5.000000
struct tm *gmtime(const time_t *timep);
将时间转换为格林尼治标准时间,然后将结果由tm结构体返回。
time_t tm;
struct tm *stm;
time(&tm);
/* 获取 GMT 时间 */
stm = gmtime(&tm);
printf("%2d:%02d\n", stm->tm_hour, stm->tm_min);
打印结果如下:
9:03
struct tm *localtime(const time_t *timep);
把从1970-01-01 00:00:00到当前时间系统所偏移的秒数时间转换为本地时间。
time_t tm;
struct tm *stm;
struct tm *stm1;
time(&tm);
stm = localtime(&tm);
stm1 = gmtime(&tm);
printf("localtime: %s", asctime(stm));
printf("gmtime: %s", asctime(stm1));
打印结果如下:
localtime: Wed Jan 4 09:14:36 2023
gmtime: Wed Jan 4 09:14:36 2023
struct tm *localtime_r(const time_t *timep, struct tm *result);
localtime是不可重入函数,非线程安全。其返回一个指针,实际的内存是localtime内部通过static申请的静态内存,通过localtime调用后的返回值如果不及时使用的话,可能被其他线程的localtime覆盖掉。
localtime_r是可重入函数,线程安全。 localtime_t 不仅需要传入指向 time_t 的一个常量指针,还需要传入指向 struct tm 的一个指针,结果将存储在 result 指向的 struct tm 对象中。
time_t curTime = time(NULL);
struct tm tm1;
localtime_r(&curTime, &tm1);
fprintf(stdout, "%04d%02d%02d%02d%02d%02d\n",
tm1.tm_year + 1900,
tm1.tm_mon + 1,
tm1.tm_mday,
tm1.tm_hour,
tm1.tm_min,
tm1.tm_sec);
打印结果如下:
[root@localhost ~]# ./TimeTest
20230104093123
#include
int gettimeofday(struct timeval *tv, struct timezone *tz);
结构体说明:
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of DST correction */
};
注意第二个参数现在基本不使用,原因是存在"虚假时钟"的问题:
The use of the timezone structure is obsolete; the tz argument should normally be specified as NULL. (See NOTES below.)
Under Linux there are some peculiar "warp clock" semantics associated with the settimeofday() system call if on the very first call (after booting) that has a non-NULL tz argument, the tv argument
is NULL and the tz_minuteswest field is nonzero. (The tz_dsttime field should be zero for this case.) In such a case it is assumed that the CMOS clock is on local time, and that it has to be
incremented by this amount to get UTC system time. No doubt it is a bad idea to use this feature.
获取到的是自1970-01-01 00:00:00 +0000 (UTC)以来的秒数和微秒数。
注意:
获取到的秒数和微秒数都是以UTC时间为基准来换算的,而不是本地时间。譬如:当前北京时间为2023-01-04 10:00:00,在计算时会先将北京时间转换成UTC时间2023-01-04 02:00:00,再去换算秒数和微秒数。
struct timeval tv;
int ret = gettimeofday(&tv, NULL);
if(0 == ret)
{
printf("sec: %lld, usec: %lld\n", tv.tv_sec, tv.tv_usec);
}
打印结果如下:
[root@localhost debug.x64-linux]# ./TimeTest
sec: 1672796493, usec: 975262
int settimeofday(const struct timeval *tv, const struct timezone *tz);
设置当前时间戳。linux下只有root权限才能使用该函数修改时间。
struct timeval tv;
int ret = gettimeofday(&tv, NULL);
if(0 == ret)
{
printf("sec: %lld, usec: %lld\n", tv.tv_sec, tv.tv_usec);
}
ret = settimeofday(&tv, NULL);
if(0 == ret)
{
printf("settimeofday success\n");
}
else
{
int errno = 0;
printf("settimeofday failed, error: %s\n", strerror(errno));
}
打印结果如下:
[root@localhost debug.x64-linux]# ./TimeTest
sec: 1672797166, usec: 21309
settimeofday success
DWORD GetTickCount(void);
计数机器启动至今毫秒数。
# 注意
1) 最多只能计数49.71天,如果windows这么长还未重启,计数失效;
2) 这个是以5秒92下的机器计数为基础的,精度只能精确到百毫秒;
#include
#include
using namespace std;
#pragma comment(lib, "Winmm.lib")
int main()
{
DWORD dwBegin, dwEnd;
dwBegin = GetTickCount();
Sleep(1000);
dwEnd = GetTickCount();
printf("difftime: %d\n", dwEnd - dwBegin);
system("pause");
return 0;
}
DWORD timeGetTime(VOID);
返回系统时间,以毫秒为单位。系统时间是从系统启动到调用函数时所经过的毫秒数。注意,这个值是32位的,会在0到2^32之间循环,约49.71天。
#include
#include
using namespace std;
#pragma comment(lib, "Winmm.lib")
int main()
{
DWORD dwBegin, dwEnd;
dwBegin = timeGetTime();
Sleep(1000);
dwEnd = timeGetTime();
printf("difftime: %d\n", dwEnd - dwBegin);
system("pause");
return 0;
}
void GetSystemTime(LPSYSTEMTIME lpSystemTime);
void GetLocalTime(LPSYSTEMTIME lpSystemTime);
BOOL SetSystemTime(const SYSTEMTIME* lpSystemTime);
获取系统时间。
#include
#include
using namespace std;
#pragma comment(lib, "Winmm.lib")
int main()
{
SYSTEMTIME st, lt;
GetSystemTime(&st);
GetLocalTime(<);
printf("%02d-%02d-%02d %02d:%02d:%02d.%02d\n", st.wYear,
st.wMonth,
st.wDay,
st.wHour,
st.wMinute,
st.wSecond,
st.wMilliseconds);
printf("%02d-%02d-%02d %02d:%02d:%02d.%02d\n", lt.wYear,
lt.wMonth,
lt.wDay,
lt.wHour,
lt.wMinute,
lt.wSecond,
lt.wMilliseconds);
system("pause");
return 0;
}
// 文件时间转系统时间
BOOL FileTimeToSystemTime(const FILETIME* lpFileTime,LPSYSTEMTIME lpSystemTime);
// 系统时间转文件时间
BOOL SystemTimeToFileTime(const SYSTEMTIME* lpSystemTime,LPFILETIME lpFileTime);
# 参数说明
lpFileTime: 指向 FILETIME 结构的指针,其中包含要转换为系统 (UTC) 日期和时间格式的文件时间。此值必须小于0x8000000000000000。 否则,该函数将失败。
lpSystemTime: 指向 SYSTEMTIME 结构的指针,用于接收转换的文件时间。
#include
#include
using namespace std;
#pragma comment(lib, "Winmm.lib")
int main()
{
const char* day[] = { "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" };
const char* month[] = { "January","February","March","April","May","June","July", "August","September","October","November","December" };
long long value = 132372033265000000; //文件时间
FILETIME ft = { 0 };
ft.dwHighDateTime = (value & 0xffffffff00000000) >> 32;
ft.dwLowDateTime = value & 0xffffffff;
SYSTEMTIME sys = { 0 };
FileTimeToSystemTime(&ft, &sys);
printf("%02d-%02d-%02d %02d:%02d:%02d.%02d\n", sys.wYear,
sys.wMonth,
sys.wDay,
sys.wHour,
sys.wMinute,
sys.wSecond,
sys.wMilliseconds);
system("pause");
return 0;
}
BOOL QueryPerformanceCounter(
[out] LARGE_INTEGER *lpPerformanceCount
);
获取微秒级时间戳,同linux下的gettimeofday函数。
#include
#include
using namespace std;
#pragma comment(lib, "Winmm.lib")
int main()
{
LARGE_INTEGER li = { 0 };
// 获取计时器频率
QueryPerformanceFrequency(&li);
double diff = li.QuadPart;
printf("Frequency: %lf\n", diff);
QueryPerformanceCounter(&li);
INT64 before = li.QuadPart;
Sleep(1000);
QueryPerformanceCounter(&li);
INT64 after = li.QuadPart;
printf("before: %lld\n", before);
printf("after: %lld\n", after);
printf("diff: %lld\n", after - before);
printf("diff: %lf\n", (after - before) * 1000 / diff);
system("pause");
return 0;
}
打印如下:
Frequency: 10000000.000000
before: 976944511621
after: 976954521296
diff: 10009675
diff: 1000.967500