Extract year/month/day etc. from std::chrono::time_point in C++

后端 未结 1 1290
有刺的猬
有刺的猬 2020-11-27 12:03

How can I extract the year, month, day, hour, minute, second and millisecond from an std::chrono::time_point object?

I only saw example

相关标签:
1条回答
  • 2020-11-27 12:32

    You can only extract this information from a system_clock::time_point. This is the only system-supplied clock that has a relationship with the civil calendar. Here is how to get the current time_point using this clock:

     system_clock::time_point now = system_clock::now();
    

    You can then convert this to a time_t with:

    time_t tt = system_clock::to_time_t(now);
    

    Using the C library you can then convert a time_t to a tm, but you must choose whether you want that conversion to happen in the UTC timezone, or you local timezone:

    tm utc_tm = *gmtime(&tt);
    tm local_tm = *localtime(&tt);
    

    Then you can print out the components of the tm, for example:

    std::cout << local_tm.tm_year + 1900 << '\n';
    std::cout << local_tm.tm_mon + 1 << '\n';
    std::cout << local_tm.tm_mday << '\n';
    

    Additionally

    If you want, you can take advantage of this non-guaranteed information:

    Every implementation of system_clock I'm aware of is based on unix time. I.e. the number of seconds since New Years 1970 UTC, neglecting leap seconds. And the precision of this count is usually finer than seconds. Here is a complete program which extracts all of this information:

    #include <chrono>
    #include <ctime>
    #include <iostream>
    
    int
    main()
    {
        using namespace std;
        using namespace std::chrono;
        typedef duration<int, ratio_multiply<hours::period, ratio<24> >::type> days;
        system_clock::time_point now = system_clock::now();
        system_clock::duration tp = now.time_since_epoch();
        days d = duration_cast<days>(tp);
        tp -= d;
        hours h = duration_cast<hours>(tp);
        tp -= h;
        minutes m = duration_cast<minutes>(tp);
        tp -= m;
        seconds s = duration_cast<seconds>(tp);
        tp -= s;
        std::cout << d.count() << "d " << h.count() << ':'
                  << m.count() << ':' << s.count();
        std::cout << " " << tp.count() << "["
                  << system_clock::duration::period::num << '/'
                  << system_clock::duration::period::den << "]\n";
    
        time_t tt = system_clock::to_time_t(now);
        tm utc_tm = *gmtime(&tt);
        tm local_tm = *localtime(&tt);
        std::cout << utc_tm.tm_year + 1900 << '-';
        std::cout << utc_tm.tm_mon + 1 << '-';
        std::cout << utc_tm.tm_mday << ' ';
        std::cout << utc_tm.tm_hour << ':';
        std::cout << utc_tm.tm_min << ':';
        std::cout << utc_tm.tm_sec << '\n';
    }
    

    It is handy to create a custom duration to model days:

    typedef duration<int, ratio_multiply<hours::period, ratio<24> >::type> days;
    

    Now you can get the time since the epoch, to as fine a precision as it can manage, with:

    system_clock::duration tp = now.time_since_epoch();
    

    Then truncate it to days, and subtract that off.

    Then truncate it to hours, and subtract that off.

    Continue until you've subtracted off the seconds.

    What you're left with is the fraction of a second with the units of system_clock::duration. So print out that run time value and the compile time units of that value as shown.

    For me this program prints out:

    15806d 20:31:14 598155[1/1000000]
    2013-4-11 20:31:14
    

    My output indicates the system_clock::duration precision is microseconds. If desired, that can be truncated to milliseconds with:

    milliseconds ms = duration_cast<milliseconds>(tp);
    

    Update

    This header-only C++11/14 library encapsulates the work above, reducing client work down to:

    #include "date.h"
    #include <iostream>
    
    int
    main()
    {
        // Reduce verbosity but let you know what is in what namespace
        namespace C = std::chrono;
        namespace D = date;
        namespace S = std;
    
        auto tp = C::system_clock::now(); // tp is a C::system_clock::time_point
        {
            // Need to reach into namespace date for this streaming operator
            using namespace date;
            S::cout << tp << '\n';
        }
        auto dp = D::floor<D::days>(tp);  // dp is a sys_days, which is a
                                          // type alias for a C::time_point
        auto ymd = D::year_month_day{dp};
        auto time = D::make_time(C::duration_cast<C::milliseconds>(tp-dp));
        S::cout << "year        = " << ymd.year() << '\n';
        S::cout << "month       = " << ymd.month() << '\n';
        S::cout << "day         = " << ymd.day() << '\n';
        S::cout << "hour        = " << time.hours().count() << "h\n";
        S::cout << "minute      = " << time.minutes().count() << "min\n";
        S::cout << "second      = " << time.seconds().count() << "s\n";
        S::cout << "millisecond = " << time.subseconds().count() << "ms\n";
    }
    

    Which just output for me:

    2015-07-10 20:10:36.023017
    year        = 2015
    month       = Jul
    day         = 10
    hour        = 20h
    minute      = 10min
    second      = 36s
    millisecond = 23ms
    

    Another Update

    This library grew into a C++ standards proposal and is now in the C++20 working draft. The syntax for extracting these fields from a system_clock::time_point in C++20 will be:

    #include <chrono>
    
    int
    main()
    {
        using namespace std::chrono;
        auto tp = system_clock::now();
        auto dp = floor<days>(tp);
        year_month_day ymd{dp};
        hh_mm_ss time{floor<milliseconds>(tp-dp)};
        auto y = ymd.year();
        auto m = ymd.month();
        auto d = ymd.day();
        auto h = time.hours();
        auto M = time.minutes();
        auto s = time.seconds();
        auto ms = time.subseconds();
    }
    

    The above assumes you want these fields in UTC. If you prefer them in some other time zone, that will also be possible. For example, here is how to do it in your computer's current local time zone:

    #include <chrono>
    
    int
    main()
    {
        using namespace std::chrono;
        auto tp = zoned_time{current_zone(), system_clock::now()}.get_local_time();
        auto dp = floor<days>(tp);
        year_month_day ymd{dp};
        hh_mm_ss time{floor<milliseconds>(tp-dp)};
        auto y = ymd.year();
        auto m = ymd.month();
        auto d = ymd.day();
        auto h = time.hours();
        auto M = time.minutes();
        auto s = time.seconds();
        auto ms = time.subseconds();
    }
    

    The only difference above is the construction of tp which now has type local_time as opposed to sys_time in the UTC example. Alternatively one could have picked an arbitrary time zone with this small change:

    auto tp = zoned_time{"Europe/London", system_clock::now()}.get_local_time();
    
    0 讨论(0)
提交回复
热议问题