如何将SVN日志中带微秒的时间字符串转为std::chrono对象(非C++20)
不使用C++20解析带微秒的ISO时间到std::chrono对象
要处理带微秒的YYYY-MM-DDTHH:MM:SS.ffffffZ格式时间,且保留微秒精度,你可以手动拆分时间字符串,分别解析秒级时间部分和微秒部分,再合并为std::chrono时间点。以下是具体实现:
核心思路
- 把时间字符串按
.分割,分离出「日期时间(到秒)」和「微秒+时区标识Z」两部分; - 用
std::get_time解析日期时间部分到std::tm,再通过timegm(UTC时区转换)转成std::chrono::system_clock::time_point; - 解析微秒部分为整数,转换成
std::chrono::microseconds后加到时间点上; - 若需要
steady_clock的时间点,需通过system_clock和steady_clock的当前偏移来转换(注意:steady_clock是单调时钟,无固定 epoch,无法直接从日历时间生成)。
代码实现
#include <iostream> #include <sstream> #include <chrono> #include <ctime> #include <string> #include <stdexcept> // 解析ISO 8601 UTC时间(带微秒)到system_clock::time_point std::chrono::system_clock::time_point parse_iso8601(const std::string& time_str) { // 拆分秒和微秒部分 size_t dot_pos = time_str.find('.'); if (dot_pos == std::string::npos) throw std::invalid_argument("Missing microseconds part"); std::string date_time_part = time_str.substr(0, dot_pos); std::string microsec_part = time_str.substr(dot_pos + 1); // 去掉末尾的Z if (!microsec_part.empty() && microsec_part.back() == 'Z') microsec_part.pop_back(); // 确保微秒是6位,不足补0,超过截断 if (microsec_part.size() > 6) microsec_part = microsec_part.substr(0, 6); else if (microsec_part.size() < 6) microsec_part.append(6 - microsec_part.size(), '0'); // 解析日期时间部分到std::tm(UTC) std::tm t = {}; std::istringstream ss(date_time_part + "Z"); // 加Z让格式匹配%Y-%m-%dT%H:%M:%SZ ss >> std::get_time(&t, "%Y-%m-%dT%H:%M:%SZ"); if (ss.fail()) throw std::invalid_argument("Failed to parse date time part"); // 转成time_t(UTC时区,用timegm而非mktime,mktime是本地时区) std::time_t tt = timegm(&t); if (tt == -1) throw std::invalid_argument("Invalid date time"); // 转成system_clock::time_point std::chrono::system_clock::time_point tp = std::chrono::system_clock::from_time_t(tt); // 添加微秒 long long microsec = std::stoll(microsec_part); tp += std::chrono::microseconds(microsec); return tp; } // 可选:转换system_clock时间点到steady_clock时间点 std::chrono::steady_clock::time_point system_to_steady(const std::chrono::system_clock::time_point& system_tp) { // 记录两个时钟的当前偏移 auto system_now = std::chrono::system_clock::now(); auto steady_now = std::chrono::steady_clock::now(); auto offset = system_tp - system_now; return steady_now + offset; } int main() { try { std::string svn_time = "2023-09-23T23:08:30.383815Z"; auto system_tp = parse_iso8601(svn_time); // 输出验证:转成时间字符串 std::time_t tt = std::chrono::system_clock::to_time_t(system_tp); std::cout << "UTC时间:" << std::ctime(&tt); // 输出微秒部分 auto microsec = std::chrono::duration_cast<std::chrono::microseconds>(system_tp.time_since_epoch()) % 1000000; std::cout << "微秒:" << microsec.count() << "\n"; // 转成steady_clock时间点(仅当需要单调时钟时使用) auto steady_tp = system_to_steady(system_tp); // steady_clock无法直接转成日历时间,可用于时间间隔计算 } catch (const std::exception& e) { std::cerr << "解析失败:" << e.what() << "\n"; } return 0; }
关键细节说明
- 时区处理:必须用
timegm而非mktime,因为mktime会把std::tm当作本地时区解析,而SVN日志的时间是UTC(带Z标识),timegm直接按UTC转换为time_t。 - 微秒处理:拆分后要确保微秒是6位,不足补0、超过截断,保证精度正确。
- steady_clock的限制:
steady_clock是单调递增时钟,没有固定的起始时间(epoch),因此不能直接从日历时间生成。如果需要使用steady_clock,只能通过记录当前system_clock和steady_clock的偏移来间接转换,这种转换仅在短时间内有效(避免系统时间调整导致偏移失效)。
内容的提问来源于stack exchange,提问作者jpo38
相关产品推荐
相关产品推荐

