Reputation: 2269
I have a lot of strings that are in the form "HH:MM" and I constantly have to do arithmetic on them (for example add 5 minutes, add 24 hours, etc).
I am wondering if there is any built in class in the standard library that can handle such arithmetic instead of having to manually change the string and handle corner cases?
Upvotes: 2
Views: 1976
Reputation: 218710
If you don't have strptime
available, and if you have <chrono>
and don't want to fool around with the C API, you can use Howard Hinnant's free, open-source, portable datetime library to write helpers to convert to and from std::chrono::minutes
.
So convert from string to minutes, do whatever computation you need, and then convert back to string:
#include "date.h"
#include <iostream>
#include <string>
#include <sstream>
std::chrono::minutes
to_minutes(const std::string& s)
{
std::istringstream in{s};
in.exceptions(std::ios::failbit);
std::chrono::minutes m;
in >> date::parse("%H:%M", m);
return m;
}
std::string
to_string(std::chrono::minutes m)
{
return date::format("%H:%M", m);
}
int
main()
{
using namespace std::chrono_literals;
std::cout << to_string(to_minutes("5:47") + 2h + 168min) << '\n';
}
Output:
10:35
This library has been ported to recent versions of VS, gcc and clang.
You can also use this library to work with different precisions quite seamlessly, for example seconds, or even milliseconds, and even mix all the precisions you need together:
std::chrono::milliseconds
to_milliseconds(const std::string& s)
{
std::istringstream in{s};
in.exceptions(std::ios::failbit);
std::chrono::milliseconds ms;
in >> date::parse("%T", ms);
return ms;
}
std::string
to_string(std::chrono::milliseconds ms)
{
return date::format("%T", ms);
}
// ...
std::cout << to_string(to_minutes("5:47") + 2h + 168min +
122s + 465ms + to_milliseconds("1:23:02.123")) << '\n';
Output:
12:00:04.588
Upvotes: 3
Reputation: 2750
Using <ctime>
you can convert your HH:MM string to a struct tm
as follows:
struct tm time_components;
memset(&time_components, 0, sizeof(struct tm));
time_components.tm_year = 2017;
strptime("01:45", "%H:%M", &time_components);
This assumes you have strptime
available. If not, use sscanf
or something similar to extract your hour and minute components--I'm sure you're doing this already. I'm setting tm_year
there because otherwise it's zero, which is not a valid year for the conversion to time_t
later on.
We can easily convert the tm
struct back into a HH:MM string using strftime
.
char buf[6];
strftime(buf, 6, "%H:%M", &time_components);
printf("time_components before manipulation: %s\n", buf);
But how about manipulating it? My first thought was 'just convert to time_t
and add/subtract however many seconds you want'. But time_t
isn't guaranteed to be in seconds, so don't do that. Instead, add whatever number of minutes you like to the tm_min
component of the tm
struct, then call mktime
with it, which will correctly handle any values that are outside the normal bounds of hours/minutes/seconds. At that point you have a time_t
, which we don't want to mess with, so just convert that back into a tm
using localtime
. localtime
will mirror the conversion that occurred in mktime
, so you shouldn't have to worry about time zones and so on. Now you've essentially normalized the tm
structure, and your overflowing minutes have been converted to hours, and your overflowing hours have been converted to days.
time_components.tm_min += 65; // Add 65 minutes. Negative values work as expected too!
time_t temp_time = mktime(&time_components);
struct tm* adjusted_time = localtime(&temp_time);
strftime(buf, 6, "%H:%M", adjusted_time);
printf("adjusted_time: %s\n", buf);
That adjusted_time
pointer seems to point to an internal tm
struct that will change in any subsequent calls to mktime
or gmtime
, FYI.
There's no doubt this is a fairly hellish approach. If all you need to do is handle minute/hour overflow and simple arithmetic, I'd be tempted to roll my own or look elsewhere.
Upvotes: 2
Reputation: 842
http://en.cppreference.com/w/cpp/chrono/duration supports the arithmetic and types you want, but I don't think you can construct from strings.
However you can with Boost Chrono. I tend to prefer it for most cases, because if I'm dealing with the problem of hours and minutes, I'll probably end up dealing with dates too in the future.
Upvotes: 1