C 标准库 <time.h> 函数详解

发布于:2025-07-28 ⋅ 阅读:(13) ⋅ 点赞:(0)

目录

概述

1 核心数据类型

1.1 time_t

 1.2 clock_t

1.3 struct tm

1.4 size_t

2 核心函数

2.1 时间获取函数

2.2 时间转换函数

2.3 时间差计算

2.4  时间格式化函数

3 线程安全版本(POSIX 扩展)

3.1 函数列表 

 3.2 时间处理完整示例

4 重要注意事项

5 实际应用场景

5.1 日志记录

5.2 定时任务

5.3 性能分析

5.4 时间戳转换

6 Nordic MCU上验证


概述

本文主要介绍C 标准库 <time.h> 函数,<time.h> 是 C 语言中处理时间和日期的标准库,提供了一系列函数和数据类型用于时间获取、转换和格式化操作提供了一套完整的时间处理工具链,尽管在精度和时区处理上有一定限制,但对于大多数应用场景已经足够。在开发跨平台应用时,应注意不同系统对线程安全和时区支持的差异。

1 核心数据类型

1.1 time_t

  • 描述:表示日历时间的算术类型(通常为整数)

  • 特性

  • 存储从 "纪元"(通常为 1970-01-01 00:00:00 UTC)起经过的秒数

  • 在 32 位系统上最大值为 2038-01-19 03:14:07(Y2038 问题)

  • 64 位系统可表示约 2920 亿年的时间范围

 1.2 clock_t

  • 描述:表示处理器时间的算术类型

  • 用途:测量程序执行时间(CPU 时间)

1.3 struct tm

  • 描述:分解时间结构

struct tm {
    int tm_sec;   // 秒 [0-60](允许闰秒)
    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(未知)
};

1.4 size_t

  • 用于 strftime 函数表示缓冲区大小

2 核心函数

2.1 时间获取函数

1) time_t time(time_t *timer)

  • 功能:获取当前日历时间

  • 参数

    • timer:存储结果的指针(可为 NULL)

  • 返回

    • 成功:当前时间(从纪元开始的秒数)

    • 失败:(time_t)(-1)

time_t now;
now = time(NULL);  // 获取当前时间

2) clock_t clock(void)

  • 功能:获取程序使用的处理器时间

  • 返回

    • 成功:从程序启动开始的 CPU 时间(时钟滴答数)

    • 失败:(clock_t)(-1)

  • 注意:需除以 CLOCKS_PER_SEC 转换为秒

clock_t start = clock();
// 执行代码...
clock_t end = clock();
double cpu_time = (double)(end - start) / CLOCKS_PER_SEC;

2.2 时间转换函数

1) struct tm *gmtime(const time_t *timer);

  • 功能:将日历时间转换为 UTC 时间

  • 参数timer - 指向日历时间的指针

  • 返回:指向静态 tm 结构的指针(非线程安全)

time_t now = time(NULL);
struct tm *utc_time = gmtime(&now);

 2) struct tm *localtime(const time_t *timer);

  • 功能:将日历时间转换为本地时间(考虑时区和夏令时)

  • 参数timer - 指向日历时间的指针

  • 返回:指向静态 tm 结构的指针(非线程安全)

time_t now = time(NULL);
struct tm *local_time = localtime(&now);

3) time_t mktime(struct tm *timeptr);

  • 功能:将本地时间转换为日历时间

  • 特性

    • 自动规范化超出范围的字段

    • 计算并填充 tm_wday 和 tm_yday

    • 正确处理夏令时

struct tm new_year = {0};
new_year.tm_year = 124; // 2024年(2024-1900)
new_year.tm_mon = 0;    // 一月
new_year.tm_mday = 1;   // 1号
time_t timestamp = mktime(&new_year);

2.3 时间差计算

double difftime(time_t time1, time_t time0);

  • 功能:计算两个日历时间的差值(秒)

  • 返回time1 - time0(以秒为单位的双精度值)

time_t start = time(NULL);
// 执行操作...
time_t end = time(NULL);
double elapsed = difftime(end, start);

2.4  时间格式化函数

