linux 系统函数之 (localtime, localtime_r, strftime)

本文详细介绍了localtime和localtime_r两个函数的区别及用法,并提供了具体的代码示例。localtime可能因多线程并发调用导致数据被重写,而localtime_r则保证了线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                首先介绍一下localtime和localtime_r的区别。

                The  localtime()  function  converts  the calendar time timep to broken-down time representation, expressed relative to the user's specified timezone.  The
       function acts as if it called tzset(3) and sets the external variables tzname with information about the current timezone,  timezone  with  the  difference
       between  Coordinated  Universal  Time (UTC) and local standard time in seconds, and daylight to a nonzero value if daylight savings time rules apply during
       some part of the year.  The return value points to a statically allocated struct which might be overwritten by subsequent calls to any of the date and time
       functions.  The localtime_r() function does the same, but stores the data in a user-supplied struct.  It need not set tzname, timezone, and daylight.

               上面是man手册中的解释, localtime 不能同时使用,否则可能会被重写。 localtime_r则是线程安全的。

 

1. localtime

头文件:

            #include <time.h>

 函数定义:

            struct tm *localtime(const time_t *timep);

功能:

             把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为本地时间,而gmtimes函数转换后的时间没有经过时区变换,是UTC时间 。

参数:

              time_t  从1970-1-1时计算的utc时间。

返回值:

              返回指向tm 结构体的指针.tm结构体是time.h中定义的用于分别存储时间的各个量(年月日等)的结构体.

示例:               

              time_t tNow =time(NULL);

              time_t tEnd = tNow + 1800;

              struct tm* ptm = localtime(&tNow);

              struct tm* ptmEnd = localtime(&tEnd);

 

2. localtime_r

头文件:

#include <time.h>

函数定义:

struct tm *localtime_r(const time_t *timep, struct tm *result);

功能:

把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为本地时间,而gmtimes函数转换后的时间没有经过时区变换,是UTC时间 。

参数:

time_t 从1970-1-1时计算的utc时间。 tm 结构体用于获取返回的时间。

返回值:

返回指向tm 结构体的指针.tm结构体是time.h中定义的用于分别存储时间的各个量(年月日等)的结构体.

示例:

              time_t tNow =time(NULL);

             time_t tEnd = tNow + 1800;

             struct tm ptm = { 0 };

             struct tm ptmEnd = { 0 };

             localtime_r(&tNow, &ptm);

             localtime_r(&tEnd, &ptmEnd);

 

 

3. strftime

头文件:

#include <time.h>

函数定义:

size_t strftime(char *s, size_t max, const char *format,
                       const struct tm *tm);

功能:

strftime() 函数根据区域设置格式化本地时间/日期,函数的功能将时间格式化,或者说格式化一个时间字符串。

参数:  

我们可以根据format指向字符串中格式命令把timeptr中保存的时间信息放在strDest指向的字符串中,最多向strDest中存放maxsize个字符。该函数返回向strDest指向的字符串中放置的字符数。
函数strftime()的操作有些类似于sprintf():识别以百分号(%)开始的格式命令集合,格式化输出结果放在一个字符串中。格式化命令说明串strDest中各种日期和时间信息的确切表示方法。格式串中的其他字符原样放进串中。格式命令列在下面,它们是区分大小写的。
%a 星期几的简写
%A 星期几的全称
%b 月份的简写
%B 月份的全称
%c 标准的日期的时间串
%C 年份的后两位数字
%d 十进制表示的每月的第几天
%D 月/天/年
%e 在两字符域中,十进制表示的每月的第几天
%F 年-月-日
%g 年份的后两位数字,使用基于周的年
%G 年份,使用基于周的年
%h 简写的月份名
%H 24小时制的小时
%I 12小时制的小时
%j 十进制表示的每年的第几天
%m 十进制表示的月份
%M 十时制表示的分钟数
%n 新行符
%p 本地的AM或PM的等价显示
%r 12小时的时间
%R 显示小时和分钟:hh:mm
%S 十进制的秒数
%t 水平制表符
%T 显示时分秒:hh:mm:ss
%u 每周的第几天,星期一为第一天 (值从1到7,星期一为1)
%U 第年的第几周,把星期日作为第一天(值从0到53)
%V 每年的第几周,使用基于周的年
%w 十进制表示的星期几(值从0到6,星期天为0)
%W 每年的第几周,把星期一做为第一天(值从0到53)
%x 标准的日期串
%X 标准的时间串
%y 不带世纪的十进制年份(值从0到99)
%Y 带世纪部分的十制年份
%z,%Z 时区名称,如果不能得到时区名称则返回空字符。
%% 百分号

 

