Ubuntu20下Boost1.71.0运行中时区变更后本地时间不更新的解决方法
问题:运行时修改系统时区后,Boost本地时间无法自动更新
环境信息
- Boost版本:1.71.0
- 操作系统:Ubuntu 20.04
测试代码
#include <iostream> #include <boost/date_time/c_local_time_adjustor.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <thread> int main(int argc, char const* argv[]) { while (true) { auto utc_now = boost::date_time::second_clock<boost::posix_time::ptime>::universal_time(); auto now = boost::date_time::c_local_adjustor<boost::posix_time::ptime>::utc_to_local(utc_now); std::cout << "now: " << to_simple_string(now) << std::endl; std::this_thread::sleep_for(std::chrono::seconds(10)); } return 0; }
现象描述
运行程序后,在另一终端执行timedatectl修改系统时区:
./program now: 2024-Mar-12 12:30:09 # 另一终端执行:sudo timedatectl set-timezone <新时区> now: 2024-Mar-12 12:30:19 # 本地时间无变化,仍与时区修改前一致
重启程序后,输出的本地时间会自动更新为新时区对应的时间:
./program now: 2024-Mar-12 06:30:25 # 新时区对应的本地时间
请问如何让程序在运行状态下自动感知时区变化并更新本地时间?
解决方案
问题根源在于c_local_adjustor会在初次调用时缓存系统时区信息,后续不会主动重新读取。要实现运行时动态感知时区变化,可采用以下两种方案:
1. 每次转换前手动重置时区缓存
Boost.Date_Time底层依赖POSIX标准的tzset()函数,该函数会重新读取系统时区配置文件(Ubuntu下为/etc/localtime)并重置进程内的时区缓存。修改代码,在每次转换UTC到本地时间前调用该函数:
#include <iostream> #include <boost/date_time/posix_time/posix_time.hpp> #include <thread> #include <ctime> int main(int argc, char const* argv[]) { while (true) { auto utc_now = boost::date_time::second_clock<boost::posix_time::ptime>::universal_time(); // 重置时区缓存 tzset(); // 使用to_local_time替代c_local_adjustor,依赖更新后的时区信息转换 auto now = boost::posix_time::to_local_time(utc_now); std::cout << "now: " << boost::posix_time::to_simple_string(now) << std::endl; std::this_thread::sleep_for(std::chrono::seconds(10)); } return 0; }
2. 监听时区配置文件变化(高效方案)
Ubuntu修改时区会触发/etc/localtime软链接的更新,可通过inotify API监听该文件的变化,仅在实际时区变更时重置缓存,避免循环调用tzset()的开销:
#include <sys/inotify.h> #include <unistd.h> #include <iostream> #include <boost/date_time/posix_time/posix_time.hpp> #include <thread> #include <ctime> void watch_timezone_change() { int fd = inotify_init(); if (fd == -1) { perror("inotify_init"); return; } int wd = inotify_add_watch(fd, "/etc/localtime", IN_MODIFY | IN_ATTRIB | IN_DELETE_SELF); if (wd == -1) { perror("inotify_add_watch"); close(fd); return; } char buf[4096]; while (true) { int len = read(fd, buf, sizeof(buf)); if (len == -1) { perror("read"); break; } // 检测到时区文件变化,重置缓存 tzset(); std::cout << "时区已更新,缓存已重置" << std::endl; } inotify_rm_watch(fd, wd); close(fd); } int main(int argc, char const* argv[]) { // 启动单独线程监听时区变化 std::thread timezone_watcher(watch_timezone_change); timezone_watcher.detach(); while (true) { auto utc_now = boost::date_time::second_clock<boost::posix_time::ptime>::universal_time(); auto now = boost::posix_time::to_local_time(utc_now); std::cout << "now: " << boost::posix_time::to_simple_string(now) << std::endl; std::this_thread::sleep_for(std::chrono::seconds(10)); } return 0; }
编译时需链接pthread库:
g++ -o program program.cpp -lboost_date_time -lpthread
注意事项
tzset()是POSIX标准接口,仅在Linux/Unix系统有效,Windows需使用SetTimeZoneInformation等对应API。- Boost 1.71.0版本中
c_local_adjustor的缓存无公开清除接口,因此使用to_local_time配合tzset()是更稳妥的方案。
内容的提问来源于stack exchange,提问作者ibse
相关产品推荐
相关产品推荐

