23
\$\begingroup\$

The following will by my simple helper, that should behave just like std::chrono::system_clock but with the epoch being 1.1. 2000, that I can easily cast durations to seconds (or milliseconds or centiseconds) and store them in some records (originated from small devices).

It all started with one constexpr:

/// 1.1.2000 0:00
    static constexpr time_point<system_clock> epoch
      = time_point<system_clock>(seconds(946681200))

Which helped me to get e.g. milliseconds from 1.1.2000 like this:

duration_cast<milliseconds>( system_clock::now() - epoch ).count()

The problem is, that there is no contract / specification what is the epoch, but usually is 1.1. 1970.

(I have once selected 1.1. 2000 because of RTCs in units and designed the firmware to work until 1.1. 2100 when I will probably be dead already or very very old man and won't have to care... record size and amount of data transmitted was more important at the time I have designed the protocol.)

(There are some using std::chrono::system_clock and such inside namespace firda in that header)

#include "basics.hpp"
using namespace firda;

/// Clock with altered epoch
template<class Clock = system_clock
  , typename Clock::rep Epoch = 946681200>
  struct adapted_clock {

    using rep        = typename Clock::rep;
    using period     = typename Clock::period;
    using duration   = typename Clock::duration;
    using time_point = typename firda::time_point<adapted_clock>;

    static constexpr bool is_steady
      = Clock::is_steady;
    static constexpr typename Clock::time_point epoch
      = typename Clock::time_point(seconds(Epoch));

    static time_point now() {
        return time_point(Clock::now() - epoch);
    }

    static time_t to_time_t(const time_point& t) {
        return Clock::to_time_t(epoch + t.time_since_epoch());
    }

    static time_point from_time_t(time_t t) {
        return time_point(Clock::from_time_t(t) - epoch);
    }
};

using y2k_clock = adapted_clock<>;

int main() {
    auto now1 = system_clock::now();
    auto now2 = y2k_clock::now();
    cout << "now1.count: " << duration_cast<seconds>(now1
      .time_since_epoch()).count() << '\n';
    cout << "now2.count: " << duration_cast<seconds>(now2
      .time_since_epoch()).count() << '\n';
    time_t time1 = system_clock::to_time_t(now1);
    time_t time2 = y2k_clock::to_time_t(now2);
    cout << "time1: " << std::ctime(&time1) << '\n';
    cout << "time2: " << std::ctime(&time2) << '\n';
}
\$\endgroup\$
  • 1
    \$\begingroup\$ 946681200s past Jan/1/1970 00:00:00 is Dec/31/1999 23:00:00. Did you mean 946684800s? \$\endgroup\$ – user75007 Jun 3 '15 at 21:23
  • \$\begingroup\$ @HowardHinnant: Yes, I have probably mixed it badly with local time (CET ~ GMT+1). 946681200/3600=262967 which is odd. 946684800/3600/24=10957=30*365+7 (30*365.25 = 10957.5) \$\endgroup\$ – firda Jun 4 '15 at 11:10
8
+50
\$\begingroup\$

Frankly, there isn't much to say about this class since it is simple enough and relies on another simple clock. The type alias, static members and static methods seem to all rely just like they should on the template parameter Clock so there is no problem. I believe that firda::time_point is nothing really from std::chrono::time_point os this shouldn't be a problem either.

The only remarks I have is about the template parameters: I am not sure whether defaulting Clock to std::chrono::system_clock makes sense. Is it really the default clock amongst the three standard clocks? If you think so, why. But frankly, defaulting Epoch has no sense: adapted_clock<> doesn't tell anything about the epoch to the user and you already provide the meaningful type alias y2k_clock (note that using the defaults is always equivalent to using y2k_clock, which makes them useless). You can't default Clock without defaulting Epoch though because you can't change the order of the template parameters.

I think that the best solution would be to leave undefaulted template parameters in adapted_clock but make y2l_clock an alias template with a possibly default Clock template parameter:

template<typename Clock=std::chrono::system_clock>
using y2k_clock = adapted_clock<Clock, 946681200>;

If you don't like this alias template solution, I would still recommend not to default the template parameters of adapted_clock but instead to write this:

using y2k_clock = adapted_clock<std::chrono::system_clock, 946681200>;
\$\endgroup\$
  • \$\begingroup\$ I will take your hints into consideration. firda::time_point = std::time_point if selected in the header (which is by default). I use it to be able to swith to boost or any other implementation as needed. The design and defaulted template params are towards the goal - the y2k_clock (year 2000 clock) and adapted_clock thus prefer this epoch (and allow other clocks to be used). But I take it that I should think from the opposite side: who and how will use that ;) Making another template specifically adjusting to Y2K sounds good. \$\endgroup\$ – firda Dec 19 '14 at 10:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.