7

I'm looking for a way to print out milliseconds in this format using C++:

cout << hours << " Hours : " << minutes << " Minutes : " << seconds << " Seconds : " << milliseconds << " Milliseconds" << endl;

I know there are a ton of duplicate questions about this. But none of them really handle how to get the remainder in milliseconds. There are a few that do this using Java, but I want a solution in C++.

Edit:

I wanted to clarify the question. I'm looking to take a time value that I get for the time it takes a program to run and print out that time in a legible format for the user. Getting the standard hr:min:sec was straight forward. But including any remaining milliseconds was tripping me up.

Berkyjay
  • 158
  • 1
  • 1
  • 10
  • So your trouble is getting the milliseconds? Would this be a duplicate then? https://stackoverflow.com/q/19555121/2602718 – scohe001 Jun 06 '18 at 18:36
  • All of the ton of duplicates will exhibit a pattern to get from bigger to lower and lower units. Extrapolate and apply to your question. – Yunnosch Jun 06 '18 at 18:36
  • Could you link a duplicate and explain why it's not applicable? It's not clear what your special requirements are. – François Andrieux Jun 06 '18 at 18:36
  • 1
    Hint: 1000ms = 1s; 60s = 1min; 60min = 1h. The rest is math and it shouldn't matter whether you only have an example in Java (your c++ line seems valid so far) – Tom Mekken Jun 06 '18 at 18:47

8 Answers8

18
std::string format_duration( std::chrono::milliseconds ms ) {
    using namespace std::chrono;
    auto secs = duration_cast<seconds>(ms);
    ms -= duration_cast<milliseconds>(secs);
    auto mins = duration_cast<minutes>(secs);
    secs -= duration_cast<seconds>(mins);
    auto hour = duration_cast<hours>(mins);
    mins -= duration_cast<minutes>(hour);

    std::stringstream ss;
    ss << hour.count() << " Hours : " << mins.count() << " Minutes : " << secs.count() << " Seconds : " << ms.count() << " Milliseconds";
    return ss.str();
}

live example.

