Search code examples
c++datec++11c++-chrono

How can hours, minutes, and seconds be extracted from a time_point in milliseconds?


How can I extract hours, minutes, and seconds from a given std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>?

My code in below does not return the hour correctly. It returns a vary large value for the hour (e.g., Hours: 472057, Minutes: 3).

std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();

auto hours = std::chrono::duration_cast<std::chrono::hours>(tp.time_since_epoch()).count();
auto minutes = std::chrono::duration_cast<std::chrono::minutes>(tp.time_since_epoch()).count() % 60;

// Print the hours and minutes
std::cout << "Hours: " << hours << std::endl;
std::cout << "Minutes: " << minutes << std::endl;

Solution

  • As you've specified C++11, and removed the part about using Howard Hinnant's date lib, you'll have to re-create that part of the date lib that exists only as 3rd party lib until C++20. This isn't too much trouble if you are content with UTC. Getting local time is more effort:

    #include <chrono>
    #include <iostream>
    
    int
    main()
    {
        // This is a date_time in UTC
        std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
    
        // This is a date in UTC
        auto tp_days = std::chrono::time_point_cast<std::chrono::duration<int, std::ratio<86400>>>(tp);
    
        // This is a time in UTC
        auto tod = tp - tp_days;
    
        // This is how to separate a duration into {h, m, s}
        auto h = std::chrono::duration_cast<std::chrono::hours>(tod);
        tod -= h;
        auto m = std::chrono::duration_cast<std::chrono::minutes>(tod);
        tod -= m;
        auto s = std::chrono::duration_cast<std::chrono::seconds>(tod);
    
        std::cout << "Hours: " << h.count() << '\n';
        std::cout << "Minutes: " << m.count() << '\n';
        std::cout << "Seconds: " << s.count() << '\n';
    }
    

    Note that this only works correctly for system_clock time_points after 1970-01-01T00:00:00 UTC. This is because time_point_cast truncates towards zero. C++17 introduces floor which truncates towards negative infinity, and should be preferred as it works both before and after the epoch.

    To get local time you'll need to figure out your current UTC offset and add that to your time_point prior to splitting it up into a date and time (prior to truncating to days precision).