Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux C 語言獲取系統時間完全指南:time、ctime、localtime、strftime 高精度與格式化

編輯:Linux 教學

在 C 語言編程中,獲取、轉換、格式化時間是基礎且高頻的需求。本文系統梳理 time()、ctime()、localtime()/gmtime()、strftime() 的用法差異、線程安全陷阱、高精度替代方案(clock_gettime),並提供可直接落地的代碼模板。

基礎時間類型

類型定義含義範圍/精度
time_t算術類型 (通常 long/int64_t)**日曆時間**:自 Unix 紀元 (1970-01-01 00:00:00 UTC) 經過的**秒數**秒級,32 位 2038 年溢出,64 位安全
struct tm分解時間結構體年、月、日、時、分、秒、星期、儒略日、夏令時標誌組件化,便於格式化/運算
struct timespectime_t tv_sec; long tv_nsec;**高精度時間**:秒 + 納秒納秒級,clock_gettime 返回
struct timevaltime_t tv_sec; suseconds_t tv_usec;微秒級時間 (POSIX.1-2001 標記過時)微秒級,gettimeofday 返回

核心函數族

1. time(time_t *tloc) — 獲取日曆時間

time_t now = time(NULL);  // 推薦用法,忽略參數
// 或
time_t now;
time(&now);
  • 返回:當前 UTC 秒數 (time_t),出錯返回 (time_t)-1。
  • 精度:秒級。

2. ctime(const time_t *timer) / ctime_r — 轉為固定格式字符串


char *ctime(const time_t *timer);       // 非線程安全,返回靜態緩衝區
char *ctime_r(const time_t *timer, char *buf); // 線程安全,需提供 26+ 字節緩衝區
  • 輸出格式固定:"Wed Jun 30 21:49:08 1993\n\0" (26 字符)。
  • 不可自定義格式,僅用於快速調試日志。

3. localtime / gmtime / localtime_r / gmtime_r — 分解時間


struct tm *localtime(const time_t *timer);      // 非線程安全,返回靜態 struct tm
struct tm *localtime_r(const time_t *timer, struct tm *result); // 線程安全
struct tm *gmtime(const time_t *timer);         // UTC 時間,非線程安全
struct tm *gmtime_r(const time_t *timer, struct tm *result);    // 線程安全
  • localtime*:按**本地時區** (環境變量 TZ、/etc/localtime) 轉換。
  • gmtime*:按 **UTC** 轉換。
  • 多線程程序必須使用 _r 後綴版本,避免靜態緩衝區競爭導致數據錯亂。

4. strftime — 靈活格式化 (核心推薦)


size_t strftime(char *buf, size_t maxsize, const char *format, const struct tm *tm);
  • 線程安全:僅操作用戶提供的緩衝區。
  • 格式化指令豐富 (常用):
指令含義示例
%Y四位數年份2024
%m兩位數月份 (01-12)06
%d兩位數日期 (01-31)30
%H24制小時 (00-23)21
%M分鐘 (00-59)49
%S秒 (00-60, 含閏秒)08
%F等價 %Y-%m-%d2024-06-30
%T等價 %H:%M:%S21:49:08
%z時區偏移 (+HHMM/-HHMM)+0800
%Z時區名稱CST
%sUnix 時間戳 (秒) (GNU 擴展)1719755348
  • 返回:寫入字符數 (不含結束符),0 表示緩衝區不足。

5. 高精度:clock_gettime (推薦替代 gettimeofday)


#include <time.h>
int clock_gettime(clockid_t clk_id, struct timespec *tp);
時鐘類型含義精度/用途
CLOCK_REALTIME系統實時時間 (可被 NTP/用戶調整)納秒級,壁鐘時間,受系統時間調整影響
CLOCK_MONOTONIC單調時間 (自啟動以來,不可調整)**測量間隔、超時、性能分析首選**,不受系統時間回撥影響
CLOCK_MONOTONIC_RAW原始單調時間 (無 NTP 頻率校準)硬件時鐘原始計數,最高精度
CLOCK_PROCESS_CPUTIME_ID進程 CPU 時間性能分析
CLOCK_THREAD_CPUTIME_ID線程 CPU 時間性能分析