示例:

        struct timeval tv = {0,};
        struct tm      now = {0, };
        char           now_str[32];

        gettimeofday (&tv, NULL);

         localtime_r (&tv.tv_sec, &now);
        strftime (now_str, 32, "%Y/%m/%d-%H:%M:%S", &now);

        

 

 

<think>我们遇到了两个主要问题:1.在自定义Glog日志格式时使用了`localtime_r`函数,但出现未定义标识符错误。2.需要调整时间格式和日志输出格式(包括解决日期格式错误、文件名后多余的0以及日志内容换行问题)。针对问题1:`localtime_r`是POSIX标准函数,在Windows环境下不可用。因此,我们需要编写跨平台的代码。针对问题2:我们需要重新设计自定义前缀的格式,确保输出正确。以下是一个跨平台的自定义日志前缀函数实现,并解决了上述格式问题:步骤:1.使用`google::InstallFormatter`设置自定义格式化函数。2.在格式化函数中,将时间戳转换为本地时间(跨平台)。3.按需格式化输出(日期时间、日志级别、线程ID、文件名等)。4.注意:不要输出换行符,因为Glog会在日志消息后自动添加换行。具体实现:</think> ### 解决Glog自定义日志格式的未定义标识符问题 您遇到的核心问题是跨平台兼容性:`localtime_r`是POSIX标准函数,在Windows环境下不可用。以下是完整解决方案: #### 跨平台时间处理方案 ```cpp #include <glog/logging.h> #include <iomanip> void CustomLogPrefix(std::ostream& s, const google::LogMessageInfo& l) { // 跨平台时间处理 const time_t seconds = l.time; struct tm tm_time; // Windows使用localtime_s,POSIX使用localtime_r #ifdef _WIN32 localtime_s(&tm_time, &seconds); #else localtime_r(&seconds, &tm_time); #endif // 格式化时间戳:YYYY-MM-DD HH:MM:SS.microsec char time_buf[64]; strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", &tm_time); s << time_buf << "." << std::setfill('0') << std::setw(6) << l.usec; // 日志级别 const char* severity_str = "?"; switch (l.severity[0]) { // 直接使用首字母判断 case 'I': severity_str = "INFO"; break; case 'W': severity_str = "WARN"; break; case 'E': severity_str = "ERROR"; break; case 'F': severity_str = "FATAL"; break; } s << " [" << severity_str << "]"; // 线程ID s << " [TID:" << std::setw(6) << std::setfill('0') << l.thread_id << "]"; // 文件名(移除路径) const char* filename = strrchr(l.filename, '/'); filename = filename ? filename + 1 : l.filename; s << " [" << std::setw(20) << std::left << filename; // 行号(固定宽度) s << ":" << std::setw(4) << std::right << l.line << "]"; } ``` #### 初始化代码 ```cpp int main(int argc, char** argv) { google::InitGoogleLogging(argv[0]); google::InstallFormatter(&CustomLogPrefix); // 应用自定义格式 // 测试日志(内容与格式将在同一行) LOG(INFO) << "应用启动"; } ``` #### 解决的关键问题 1. **跨平台时间处理**: - Windows使用`localtime_s`函数[^1] - Linux/macOS使用`localtime_r`函数 - 时间格式:`2024-05-09 14:30:45.123456` 2. **文件名格式修正**: ```cpp // 输入: "/src/main.cpp" const char* filename = strrchr(l.filename, '/'); // 定位最后一个'/' filename = filename ? filename + 1 : l.filename; // 取"main.cpp" ``` 输出示例:`[main.cpp : 23]` 3. **日志内容换行**: - 不要在自定义函数中添加`<< std::endl` - Glog自动在消息末尾添加换行符 #### 性能优化建议(参考引用[2][3]) 1. 使用**异步日志**减少线程阻塞 2. 在IO密集型场景考虑SPDLOG(异步模式性能更佳[^3]) ```cpp // SPDLOG异步示例(每秒刷新) #include <spdlog/async.h> auto logger = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs.txt"); spdlog::flush_every(std::chrono::seconds(1)); // [^1] ``` #### 常见错误排查 1. **符号未定义问题**(引用[4]): - 检查glog库版本一致性 - 清理项目后重新编译: ```bash rm -rf build/ && mkdir build && cd build cmake .. && make ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值