最簡單的time
在C語言編程中,處理時間最簡單的函數就是time了。它的原型為:
#include <time.h> time_t time(time_t *_Nullable tloc);
返回自從EPOCH,即1970年1月1日的零點零時零分,到當前的秒數。
輸入參數可以是NULL。如果輸入參數不是NULL,那么返回值也會存入tloc的地址處。
需要注意的是,在不同的平臺上,這里的time_t
定義可能不同。
在glibc-devel-2.41
中,/usr/include/bits/types/time_t.h
中有如下定義:
#include <bits/types.h>5 6 /* Returned by `time'. */7 #ifdef __USE_TIME64_REDIRECTS8 typedef __time64_t time_t;9 #else10 typedef __time_t time_t;11 #endif
而__time64_t
與__time_t
的定義則如下:
__STD_TYPE __TIME64_T_TYPE __time64_t;__STD_TYPE __TIME_T_TYPE __time_t; /* Seconds since the Epoch. */
再繼續跟下去,涉及到各種宏等分支,最終的定義在Fedora 42的X86_64平臺上,這個值是long int。
Linux中的gettimeofday/settimeofday
time簡單,但是也粗糙,它只能取到秒這個單位。而gettimeofday則可能取到微秒。
與之相對,settimeofday則可以設置系統時間。
gettimeofday/settimeofday的原型如下:
#include <sys/time.h>int gettimeofday(struct timeval *restrict tv,struct timezone *_Nullable restrict tz);int settimeofday(const struct timeval *tv,const struct timezone *_Nullable tz);
struct timeval的定義為:
struct timeval { time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ };
tv_sec
就是秒數,tv_usec
是微秒。
struct timezone的定義為:
struct timezone { int tz_minuteswest; /* minutes west of Greenwich */ int tz_dsttime; /* type of DST correction */ };
當執行gettimeofday之后,返回0表示成功,非0表示失敗。
需要注意的是,tz這個結構已經過時,在我們使用gettimeofday與settimeofday的時候,直接把tz設為NULL,取得或者設置當前的本地時間就好了。
Windows中的GetLocalTime/SetLocalTime
在Windows環境下,取得與設置時間,可以使用GetLocalTime與SetLocalTime函數。
這兩個函數的原型如下:
void GetLocalTime([out] LPSYSTEMTIME lpSystemTime
);BOOL SetLocalTime([in] const SYSTEMTIME *lpSystemTime
);
相關的數據結構定義如下:
typedef struct _SYSTEMTIME {WORD wYear;WORD wMonth;WORD wDayOfWeek;WORD wDay;WORD wHour;WORD wMinute;WORD wSecond;WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
其中:
wYear
年數,范圍是從1601到30872。
mMonth
月數,范圍是從1到12。
mDayOfWeek
周偏移數,范圍是從0到6。
wDay
天數,范圍是從 1 到 31。
wHour
小時數,范圍是從 0 到 23。
wMinute
分鐘數,范圍是從 0 到 59。
wSecond
秒數,范圍是從 0 到 59。
wMilliseconds
毫秒數,范圍是從 0 到 999。
還需要注意,SetLocalTime的返回值是布爾值,成功為非0。
以下代碼,把一個從Linux取得的timeval時間,轉化成Windows上的時間,進行設置:
int set_local_time_by_timeval (const struct timeval *tv)
{struct tm utc_tm;SYSTEMTIME st = { 0 }; if (localtime_s (&utc_tm, &tv->tv_sec) != 0) { return -1;} st.wYear = utc_tm.tm_year + 1900; st.wMonth = utc_tm.tm_mon + 1; st.wDay = utc_tm.tm_mday; st.wHour = utc_tm.tm_hour; st.wMinute = utc_tm.tm_min; st.wSecond = utc_tm.tm_sec; st.wMilliseconds = (WORD)(time % (1000 * 1000) / 1000); return SetLocalTime (&st) ? 0 : -1;
glib里的g_get_monotonic_time
如果我們的項目使用了glib(注意是glib,不是glibc),還有一個方便的函數可以使用,那就是g_get_monotonic_time
。
這個函數表示系統啟動以來經過的微秒數,用來做一些時間間隔設計等工作。
比如,我們需要開發一些timer類功能,在程序經過若干時間之后,執行什么操作,如果使用time或者gettimeofday之類的函數,在系統時間改變之后,就會錯亂。
但是,如果我們使用g_get_monotonic_time
則可以完全避免這種問題。
如:
gint64 start = g_get_monotonic_time();/* 此處是需要測量的業務代碼 */
/* 而且有可能系統時間更改 */gint64 end = g_get_monotonic_time();
g_print("耗時: %" G_GINT64_FORMAT " 微秒\n", end - start);
glib的這個函數有點像C++中的std::chrono::steady_clock
。
底層的clock_gettime
如果進行更精確地時間控制,在Linux中還可以使用clock族的幾個函數:
#include <time.h> int clock_getres(clockid_t clockid, struct timespec *_Nullable res); int clock_gettime(clockid_t clockid, struct timespec *tp); int clock_settime(clockid_t clockid, const struct timespec *tp);
其中,clockid是可以控制的一些參數,如CLOCK_MONOTONIC、CLOCK_BOOTTIME、CLOCK_REALTIME等,具體意義可以見文知義。
而timespec的定義類似于timeval,但是第二個參數不是微秒,而是納秒。
#include <time.h> struct timespec { time_t tv_sec; /* Seconds */ /* ... */ tv_nsec; /* Nanoseconds [0, 999'999'999] */ };