You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将SVN日志中带微秒的时间字符串转为std::chrono对象(非C++20)

不使用C++20解析带微秒的ISO时间到std::chrono对象

要处理带微秒的YYYY-MM-DDTHH:MM:SS.ffffffZ格式时间,且保留微秒精度,你可以手动拆分时间字符串,分别解析秒级时间部分和微秒部分,再合并为std::chrono时间点。以下是具体实现:

核心思路

  1. 把时间字符串按.分割,分离出「日期时间(到秒)」和「微秒+时区标识Z」两部分;
  2. 用std::get_time解析日期时间部分到std::tm,再通过timegm(UTC时区转换)转成std::chrono::system_clock::time_point;
  3. 解析微秒部分为整数,转换成std::chrono::microseconds后加到时间点上;
  4. 若需要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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 21:44:54