ANSI C <time.h> 提供完整的時間獲取、解碼、格式化、運算功能。本文系統梳理核心數據結構、標準函數、時間運算技巧、文件時間戳獲取、跨平台注意事項,並提供可直接使用的代碼模板。
struct tm {
int tm_sec; // 秒 [0,61] (含閏秒)
int tm_min; // 分 [0,59]
int tm_hour; // 時 [0,23]
int tm_mday; // 日 [1,31]
int tm_mon; // 月 [0,11] (0=一月)
int tm_year; // 年 (從1900起)
int tm_wday; // 星期 [0,6] (0=星期日)
int tm_yday; // 年內天數 [0,365]
int tm_isdst; // 夏令時標志 (>0 生效, 0 未生效, <0 未知)
};
| 函數 | 功能 | 關鍵說明 |
|---|---|---|
time() | 獲取日曆時間 (time_t) | 參數為 NULL 時返回值;非 NULL 時也寫入參數指向位置 |
localtime() | time_t → 本地時間 tm | 返回**靜態結構體指針**,非線程安全,不可多線程共用 |
gmtime() | time_t → UTC 時間 tm | 同 localtime,返回靜態結構體 |
ctime() | time_t → 固定格式字符串 | 等價 asctime(localtime(&t)),返回靜態緩衝區 |
asctime() | tm → 固定格式字符串 | 返回靜態緩衝區 |
strftime() | tm → 自定義格式字符串 | **線程安全**,緩衝區由調用者提供,格式化符見下表 |
mktime() | 本地時間 tm → time_t | **修正 tm 字段越界值** (如月=12→年+1,月=0),更新 wday/yday |
difftime() | 兩 time_t 差值 (秒) | 返回 double,避免 time_t 可能為無符號類型導致的溢出 |
| 符號 | 含義 | 示例 |
|---|---|---|
%Y | 四位年份 | 2024 |
%m | 兩位月份 | 01-12 |
%d | 兩位日期 | 01-31 |
%H | 24制小時 | 00-23 |
%M | 分鐘 | 00-59 |
%S | 秒 | 00-61 |
%A | 完整星期名 | Monday |
%B | 完整月份名 | January |
%c | 標準日期時間 | Mon Jan 01 12:00:00 2024 |
%x | 標準日期 | 01/01/2024 |
%X | 標準時間 | 12:00:00 |
%Z | 時區名 | CST |
%% | 字面 % | % |
struct tm t = *localtime(&now);
t.tm_mday += 30; // 30 天后
mktime(&t); // 自動修正月份/年份溢出
printf("30 days later: %s", asctime(&t));
time_t start = time(NULL);
// ... 執行任務 ...
time_t end = time(NULL);
printf("Elapsed: %.0f seconds\n", difftime(end, start));
typedef struct { int year, month, day; } Date;
Date date_diff(Date earlier, Date later) {
Date diff = {0,0,0};
// 日差
diff.day = later.day - earlier.day;
if (diff.day < 0) {
// 借位:上個月天數
int prev_month = (later.month == 1) ? 12 : later.month - 1;
int prev_year = (later.month == 1) ? later.year - 1 : later.year;
diff.day += days_in_month(prev_year, prev_month);
later.month--;
}
// 月差
diff.month = later.month - earlier.month;
if (diff.month < 0) {
diff.month += 12;
later.year--;
}
// 年差
diff.year = later.year - earlier.year;
return diff;
}
ANSI C 無標準文件時間 API。POSIX stat() / Windows _stat() / GetFileTime() 是主流方案。
#include
#include
time_t get_file_mtime(const char *path) {
struct stat st;
if (stat(path, &st) == 0) return st.st_mtime;
return (time_t)-1;
}
#include
#include
time_t get_file_mtime(const char *path) {
struct _stat st;
if (_stat(path, &st) == 0) return st.st_mtime;
return (time_t)-1;
}
int is_newer(const char *file1, const char *file2) {
time_t t1 = get_file_mtime(file1);
time_t t2 = get_file_mtime(file2);
return (t1 != (time_t)-1 && t2 != (time_t)-1) && difftime(t1, t2) > 0;
}
int touch_file(const char *path) {
FILE *f = fopen(path, "r+b"); // 二進制讀寫,不截斷
if (!f) f = fopen(path, "wb"); // 不存在則創建
if (!f) return -1;
// 寫入首字節 (不改變內容) 觸發時間戳更新
fseek(f, 0, SEEK_SET);
int ch = fgetc(f);
fseek(f, 0, SEEK_SET);
fputc(ch, f);
fclose(f);
return 0;
}
localtime/gmtime/ctime/asctime (返回靜態緩衝區)localtime_r (POSIX) / localtime_s (C11/MSVC) — 調用者提供 struct tm 緩衝區strftime 天然線程安全 (用戶提供緩衝區)TZ 控制 localtime 行為setenv("TZ", "Asia/Shanghai", 1); tzset(); (POSIX)_putenv("TZ=GMT-8"); _tzset();time_t/gmtime),僅在顯示層按用戶時區格式化掌握 time.h 核心結構與函數、strftime 格式化、mktime 修正特性、跨平台 stat 獲取文件時間、線程安全 API 選擇,即可應對 99% 的 C 語言時間日期處理需求。