【问题标题】:How to convert std::chrono::time_point to calendar datetime string with fractional seconds?如何将 std::chrono::time_point 转换为带有小数秒的日历日期时间字符串?
【发布时间】:2012-10-01 21:10:51
【问题描述】:

如何将std::chrono::time_point 转换为带有小数秒的日历日期时间字符串?

例如:

"10-10-2012 12:38:40.123456"

【问题讨论】:

    标签: c++ datetime c++11 std chrono


    【解决方案1】:

    如果是system_clock,这个类有time_t转换。

    #include <iostream>
    #include <chrono>
    #include <ctime>
    
    using namespace std::chrono;
    
    int main()
    {
      system_clock::time_point p = system_clock::now();
    
      std::time_t t = system_clock::to_time_t(p);
      std::cout << std::ctime(&t) << std::endl; // for example : Tue Sep 27 14:21:13 2011
    }
    

    示例结果:

    Thu Oct 11 19:10:24 2012
    

    编辑: 但是, time_t 不包含小数秒。 另一种方法是使用 time_point::time_since_epoch() 函数。此函数从纪元返回持续时间。 下面的例子是毫秒分辨率的小数。

    #include <iostream>
    #include <chrono>
    #include <ctime>
    
    using namespace std::chrono;
    
    int main()
    {
      high_resolution_clock::time_point p = high_resolution_clock::now();
    
      milliseconds ms = duration_cast<milliseconds>(p.time_since_epoch());
    
      seconds s = duration_cast<seconds>(ms);
      std::time_t t = s.count();
      std::size_t fractional_seconds = ms.count() % 1000;
    
      std::cout << std::ctime(&t) << std::endl;
      std::cout << fractional_seconds << std::endl;
    }
    

    示例结果:

    Thu Oct 11 19:10:24 2012
    
    925
    

    【讨论】:

    【解决方案2】:

    下面的自解释代码首先创建一个对应于 10-10-2012 12:38:40 的 std::tm,将其转换为 std::chrono::system_clock::time_point,添加 0.123456 秒,然后通过转换回std::tm。如何处理小数秒是最后一步。

    #include <iostream>
    #include <chrono>
    #include <ctime>
    
    int main()
    {
        // Create 10-10-2012 12:38:40 UTC as a std::tm
        std::tm tm = {0};
        tm.tm_sec = 40;
        tm.tm_min = 38;
        tm.tm_hour = 12;
        tm.tm_mday = 10;
        tm.tm_mon = 9;
        tm.tm_year = 112;
        tm.tm_isdst = -1;
        // Convert std::tm to std::time_t (popular extension)
        std::time_t tt = timegm(&tm);
        // Convert std::time_t to std::chrono::system_clock::time_point
        std::chrono::system_clock::time_point tp = 
                                         std::chrono::system_clock::from_time_t(tt);
        // Add 0.123456 seconds
        // This will not compile if std::chrono::system_clock::time_point has
        //   courser resolution than microseconds
        tp += std::chrono::microseconds(123456);
        
        // Now output tp
    
        // Convert std::chrono::system_clock::time_point to std::time_t
        tt = std::chrono::system_clock::to_time_t(tp);
        // Convert std::time_t to std::tm (popular extension)
        tm = std::tm{0};
        gmtime_r(&tt, &tm);
        // Output month
        std::cout << tm.tm_mon + 1 << '-';
        // Output day
        std::cout << tm.tm_mday << '-';
        // Output year
        std::cout << tm.tm_year+1900 << ' ';
        // Output hour
        if (tm.tm_hour <= 9)
            std::cout << '0';
        std::cout << tm.tm_hour << ':';
        // Output minute
        if (tm.tm_min <= 9)
            std::cout << '0';
        std::cout << tm.tm_min << ':';
        // Output seconds with fraction
        //   This is the heart of the question/answer.
        //   First create a double-based second
        std::chrono::duration<double> sec = tp - 
                                        std::chrono::system_clock::from_time_t(tt) +
                                        std::chrono::seconds(tm.tm_sec);
        //   Then print out that double using whatever format you prefer.
        if (sec.count() < 10)
            std::cout << '0';
        std::cout << std::fixed << sec.count() << '\n';
    }
    

    对我来说这个输出:

    10-10-2012 12:38:40.123456
    

    您的std::chrono::system_clock::time_point 可能精确到也可能不够精确,无法容纳微秒。

    更新

    更简单的方法是使用this date library。代码简化为(使用 C++14 持续时间文字):

    #include "date.h"
    #include <iostream>
    #include <type_traits>
    
    int
    main()
    {
        using namespace date;
        using namespace std::chrono;
        auto t = sys_days{10_d/10/2012} + 12h + 38min + 40s + 123456us;
        static_assert(std::is_same<decltype(t),
                                   time_point<system_clock, microseconds>>{}, "");
        std::cout << t << '\n';
    }
    

    哪个输出:

    2012-10-10 12:38:40.123456
    

    如果您不需要证明t 的类型是std::chrono::time_point,则可以跳过static_assert

    如果输出不符合您的喜好,例如您真的想要 dd-mm-yyyy 排序,您可以:

    #include "date.h"
    #include <iomanip>
    #include <iostream>
    
    int
    main()
    {
        using namespace date;
        using namespace std::chrono;
        using namespace std;
        auto t = sys_days{10_d/10/2012} + 12h + 38min + 40s + 123456us;
        auto dp = floor<days>(t);
        auto time = make_time(t-dp);
        auto ymd = year_month_day{dp};
        cout.fill('0');
        cout << ymd.day() << '-' << setw(2) << static_cast<unsigned>(ymd.month())
             << '-' << ymd.year() << ' ' << time << '\n';
    }
    

    它给出了所要求的输出:

    10-10-2012 12:38:40.123456
    

    更新

    以下是如何以毫秒精度巧妙地格式化当前时间 UTC:

    #include "date.h"
    #include <iostream>
    
    int
    main()
    {
        using namespace std::chrono;
        std::cout << date::format("%F %T\n", time_point_cast<milliseconds>(system_clock::now()));
    }
    

    这只是为我输出:

    2016-10-17 16:36:02.975
    

    C++17 将允许您将 time_point_cast&lt;milliseconds&gt; 替换为 floor&lt;milliseconds&gt;。在此之前,date::floor"date.h" 中可用。

    std::cout << date::format("%F %T\n", date::floor<milliseconds>(system_clock::now()));
    

    更新 C++20

    在 C++20 中,现在很简单:

    #include <chrono>
    #include <iostream>
    
    int
    main()
    {
        using namespace std::chrono;
        auto t = sys_days{10d/10/2012} + 12h + 38min + 40s + 123456us;
        std::cout << t << '\n';
    }
    

    或者只是:

    std::cout << std::chrono::system_clock::now() << '\n';
    

    std::format 将可用于自定义输出。

    【讨论】:

    【解决方案3】:

    一般来说,您不能以任何直接的方式执行此操作。 time_point 本质上只是来自特定时钟时代的duration

    如果您有std::chrono::system_clock::time_point,那么您可以使用std::chrono::system_clock::to_time_ttime_point 转换为time_t,然后使用ctimestrftime 等普通C 函数对其进行格式化。


    示例代码:

    std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
    std::time_t time = std::chrono::system_clock::to_time_t(tp);
    std::tm timetm = *std::localtime(&time);
    std::cout << "output : " << std::put_time(&timetm, "%c %Z") << "+"
              << std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch()).count() % 1000 << std::endl;
    

    【讨论】:

    • 这个解决方案的问题是 std::localtime 不是线程安全的。
    【解决方案4】:

    这对我来说适用于像 YYYY.MM.DD-HH.MM.SS.fff 这样的格式。试图让这段代码能够接受任何字符串格式就像重新发明轮子一样(即在 Boost 中有所有这些功能。

    std::chrono::system_clock::time_point string_to_time_point(const std::string &str)
    {
        using namespace std;
        using namespace std::chrono;
    
        int yyyy, mm, dd, HH, MM, SS, fff;
    
        char scanf_format[] = "%4d.%2d.%2d-%2d.%2d.%2d.%3d";
    
        sscanf(str.c_str(), scanf_format, &yyyy, &mm, &dd, &HH, &MM, &SS, &fff);
    
        tm ttm = tm();
        ttm.tm_year = yyyy - 1900; // Year since 1900
        ttm.tm_mon = mm - 1; // Month since January 
        ttm.tm_mday = dd; // Day of the month [1-31]
        ttm.tm_hour = HH; // Hour of the day [00-23]
        ttm.tm_min = MM;
        ttm.tm_sec = SS;
    
        time_t ttime_t = mktime(&ttm);
    
        system_clock::time_point time_point_result = std::chrono::system_clock::from_time_t(ttime_t);
    
        time_point_result += std::chrono::milliseconds(fff);
        return time_point_result;
    }
    
    std::string time_point_to_string(std::chrono::system_clock::time_point &tp)
    {
        using namespace std;
        using namespace std::chrono;
    
        auto ttime_t = system_clock::to_time_t(tp);
        auto tp_sec = system_clock::from_time_t(ttime_t);
        milliseconds ms = duration_cast<milliseconds>(tp - tp_sec);
    
        std::tm * ttm = localtime(&ttime_t);
    
        char date_time_format[] = "%Y.%m.%d-%H.%M.%S";
    
        char time_str[] = "yyyy.mm.dd.HH-MM.SS.fff";
    
        strftime(time_str, strlen(time_str), date_time_format, ttm);
    
        string result(time_str);
        result.append(".");
        result.append(to_string(ms.count()));
    
        return result;
    }
    

    【讨论】:

    • 实际上,当只有 1 个 MS 时,您会得到错误的结果,因为 to_String() 转换了 Long (ms.count(()) 。您需要使用 sprintf_s 转换毫秒数。跨度>
    • 我不想要小数秒,所以我把它们去掉了。然后我不得不使用strftime(time_str, strlen(time_str)+1, date_time_format, ttm); 否则,该函数认为最后一个字段没有足够的空间并且没有填充它。我认为这是可以接受的,因为 time_str 实际上有一个额外的空终止符位置。
    • 也许值得一提的是,这只适用于 UTC(不是时区,没有 DST)
    【解决方案5】:

    我会将此放在对已接受答案的评论中,因为那是它所属的地方,但我不能。因此,以防万一有人得到不可靠的结果,这可能就是原因。

    注意接受的答案,如果 time_point 在纪元之前,它会失败。

    这行代码:

    std::size_t fractional_seconds = ms.count() % 1000;
    

    如果 ms.count() 为负数,将产生意外的值(因为 size_t 并不意味着保存负值)。

    【讨论】:

    • 纪元之前的时间?为什么 ? :)
    • 因为世界不是在 1970 年创造的。
    【解决方案6】:

    就我而言,我使用的是线程安全的 chrono 和 c 函数 localtime_r(与 std::localtime 相反)。

    #include <iostream>
    #include <chrono>
    #include <ctime>
    #include <time.h>
    #include <iomanip>
    
    
    int main() {
      std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
      std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
      std::chrono::milliseconds now2 = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
      struct tm currentLocalTime;
      localtime_r(&currentTime, &currentLocalTime);
      char timeBuffer[80];
      std::size_t charCount { std::strftime( timeBuffer, 80,
                                             "%b %d %T",
                                              &currentLocalTime)
                             };
    
      if (charCount == 0) return -1;
    
      std::cout << timeBuffer << "." << std::setfill('0') << std::setw(3) << now2.count() % 1000 << std::endl;
      return 0;
    }
    

    【讨论】:

      【解决方案7】:

      如果您要以 numpy datetime64 的格式格式化 system_clock::time_point,您可以使用:

      std::string format_time_point(system_clock::time_point point)
      {
          static_assert(system_clock::time_point::period::den == 1000000000 && system_clock::time_point::period::num == 1);
          std::string out(29, '0');
          char* buf = &out[0];
          std::time_t now_c = system_clock::to_time_t(point);
          std::strftime(buf, 21, "%Y-%m-%dT%H:%M:%S.", std::localtime(&now_c));
          sprintf(buf+20, "%09ld", point.time_since_epoch().count() % 1000000000);
          return out;
      }
      

      样本输出:2019-11-19T17:59:58.425802666

      【讨论】:

        猜你喜欢
        • 2016-04-23
        • 2018-07-27
        • 1970-01-01
        • 2021-09-28
        • 1970-01-01
        • 2013-01-08
        • 2014-01-28
        • 2020-05-30
        相关资源
        最近更新 更多