3.时间类的实现-c++ linux编程:从0实现muduo库系列
重点内容
这一讲的内容主要是时间类的实现,但时间类面试很少被问到,所以这里我们主要讲CMakeLists.txt如何生成库文件,以及make install的时候如何把对应的库文件和头文件安装到指定目录。
视频讲解:《C++Linux编程进阶:从0实现muduo C++网络框架系列》-第3讲.时间类的实现
主要改动
首先创建第三节课的目录并复制前1节课的内容:
cp -r lesson2 lesson3
muduo网络库框架:
- 实现:base/Timestamp.h/cc
- 实现:base/Date.h/cc
- 实现:base/TimeZone.h/cc
muduo网络库测试范例
- examples/test_timestamp.cc
- examples/test_date.cc
- examples/test_timezone.cc
CMakeLists.txt
- base/CMakeLists.txt
- examples/CMakeLists.txt
1 CMake生成库文件以及make install安装库文件和头文件
1.1 CMakeLists.txt主要改动
重点掌握
具体细节内容:
1.base/CMakeLists.txt
add_library(mymuduo_base ${base_SRCS}) 生成mymuduo_base静态库
target_link_libraries(mymuduo_base pthread) mymuduo_base依赖pthread库,默认优先依赖动态库
安装文件:
- 安装库文件:install(TARGETS mymuduo_base DESTINATION /usr/local/lib)
- 安装头文件:install(FILES ${base_HEADERS} DESTINATION /usr/local/include/mymuduo/base)
2.examples/CMakeLists.txt
target_link_libraries(test_date mymuduo_base) ,这里链接mymuduo_base这个库文件,可以注释后测试报什么错。
1.2 CMake语法讲解
1.2.1 add_library生成库文件
# 方式1:不指定类型,使用默认行为(静态库) add_library(mylib src1.cpp src2.cpp) # 方式2:明确指定类型 add_library(mylib STATIC src1.cpp src2.cpp) # 静态库 add_library(mylib SHARED src1.cpp src2.cpp) # 动态库 # 方式3:通过变量控制默认行为 set(BUILD_SHARED_LIBS ON) add_library(mylib src1.cpp src2.cpp) # 将生成动态库
1.2.2 target_link_libraries
1. 基本语法解析
target_link_libraries(test_date # 目标程序,这里也可以是库 mymuduo_base) # 需要链接的库
2.实际使用示例
# 1. 基本用法 add_executable(test_date test_date.cc) target_link_libraries(test_date mymuduo_base) # 2. 链接多个库 target_link_libraries(test_date mymuduo_base pthread rt )
3.更复杂的语法 -目前没有必要深究
# 三种链接属性 target_link_libraries(test_date PRIVATE mymuduo_base # 私有链接:只有test_date使用 PUBLIC other_lib # 公有链接:test_date和链接test_date的目标都能使用 INTERFACE util_lib # 接口链接:只有链接test_date的目标能使用 )
后续遇到再深究。
1.2.3 install安装文件
1.安装库文件
insinstall(TARGETS mymuduo_base #库文件 DESTINATION /usr/local/lib # 安装位置 )
2.安装头文件
# 先选定要安装的头文件 set(base_HEADERS Version.h Types.h copyable.h noncopyable.h StringPiece.h Timestamp.h Date.h TimeZone.h ) install(FILES ${base_HEADERS} # 要安装的头文件 DESTINATION /usr/local/include/mymuduo/base # 安装位置 )
为什么不用lesson2的file命令?
file(GLOB HEADERS "*.h")
是因为有些头文件只是项目内部使用,可以不用对外提供,但file命令通过 *.h 匹配,则会选中所有的头文件。
2 时间类设计
2.1 涉及的时间函数
我来整理一下这些文件中使用的时间相关函数:
2.1.1 gettimeofday获取当前时间,精确到微秒
// 原型 int gettimeofday(struct timeval *tv, struct timezone *tz); // 说明:获取当前时间,精确到微秒 struct timeval { time_t tv_sec; // 秒 suseconds_t tv_usec; // 微秒 }; // 使用示例 struct timeval tv; gettimeofday(&tv, nullptr); int64_t seconds = tv.tv_sec; int64_t microseconds = tv.tv_usec;
2.1.2 gmtime_r将time_t转换为UTC时间的tm结构体
// 原型 struct tm *gmtime_r(const time_t *timep, struct tm *result); // 说明:将time_t转换为UTC时间的tm结构体,线程安全版本 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] int tm_year; // 年 (从1900年开始) int tm_wday; // 星期 [0,6] (星期日=0) int tm_yday; // 一年中的第几天 [0,365] int tm_isdst; // 夏令时标志 }; // 使用示例 time_t seconds = time(nullptr); struct tm tm_time; gmtime_r(&seconds, &tm_time); printf("%d-%02d-%02d %02d:%02d:%02d\n", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
2.1.3mktime将本地时间tm结构体转换为time_t
// 原型 time_t mktime(struct tm *tm); // 说明:将本地时间tm结构体转换为time_t // 使用示例 struct tm local_time = {0}; local_time.tm_year = 2024 - 1900; // 年份从1900开始 local_time.tm_mon = 2 - 1; // 月份从0开始 local_time.tm_mday = 23; // 日期 local_time.tm_hour = 15; // 小时 local_time.tm_min = 30; // 分钟 local_time.tm_sec = 0; // 秒 time_t t = mktime(&local_time);
2.1.4 timegm将UTC时间tm结构体转换为time_t
// 原型 time_t timegm(struct tm *tm); // 说明:将UTC时间tm结构体转换为time_t(与mktime相对) // 使用示例 struct tm utc_time = {0}; utc_time.tm_year = 2024 - 1900; utc_time.tm_mon = 2 - 1; utc_time.tm_mday = 23; utc_time.tm_hour = 7; // UTC时间 utc_time.tm_min = 30; utc_time.tm_sec = 0; time_t t = timegm(&utc_time);
2.1.5 time获取当前时间戳(秒级)
// 原型 time_t time(time_t *tloc); // 说明:获取当前时间戳(秒级) // 使用示例 time_t now = time(nullptr);
2.2 项目中的实际使用
这些函数在muduo网络库代码中的实际应用:
1.获取当前时间戳(微秒级):
这里返回微秒,后续使用时也可以将其转换为毫秒。
// Timestamp::now()的实现 Timestamp Timestamp::now() { struct timeval tv; gettimeofday(&tv, nullptr); int64_t seconds = tv.tv_sec; return Timestamp(seconds * kMicroSecondsPerSecond + tv.tv_usec); }
在EPollPoller::poll,定时器howMuchTimeFromNow时有使用。
2.时间格式化:
// Timestamp::toFormattedString的实现 std::string Timestamp::toFormattedString(bool showMicroseconds) const { char buf[64] = {0}; time_t seconds = static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond); struct tm tm_time; gmtime_r(&seconds, &tm_time); snprintf(buf, sizeof(buf), "%4d%02d%02d %02d:%02d:%02d", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec); return buf; }
SystemInspector::overview打印系统信息时有使用 toFormattedString函数。
3.时区转换:
// TimeZone::toLocalTime的实现 struct tm TimeZone::toLocalTime(time_t seconds) const { struct tm localTime; if (!data_) { gmtime_r(&seconds, &localTime); return localTime; } const Data::LocalTime* local = data_->findLocalTime(seconds); time_t localSeconds = seconds + local->gmtOffset; gmtime_r(&localSeconds, &localTime); return localTime; }
日志格式化时 Logger::Impl::formatTime 有使用这些函数。
这些时间函数的使用需要注意:
- 线程安全性:优先使用 _r 后缀的函数(如gmtime_r)
- 时区处理:注意UTC时间和本地时间的转换
- 精度选择:根据需求选择秒级或微秒级函数
- 性能考虑:gettimeofday比time开销大,但精度更高
3. 整体时间类设计
3.1 时间类组件关系
3.2 时间戳处理流程
3.3 日期转换流程
3.4 时区转换流程
3.5 测试用例执行
3.5.1 Timestamp 测试
# 执行时间戳测试 ./bin/test_timestamp # 测试输出示例 === 测试 Timestamp === 当前时间: 2024-03-14 10:30:45.123456 格式化时间: 2024-03-14 10:30:45 时间差: 10.000000 seconds
3.5.2 Date 测试
# 执行日期测试 ./bin/test_date # 测试输出示例 === 测试 Date === 今天: 2024-03-14 星期: Thursday 儒略日: 2460293
3.5.3 TimeZone 测试
# 执行时区测试 ./bin/test_timezone # 测试输出示例 === 测试 TimeZone === UTC: 2024-03-14 02:30:45 北京时间: 2024-03-14 10:30:45
4 章节总结
重点掌握:
使用add_library生成库文件
- add_library(mymuduo_base ${base_SRCS})
使用target_link_libraries链接目标依赖的库文件
- target_link_libraries(mymuduo_base pthread)
使用install安装库文件和头文件
- install(TARGETS mymuduo_base DESTINATION /usr/local/lib)
- install(FILES ${base_HEADERS} DESTINATION /usr/local/include/mymuduo/base)
了解内容:
- 平时用gettimeofday是最多的,可以精确到微秒, 如果你只需要秒级别的函数则可以使用time(time_t *tloc)获取秒。