Extending this to days/years/etc should be easy (there isn't a predefined std::chrono duration type for days/years/etc prior to however).

But I can do better.

template<class Duration>
struct split_duration {
  Duration d;
  std::chrono::milliseconds leftover;

  split_duration( std::chrono::milliseconds ms ):
    d( std::chrono::duration_cast<Duration>(ms) ),
    leftover( ms - std::chrono::duration_cast<std::chrono::milliseconds>(d) )
  {}
};


template<class...Durations>
std::tuple<Durations...> durations( std::chrono::milliseconds ms ) {
  std::tuple<std::optional<split_duration<Durations>>...> tmp;
  ( (void)(
       (void)std::get<std::optional<split_duration<Durations>>>(tmp).emplace( ms ),
       ms = std::get<std::optional<split_duration<Durations>>>(tmp)->leftover
     ), ...
  );
  return std::make_tuple( std::get<std::optional<split_duration<Durations>>>( tmp )->d... );
}

template<class T>
struct tag_t {};
template<class T>
constexpr tag_t<T> tag = {};

inline std::string duration_name( tag_t<std::chrono::milliseconds> ) { return "ms"; }
inline std::string duration_name( tag_t<std::chrono::seconds> ) { return "Seconds"; }
inline std::string duration_name( tag_t<std::chrono::minutes> ) { return "Minutes"; }
inline std::string duration_name( tag_t<std::chrono::hours> ) { return "Hours"; }
// inline std::string duration_name( tag_t<std::chrono::days> ) { return "Days"; }
// inline std::string duration_name( tag_t<std::chrono::years> ) { return "Years"; }

template<class...Durations>
std::string format_duration( std::chrono::milliseconds ms ) {
    auto split = durations<Durations...>(ms);

    std::stringstream ss;

    (
        (void)( ss << duration_name(tag<Durations>) << ": " << std::get<Durations>(split).count() << " " ), ...
    );

    return ss.str();
}

Days/Years requires , everything else is .

You just call format_durations<Durations...>( some_ms ) and out comes a formatted string based off the Durations.... You do have to do it from most-to-least significant.

durations<Durations...> gives you a tuple breakdown of the time that has to be most-to-least; you could then reorder that before formatting if you chose.

Duplicate duration types leads to compile time errors, as std::get dies a horrible ambiguous death.

Live example.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
8
int milliseconds = ...;

int seconds = milliseconds / 1000;
milliseconds %= 1000;

int minutes = seconds / 60;
seconds %= 60;

int hours = minutes / 60;
minutes %= 60;

cout << hours << " Hours : " << minutes << " Minutes : " << seconds << " Seconds : " << milliseconds << " Milliseconds" << endl;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I don't think this is correct because seconds will be ALL the ms converted to seconds, not the reminding seconds in a time format such as HH:MM:SS.mmm (how many hours, minutes, seconds and milliseconds are this tick counter). – Max Kielland Aug 23 '21 at 11:39
  • @MaxKielland did you even try it? [It works just fine](https://ideone.com/8mZgHR). Perhaps you don't understand how the `%=` operator works? `milliseconds` is converted to whole `seconds` and then the remainder is saved back to `milliseconds`. Then `seconds` is converted to whole `minutes` and the remainder is saved back to `seconds`. Then `minutes` is converted to whole `hours` and the remainder is saved back to `minutes`. – Remy Lebeau Aug 23 '21 at 14:00
  • Yes I do understand modulo (%), I just missed that line (for some unknown reason). – Max Kielland Aug 25 '21 at 08:15
7

Maybe you're looking for something like this:

#include <iostream>
using namespace std;

int main() {
//Value chosen to be 1 hour, 1 minute, 1 second, and 1 millisecond
long milli = 3661001;
//3600000 milliseconds in an hour
long hr = milli / 3600000;
milli = milli - 3600000 * hr;
//60000 milliseconds in a minute
long min = milli / 60000;
milli = milli - 60000 * min;

//1000 milliseconds in a second
long sec = milli / 1000;
milli = milli - 1000 * sec;


cout << hr << " hours and " << min << " minutes and " << sec << " seconds and " << milli << " milliseconds." << endl;
}
Jsqsh
  • 120
  • 1
  • 7
1
// Online IDE - Code Editor, Compiler, Interpreter

#include <stdio.h>
#include <stdint.h>

uint32_t DAYS_IN_MILLISECONDS = 86400000;
uint32_t HOURS_IN_MILLISECONDS = 3600000;
uint16_t MINUTES_IN_MILLISECONDS = 60000;
uint16_t SECONDS_IN_MILLISECONDS = 1000;

int main()
{
    uint32_t total_milliseconds = 23*60*60000 + 100 + 1000;
    uint8_t days = total_milliseconds / DAYS_IN_MILLISECONDS;
    uint8_t hours = (total_milliseconds - days*DAYS_IN_MILLISECONDS) / HOURS_IN_MILLISECONDS;
    uint8_t minutes = (total_milliseconds - days*DAYS_IN_MILLISECONDS - hours*HOURS_IN_MILLISECONDS) / MINUTES_IN_MILLISECONDS;
    uint8_t seconds = (total_milliseconds - days*DAYS_IN_MILLISECONDS - hours*HOURS_IN_MILLISECONDS - minutes*MINUTES_IN_MILLISECONDS) / SECONDS_IN_MILLISECONDS;
    uint8_t milliseconds = total_milliseconds - days*DAYS_IN_MILLISECONDS - hours*HOURS_IN_MILLISECONDS - minutes*MINUTES_IN_MILLISECONDS - seconds*SECONDS_IN_MILLISECONDS;
    printf("%i:days %i:hours %i:minutes %i:seconds %i:milliseconds", days, hours, minutes, seconds, milliseconds);
    return 0;
}
euraad
  • 2,467
  • 5
  • 30
  • 51
0

Not exactly what you're looking for, but if you're only accessing the current time, this will work:

#include <chrono>

auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch());
const auto seconds = std::chrono::duration_cast<std::chrono::seconds>(ms);
ms -= seconds;

You can then add ms.count() to your cout, even if it isn't the actual remainder of the time you originally look at.

Edit: This method should be faster than using a modulus.

L. Kue
  • 473
  • 5
  • 19
0
#ifndef DATETIME_H_
#define DATETIME_H_

/* Useful Constants */
#define SECS_PER_MIN  (60UL)
#define SECS_PER_HOUR (3600UL)
#define SECS_PER_DAY  (SECS_PER_HOUR * 24L)
#define DAYS_PER_WEEK (7L)
#define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK)
#define SECS_PER_YEAR (SECS_PER_WEEK * 52L)
#define SECS_YR_2000  (946681200UL)

/* Useful Macros for getting elapsed time */
/** Get just seconds part of given Unix time */
#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN)
/** Get just minutes part of given Unix time */
#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN)
/** Get just hours part of given Unix time */
#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR)
/** Get day of week from given Unix time */
#define dayOfWeek(_time_)  (( _time_ / SECS_PER_DAY + 4)  % DAYS_PER_WEEK) // 0 = Sunday
/** Get elapsed days since 1970-01-01 from given Unix time */
#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY)  // this is number of days since Jan 1 1970
/** Get quantity of seconds since midnight from given Unix time */
#define elapsedSecsToday(_time_)  (_time_ % SECS_PER_DAY)   // the number of seconds since last midnight
/** Get Unix time of midnight at start of day from given Unix time */
#define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY)  // time at the start of the given day
/** Get Unix time of midnight at end of day from given just Unix time */
#define nextMidnight(_time_) ( previousMidnight(_time_)  + SECS_PER_DAY ) // time at the end of the given day
/** Get quantity of seconds since midnight at start of previous Sunday from given Unix time */
#define elapsedSecsThisWeek(_time_)  (elapsedSecsToday(_time_) +  (dayOfWeek(_time_) * SECS_PER_DAY) )



#endif /* DATETIME_H_ */

Sample Code :

unsigned long long s = 1 * 10 ;
unsigned long long m = 1 * 60 * 20;
unsigned long long h = 1 * 60 * 60 * 3;
unsigned long long d = 1 * 60 * 60 * 24 * 60;
unsigned long long M = 1 * 60 * 60 * 24 * 30 * 5;
unsigned long long y = 1 * 60 * 60 * 24 * 30 * 12 * 6;

//unsigned long long timeInSec = s + m + h + d + M + y;
unsigned long long timeInSec = s + m + h + d;

long long seconds = numberOfSeconds(timeInSec);
long long minutes = numberOfMinutes(timeInSec);
long long hours = numberOfHours(timeInSec);
long long day = dayOfWeek(timeInSec);
long long _elapsedDays = elapsedDays(timeInSec);
long long _elapsedSecsToday = elapsedSecsToday(timeInSec);
0

Not that I do not find interesting the other solutions, and it is my humble monkey way of working... always very near of the assembly code ... in the previous solutions, there is way too many variables created to achieve the goal.. in my exemple, only 2 variables are created, a string: result, an int8_t: n, plus a parameter in the function declaration, so the milliseconds are passed to the function using a INT: value

#include <string>  
using namespace std;


// milliseconds to DDD HH:MM:SS.mmm , days are displayed if > 0   

string ToDDHHMMSSmmm(int value) 
{
  string result=""; uint8_t n=value/86400000;
  if (n>0) {result=to_string(n)+' ';}
  value-=86400000*n; n=value/3600000; if(n<10) result+='0';
  result+=to_string(n); value-=3600000*n;
  n=value/60000; result+=':'; if(n<10) result+='0';
  result+=to_string(n); value-=60000*n;
  n=value/1000; result+=':'; if(n<10) result+='0';
  result+=to_string(n); value-=1000*n; 
  result+='.'; if(value<100) result+='0'; 
  if(value<10) result+='0'; 
  result+=to_string(value);
  return result;
}

1- a string result is created empty

2- a byte (8 bit int) is created(n) as the milliseconds passed to the int parameter named value is divided by 86400000 (representing the quantity of millisec per day)

3- if n is greater than 0, the numbers of days(max 255 days !) is added to the string result.

4- using the same variables, n(the previously calculated days) will be multiplied by the same number, and the result is subtracted from the variable value. now n is set to the value divided by 3600000.

5- if n<10, we have only one digit for the hours, so add a char '0' to the string result.

6- and convert + add to the string the value of n.

7- same principle for the minutes, the seconds, and the last part the 3 digit milliseconds.

8- the string is built as the calculation are done, and the return string is always in the perfect format (DDD) HH:MM:SS.mmm

The bytes used are as follow int32 for the value 4 int8 for the work 1 String 13 to 16 max (if days > 0)

...for a total of 21 Bytes, the large numbers are never stored in variables, they remain in the code and are calculated in the register of the cpu or mcu only. To be even nearer of ASSEMBLY, I should not used std::string and std::to_string, instead, pointers and using 0x20 for spaces, 0x3a for semi colons, and adding 0x30 to any digit values to create the ASCII directly in memory.

  • interesting, but the asker didn't put any emphasis on being "close to assembly", and the format of your output doesn't even strictly match that requested. – starball Sep 02 '22 at 18:49
-1

Two types of output

#include <iostream>
using namespace std;

int main() {
//Value chosen to be 1 hour, 1 minute, 1 second, and 1 millisecond
long milli = 3661001;

// hours
int hr = (milli / (1000 * 60 * 60)) % 24;

// minutes
int min = (milli / (1000 * 60)) % 60;

// seconds
int sec = (milli / 1000) % 60;

// milliseconds
int mill = milli % 1000;

char msg[10];
        sprintf(
                msg,
                "%02d hours and %02d minutes and %02d seconds and %02d milliseconds.\n",
                hr,
                min,
                sec,
                mill
                );
printf(msg);

cout << hr << " hours and " << min << " minutes and " << sec << " seconds and " << mill << " milliseconds." << endl;
}
Ilya Khudyakov
  • 301
  • 3
  • 15