2

So say I set a time in tm to be 23:00:00

ptm->tm_hour = 23; ptm->tm_min = 0; ptm->tm_sec = 0;

And I want to allow a user to subtract time from this

ptm->tm_hour -= hourinput; ptm->tm_min -= minuteinput; ptm->tm_sec -= secondinput;

If the user subtracts 0 hours, 5 minutes, and 5 seconds, instead of showing up as 22:54:55, it will show up as 23:-5:-5.

I suppose I could do a bunch of if statements to check if ptm is below 0 and account for this, but is there a more efficient way of getting the proper time?

Blaze
  • 16,736
  • 2
  • 25
  • 44
Zevvysan
  • 283
  • 2
  • 13
  • 1
    Yes you need to "do a bunch of if statements to check if ptm is below 0 and account for this". – Some programmer dude Jan 24 '19 at 08:12
  • 1
    Don't manipulate time this way, always do it based on the epoch time, which means you can adjust the milliseconds since 1970 and translate back to HMS using standard function calls, makes life so much easier. – SPlatten Jan 24 '19 at 08:17

2 Answers2

4

Yes, you can use std::mktime for this. It doesn't just convert a std::tm to a std::time_t, it also fixes the tm if some field went out of range. Consider this example where we take the current time and add 1000 seconds.

#include <iostream>
#include <iomanip> // put_time
#include <ctime>

int main(int argc, char **argv) {
    std::time_t t = std::time(nullptr);
    std::tm tm = *std::localtime(&t);
    std::cout << "Time: " << std::put_time(&tm, "%c %Z") << std::endl;
    tm.tm_sec += 1000; // the seconds are now out of range
    //std::cout << "Time in 1000 sec" << std::put_time(&tm, "%c %Z") << std::endl; this would crash!
    std::mktime(&tm); // also returns a time_t, but we don't need that here
    std::cout << "Time in 1000 sec: " << std::put_time(&tm, "%c %Z") << std::endl;
    return 0;
}

My Output:

Time: 01/24/19 09:26:46 W. Europe Standard Time

Time in 1000 sec: 01/24/19 09:43:26 W. Europe Standard Time

As you can see, the time went from 09:26:46 to 09:43:26.

Community
  • 1
  • 1
Blaze
  • 16,736
  • 2
  • 25
  • 44
3

Here's another approach using Howard Hinnant's date library, which is on its way into C++2a.

#include <iostream>
#include "date/date.h"

using namespace std::chrono_literals;

// Time point representing the start of today:
auto today = date::floor<date::days>(std::chrono::system_clock::now());

auto someTp = today + 23h; // Today, at 23h00
auto anotherTp = someTp - 5min - 5s; // ... should be self-explanatory now :)

std::cout << date::format("%b-%d-%Y %T\n", anotherTp);

If you want to expose the manipulation of time points via a user interface, the compile-time constructs 23h, 5min and so on are of course not available. Those literals construct std::chrono::duration objects, so you need a mechanism to turn user input into equivalent instances.

lubgr
  • 37,368
  • 3
  • 66
  • 117