1) char *asctime(const struct tm *timeptr);

  • 功能:将 tm 结构转换为固定格式字符串

  • 格式"Day Mon dd hh:mm:ss yyyy\n"

  • 返回:指向静态缓冲区的指针(非线程安全)

struct tm *t = localtime(&now);
printf("Current: %s", asctime(t));
// 输出示例:Tue Jun 18 14:30:45 2024

2) char *ctime(const time_t *timer);

  • 功能:将日历时间转换为本地时间字符串

  • 等效asctime(localtime(timer))

  • 返回:指向静态缓冲区的指针(非线程安全)

time_t now = time(NULL);
printf("Current: %s", ctime(&now));

3) size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr);

  • 功能:自定义格式化时间输出

  • 参数

    • str:输出缓冲区

    • maxsize:缓冲区最大容量

    • format:格式字符串

    • timeptr:指向 tm 结构的指针

  • 返回:写入字符数(不包括终止空字符),失败返回0

常用格式说明符

说明符 含义 示例
%Y 四位年份 2024
%y 两位年份 24
%m 月份(01-12) 06
%d 月中的日(01-31) 18
%H 24小时制小时(00-23) 14
%I 12小时制小时(01-12) 02
%M 分钟(00-59) 30
%S 秒(00-60) 45
%A 完整星期名 Tuesday
%a 缩写星期名 Tue
%B 完整月份名 June
%b 缩写月份名 Jun
%p AM/PM PM
%Z 时区名 CST
%z UTC偏移(+HHMM) +0800
%F ISO 8601日期格式 2024-06-18
%T ISO 8601时间格式 14:30:45

char buffer[80];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S %Z", localtime(&now));
printf("Formatted: %s\n", buffer); // 2024-06-18 14:30:45 CST

3 线程安全版本(POSIX 扩展)

3.1 函数列表 

标准函数 线程安全版本
gmtime gmtime_r
localtime localtime_r
asctime asctime_r
ctime ctime_r

使用案例:

// 线程安全用法示例
struct tm result;
localtime_r(&now, &result);

char buffer[80];
asctime_r(&result, buffer);

 3.2 时间处理完整示例

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