實戰代碼模板

模板 1:線程安全獲取本地時間字符串 (strftime)


#include <time.h>
#include <stdio.h>

void get_local_time_str(char *buf, size_t len, const char *fmt) {
    time_t now = time(NULL);
    struct tm tm;
    localtime_r(&now, &tm);  // 線程安全
    strftime(buf, len, fmt, &tm);
}

int main() {
    char buf[64];
    get_local_time_str(buf, sizeof(buf), "%F %T %z");  // 2024-06-30 21:49:08 +0800
    puts(buf);
    return 0;
}

模板 2:解析時間組件 (年月日時分秒)


time_t now = time(NULL);
struct tm tm;
localtime_r(&now, &tm);

int year  = tm.tm_year + 1900;  // tm_year: years since 1900
int month = tm.tm_mon + 1;      // tm_mon: 0-11
int day   = tm.tm_mday;         // 1-31
int hour  = tm.tm_hour;         // 0-23
int min   = tm.tm_min;          // 0-59
int sec   = tm.tm_sec;          // 0-60 (含閏秒)
int wday  = tm.tm_wday;         // 0=Sun, 6=Sat
int yday  = tm.tm_yday;         // 0-365
int isdst = tm.tm_isdst;        // >0 夏令時, 0 非, <0 未知

模板 3:高精度測量代碼執行耗時


struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);

// ... 待測代碼 ...

clock_gettime(CLOCK_MONOTONIC, &end);
long long elapsed_ns = (end.tv_sec - start.tv_sec) * 1000000000LL + (end.tv_nsec - start.tv_nsec);
printf("Elapsed: %.3f ms\n", elapsed_ns / 1e6);

模板 4:解析字符串時間 → time_t (strptime, GNU 擴展)


#define _GNU_SOURCE
#include <time.h>

struct tm tm = {0};
char *res = strptime("2024-06-30 21:49:08", "%F %T", &tm);
if (res) {
    time_t t = mktime(&tm);  // 本地時區解析
    // time_t t = timegm(&tm); // UTC 解析 (BSD/GNU 擴展)
}

常見陷阱與避坑

  1. 多線程調用非 _r 函數:localtime/gmtime/ctime/asctime 返回靜態緩衝區,**極易導致數據錯亂**。**全項目強制使用 _r 版本**。
  2. tm_year 為距 1900 年數,**必須 +1900**;tm_mon 為 0-11,**必須 +1**。
  3. mktime() 會修正 struct tm 越界字段(如月份 13 → 翌年 1 月),並按**本地時區**轉換;timegm() 按 UTC 轉換(非標準,但廣泛可用)。
  4. time_t 在 32 位系統 2038 年溢出 (Y2038 問題)。**新代碼務必編譯為 64 位**,或使用 int64_t 顯式存儲時間戳。
  5. strftime 緩衝區不足返回 0,**務必檢查返回值**,避免截斷。預留 64-128 字節通常足夠。
  6. 測量耗時**嚴禁用 CLOCK_REALTIME**,NTP 時間同步導致時間回撥會產生負耗時。**必用 CLOCK_MONOTONIC**。

總結選型表

需求推薦方案關鍵點
獲取當前秒級時間戳time(NULL)簡單、可移植
格式化本地時間字符串time() + localtime_r() + strftime()線程安全、格式靈活
格式化 UTC 時間字符串time() + gmtime_r() + strftime()日誌系統、分布式追蹤統一用 UTC
解析時間組件運算localtime_r() 取 struct tm注意 tm_year/tm_mon 偏移
高精度間隔測量/基準測試clock_gettime(CLOCK_MONOTONIC, ...)納秒級、單調不回撥
解析字符串時間strptime() + mktime()/timegm()GNU 擴展,注意時區

掌握這套時間處理工具鏈,配合線程安全習慣與單調時鐘原則,能讓你在日誌系統、定時任務、協議時間戳、性能剖析等場景中編寫出精準、健壯、可移植的 C 代碼。

Copyright © Windows教程網 All Rights Reserved