This is quite tricky problem to get right. As Loki said, we need to convert it to seconds after epoch. But doing so is not trivial...
Standard approach:
Well, not really standard, but in my opinion one should use std::chrono
in this case. Some stuff is done in a roundabout way, but I don't think it is a huge problem. Howard Hinnant has written a library for it, but in case the license isn't acceptable, I'll reimplement some functionality by hand.
output std::time_point
:
The first issue is outputting a std::time_point
. The problem with it is that it doesn't have operator<<
overload for streams. The only way possible to deal with this is to implement our own:
Necessary links:
std::chrono::system_clock::to_time_t()
(system clock is just example).
The only ways to print time are either std::ctime()
and std::put_time()
(not counting the "implement by hand" approach). So let's follow the thread and find its end and test it:
#include <iostream>
#include <chrono>
#include <iomanip>
template <typename Clock, typename Duration>
std::ostream& operator<<(std::ostream& os, const std::chrono::time_point<Clock, Duration>& timep)
{
auto converted_timep = Clock::to_time_t(timep);
os << std::put_time(std::localtime(&converted_timep), "%Y %b %d %H:%M:%S");
return os;
}
int main()
{
auto now = std::chrono::system_clock::now();
std::cout << now;
return 0;
}
Output: 2017 Jun 04 12:07:48
.
The only problem with implementation is that it can't be used from multiple threads, since std::localtime()
returns pointer to static variable. The format could be mutable by some function, but I wouldn't care about it at this point.
Since we can manipulate std::chrono::time_point
, and std::chrono::duration
, let's use everything we have:
convert given date to std::chrono::time_point<system_clock>
:
A good news, at last! From std::mktime()
:
Converts local calendar time to a time since epoch as a
time_t
object.time->tm_wday
andtime->tm_yday
are ignored. The values intime
are permitted to be outside their normal ranges.
Hooray! So we can just fill in the std::tm
structure with year, month, and day since January 1 and we are done!
#include <iostream>
#include <chrono>
#include <iomanip>
template <typename Clock, typename Duration>
std::ostream& operator<<(std::ostream& os, const std::chrono::time_point<Clock, Duration>& timep)
{
auto converted_timep = Clock::to_time_t(timep);
os << std::put_time(std::localtime(&converted_timep), "%Y %b %d %H:%M:%S");
return os;
}
auto convert_to_timepoint(int years, int months, int days)
{
//perform checks, do division, modulus and stuff...
years -= 1900; //epoch
std::tm date = {};
date.tm_year = years;
date.tm_mon = months;
date.tm_mday = days;
return std::chrono::system_clock::from_time_t(std::mktime(&date));
}
int main()
{
auto now = std::chrono::system_clock::now();
std::cout << now << '\n';
std::cout << "calling convert_to_timepoint:\n";
auto date = convert_to_timepoint(1991, 0, 23);
std::cout << date;
return 0;
}
Output:
2017 Jun 04 13:08:49
calling convert_to_timepoint:
1991 Jan 23 00:00:00
Now the easy stuff, add the given number of days:
#include <iostream>
#include <chrono>
#include <iomanip>
template <typename Clock, typename Duration>
std::ostream& operator<<(std::ostream& os, const std::chrono::time_point<Clock, Duration>& timep)
{
auto converted_timep = Clock::to_time_t(timep);
os << std::put_time(std::localtime(&converted_timep), "%Y %b %d %H:%M:%S");
return os;
}
auto convert_to_timepoint(int years, int months, int days)
{
years -= 1900; //epoch
std::tm date = {};
date.tm_year = years;
date.tm_mon = months;
date.tm_mday = days;
return std::chrono::system_clock::from_time_t(std::mktime(&date));
}
template <typename Clock, typename Duration>
auto add_days(const std::chrono::time_point<Clock, Duration>& timepoint, int days_to_add)
{
constexpr std::time_t seconds_in_day = 60 * 60 * 24;
// mm hh dd
std::time_t days = seconds_in_day * days_to_add;
auto date = Clock::to_time_t(timepoint);
return Clock::from_time_t(date + days);
}
int main()
{
auto now = std::chrono::system_clock::now();
std::cout << now << '\n';
std::cout << "calling convert_to_timepoint with 1991 0 23 (1991 Jan 23):\n";
auto date = convert_to_timepoint(1991, 0, 23);
std::cout << date << '\n';
std::cout << "10 day later (1991 Feb 02):\n" << add_days(date, 10);
return 0;
}
Output:
2017 Jun 04 13:21:29
calling convert_to_timepoint with 1991 0 23 (1991 Jan 23):
1991 Jan 23 00:00:00
10 day later (1991 Feb 02):
1991 Feb 02 00:00:00
I left the convert_to_timepoint()
so that it would be reusable on its own, but if needed it can be fused with add_days()
.
Some thoughts:
using namespace std;
rarely buys anything, but opens a chance for errors.- standard library has a solution most of the time
- modularity is important. Breaking functions into logical pieces makes it very easy to quickly prototype, adjust, mix'n'match.
Conclusion:
In this kind of problem, it is very dangerous try to implement by hand, since there are lots of edge cases. As a result, the problem given was rather a test of knowledge of standard library.
- 9.7k
- 3
- 34
- 73