Skip to main content
Code Review

Return to Revisions

3 of 3
Commonmark migration

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:

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 and time->tm_yday are ignored. The values in time 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.

Incomputable
  • 9.7k
  • 3
  • 34
  • 73
default

AltStyle によって変換されたページ (->オリジナル) /