Search code examples
c++timerc++-chrono

How to format steady_clock time into HH:MM:SS.Milliseconds using <chrono>from c++ stdl?


I'm currently making a small game for my C++ programming class and the professor requires us to have a timer for the game. We haven't talked at all about how to use timers and any std timer libraries so we're on our own. I found the std library and have tried to implement a simple timer for the game and have managed to do so but I can't seem to figure out how to format the time from it to a more user friendly version like HH:MM:SS.Millisecons. All I have is the raw time since I started the steady_clock till I ended it and I can display that in seconds, milliseconds, minutes, whatever, but that doesn't look as good as I'd like to. I found some solutioms but they are way too hard for me to even deconstruct and try to apply. Any simple way to do what I want? Part of my code where I implement the timer:

        // Initialize game timer using <chrono>
        chrono::steady_clock::time_point start = chrono::steady_clock::now();

        // While game is running (player alive and enemy robot lefts) update map
        while (!GameEnd){
            show_maze(maze_map);
            cout << endl;
            playerMove(x, y, GameEnd, died, maze_map);
        }
        // Terminate game timer and calculate time elapsed
        chrono::steady_clock::time_point end = chrono::steady_clock::now();
        chrono::steady_clock::duration time_elapsed = end - start;

        // Show last map state before either player died or no more robots left
        show_maze(maze_map);
        
        // Boo / congratulate player for his performance on the game
        cout << "Game Over! You " << (died ? "died by hitting a fence/robot :(" : "won because all the robots died. Congratulations!") << endl;
        cout << "Your game lasted for " << chrono::duration_cast<chrono::milliseconds>(time_elapsed).count() << " milliseconds.\n\n";
    

Solution

  • You can get the number of hours out of time_elapsed with:

    auto h = chrono::duration_cast<chrono::hours>(time_elapsed);
    

    Then you can subtract the number of hours so that time_elapsed only holds a duration less than an hour:

    time_elapsed -= h;
    

    Then you can get the number of minutes out of time_elapsed:

    auto m = chrono::duration_cast<chrono::minutes>(time_elapsed);
    

    and subtract the minutes out...

    time_elapsed -= m;
    

    Now time_elapsed holds a duration less than a minute. You can continue with this pattern down to whatever granularity you desire.

    C++20 has a type called std::chrono::hh_mm_ss that does precisely this operation as a convenience:

    chrono::hh_mm_ss hms{time_elapsed};
    

    hh_mm_ss has hours(), minutes(), seconds() and subseconds() getters which return the respective chrono units. To the best of my knowledge, no vendor is shipping this yet, but you can get a preview of this part of C++20, which works with C++11/14/17 here.

    #include "date/date.h"
    #include <chrono>
    #include <iostream>
    
    int
    main()
    {
        using namespace std;
        chrono::steady_clock::time_point start = chrono::steady_clock::now();
        // ...
        chrono::steady_clock::time_point end = chrono::steady_clock::now();
        chrono::steady_clock::duration time_elapsed = end - start;
        cout << date::hh_mm_ss{chrono::duration_cast<chrono::milliseconds>(time_elapsed)} << '\n';
    }
    

    Output:

    00:00:00.000