std::format的%z说明符对local_time无效,MSVC编译报错求解
我需要生成格式为YYYY-MM-DD hh:mm:ss.fff +zz的时间戳,原本打算用std::format的%z说明符实现,但在MSVC中触发编译错误:
error C7595: 'std::basic_format_string<char,const std::chrono::time_point<std::chrono::local_t,std::chrono::durationstd::chrono::system_clock::rep,std::chrono::system_clock::period> &>::basic_format_string': call to immediate function is not a constant expression
对应的测试代码如下:
#include <chrono> #include <format> #include <iostream> std::string get_current_time_and_date() { auto const time = std::chrono::current_zone()->to_local(std::chrono::system_clock::now()); return std::format("{:%F %T %z}", time); } int main() { std::cout << get_current_time_and_date() << "\n"; return 0; }
仅使用"{:%F %T}"作为格式字符串时可正常运行,但无法包含时区信息。请问:
- 有没有解决办法?
- 这是编译器的Bug吗?
附:使用strftime的替代实现方案:
#include <chrono> #include <format> #include <iostream> #include <sstream> #include <iomanip> std::string get_current_time_and_date() { auto time = std::chrono::system_clock::now(); auto ms = std::chrono::duration_cast<std::chrono::milliseconds>( time.time_since_epoch()) % 1000; auto localT = std::chrono::system_clock::to_time_t(time); std::tm timeStruct; localtime_s(&timeStruct, &localT); std::ostringstream oss; const size_t TIME_STR_LEN = 32; char buf[TIME_STR_LEN]; std::strftime(buf, TIME_STR_LEN, "%F %T", &timeStruct); char timezonebuf[16]; std::strftime(timezonebuf, sizeof(timezonebuf), " %z", &timeStruct); oss << buf << '.' << std::setfill('0') << std::setw(3) << ms.count() << timezonebuf; return oss.str(); }
这是MSVC的编译器Bug
该错误属于MSVC对C++20 std::format处理chrono::local_t类型时的实现缺陷,目前在Visual Studio 2022 17.6及以上版本中已修复,升级编译器版本即可直接解决问题。
现有环境下的解决办法
如果暂时无法升级编译器,除了已提供的strftime方案,还有两种更简洁的替代方式:
拆分格式化逻辑,单独处理时区
将日期时间与时区的格式化分开,避免在同一std::format调用中使用%z:std::string get_current_time_and_date() { auto const sys_time = std::chrono::system_clock::now(); auto const local_time = std::chrono::current_zone()->to_local(sys_time); // 格式化日期时间部分 auto dt_str = std::format("{:%F %T}", local_time); // 计算并格式化时区偏移 auto offset = std::chrono::current_zone()->get_info(sys_time).offset; auto offset_min = std::chrono::duration_cast<std::chrono::minutes>(offset).count(); auto sign = offset_min >= 0 ? "+" : "-"; auto hours = std::abs(offset_min) / 60; auto mins = std::abs(offset_min) % 60; // 拼接完整字符串 return std::format("{} {}{:02}{:02}", dt_str, sign, hours, mins); }使用zoned_time替代local_t
std::chrono::zoned_time可以直接被std::format正确格式化,避开local_t相关的实现问题:std::string get_current_time_and_date() { std::chrono::zoned_time zt{std::chrono::current_zone(), std::chrono::system_clock::now()}; return std::format("{:%F %T %z}", zt); }该方法无需拆分逻辑,代码简洁且能直接生成符合要求的时间戳。
内容的提问来源于stack exchange,提问作者MHebes