int main() {
    // 获取当前时间
    time_t now = time(NULL);
    
    // 转换为本地时间
    struct tm *local = localtime(&now);
    printf("当前时间: %s", asctime(local));
    
    // 自定义格式化
    char time_str[100];
    strftime(time_str, sizeof(time_str), "今天是 %Y年%m月%d日 %A", local);
    printf("%s\n", time_str);
    
    // 计算未来时间
    struct tm future = *local;
    future.tm_mday += 30; // 30天后
    mktime(&future); // 规范化时间
    
    strftime(time_str, sizeof(time_str), "30天后: %Y-%m-%d %H:%M:%S", &future);
    printf("%s\n", time_str);
    
    // 测量CPU时间
    clock_t start = clock();
    for (volatile long i = 0; i < 1000000000; i++); // 耗时循环
    clock_t end = clock();
    printf("CPU时间: %.2f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
    
    // 计算时间差
    time_t end_time = time(NULL);
    printf("实际时间: %.2f秒\n", difftime(end_time, now));
    
    return 0;
}

4 重要注意事项

  1. 非线程安全性

    • gmtimelocaltimeasctimectime 使用静态缓冲区

    • 多线程环境中应使用 _r 后缀的线程安全版本

  2. 时区处理

    • 时区信息通常来自系统环境变量 TZ

    • 可使用 tzset() 函数(POSIX)初始化时区设置

  3. Y2038 问题

    • 32 位系统的 time_t 在 2038 年会溢出

    • 解决方案:使用 64 位系统或专门的时间库

  4. 夏令时处理

    • localtime 和 mktime 自动处理夏令时

    • 设置 tm_isdst = -1 让系统自动确定

  5. 精度限制

    • 标准时间函数精度通常为秒级

    • 更高精度需求需使用平台特定 API(如 POSIX clock_gettime()

5 实际应用场景

5.1 日志记录

void log_message(const char *msg) {
    time_t now = time(NULL);
    char time_str[30];
    strftime(time_str, sizeof(time_str), "[%Y-%m-%d %H:%M:%S]", localtime(&now));
    printf("%s %s\n", time_str, msg);
}

5.2 定时任务

void daily_task() {
    time_t now = time(NULL);
    struct tm *t = localtime(&now);
    
    // 每天凌晨2点执行
    if (t->tm_hour == 2 && t->tm_min == 0) {
        perform_maintenance();
    }
}

5.3 性能分析

void profile_function() {
    clock_t start = clock();
    
    // 被分析的函数
    expensive_operation();
    
    clock_t end = clock();
    printf("CPU时间: %.4f秒\n", 
           (double)(end - start) / CLOCKS_PER_SEC);
}

5.4 时间戳转换

time_t parse_iso8601(const char *str) {
    struct tm tm = {0};
    sscanf(str, "%d-%d-%dT%d:%d:%d",
           &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
           &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
    tm.tm_year -= 1900;
    tm.tm_mon -= 1;
    tm.tm_isdst = -1;
    return mktime(&tm);
}

6 Nordic MCU上验证

源代码:

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * File Name        :  app_rtc.c
 * Description      :  define the macro parameters
 ******************************************************************************
 * @attention
 *
 *  COPYRIGHT:    Copyright (c) 2025  mingfei_tang
 *  DATE:         MAR 27th, 2025
 ******************************************************************************
 */
/* USER CODE END Header */
#include "app_rtc.h"

static time_t g_rtc_cur_stamp = 0;

static bool rtc2tm(et_rtc_t *p_rtc, struct tm *p_tm, bool r2t)
{
	if (r2t)
	{
		p_tm->tm_year = 2000 + p_rtc->year - 1900;
		p_tm->tm_mon = p_rtc->month - 1;
		p_tm->tm_mday = p_rtc->day;
		p_tm->tm_hour = p_rtc->hour;
		p_tm->tm_min = p_rtc->minute;
		p_tm->tm_sec = p_rtc->second;
	}
	else
	{
		p_rtc->year = p_tm->tm_year + 1900 - 2000;
		p_rtc->month = p_tm->tm_mon + 1;
		p_rtc->day = p_tm->tm_mday;
		p_rtc->hour = p_tm->tm_hour;
		p_rtc->minute = p_tm->tm_min;
		p_rtc->second = p_tm->tm_sec;
	}

	return (p_tm->tm_year >= 2024 - 1900) &&
		   (p_tm->tm_mon >= 0) && (p_tm->tm_mon <= 11) &&
		   (p_tm->tm_mday >= 1) && (p_tm->tm_mday <= 31) &&
		   (p_tm->tm_hour >= 0) && (p_tm->tm_hour <= 23) &&
		   (p_tm->tm_min >= 0) && (p_tm->tm_min <= 59) &&
		   (p_tm->tm_sec >= 0) && (p_tm->tm_sec <= 59);
}


void rtc_expiry_function_callback(void )
{
	g_rtc_cur_stamp += 1;

    app_rtc_test();
}


void app_rtc_set( et_rtc_t *prtc)
{
    struct tm t;
    rtc2tm(prtc, &t, true); 

    drv_grtc_stop();    
	g_rtc_cur_stamp = mktime(&t); 
    drv_grtc_start();
}


void app_rtc_get( et_rtc_t *p_rtc,  time_t *stamp)
{
	struct tm t;

    *stamp = g_rtc_cur_stamp;

    t = *localtime(stamp);

    rtc2tm(p_rtc, &t, false);
}

void app_init_rtc( void )
{
    et_rtc_t rtc;

    build_rtc( &rtc );

    drv_grtc_init();

    app_rtc_set( &rtc );
}


void app_rtc_test( void )
{
    time_t stamp;
    et_rtc_t rtc;

    app_rtc_get((et_rtc_t *)&rtc, &stamp);

	printk("rtc(%02d/%02d/%02d %02d:%02d:%02d)",
            rtc.year, rtc.month, rtc.day,
            rtc.hour, rtc.minute, rtc.second);    
}


void app_rtc_first_set( void )
{
    et_rtc_t _rtcObj;

    build_rtc( &_rtcObj );
    app_rtc_set( &_rtcObj );    
}

/** End of this file  */

验证数据:


网站公告

今日签到

点亮在社区的每一天
去签到