time: Add ability to specialize system and steady clocks

Default implementation is "optimized" to use boost::chrono clocks.  When
specialization is used, now() calls are redirected to the virtual
methods of the specified specialization.

This commit also includes an implementation of UnitTestClock that
can be used in unit tests to eliminate dependency on the wall clock.

Change-Id: I7ced774634d90ce32714a71e2a66129cba91b9ae
Refs: #2158
diff --git a/src/util/time.cpp b/src/util/time.cpp
index 15ac925..b652a7b 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -20,11 +20,112 @@
  */
 
 #include "time.hpp"
+#include "time-custom-clock.hpp"
+
 #include <boost/date_time/posix_time/posix_time.hpp>
 
 namespace ndn {
 namespace time {
 
+static shared_ptr<CustomSystemClock> g_systemClock;
+static shared_ptr<CustomSteadyClock> g_steadyClock;
+
+// this method is defined in time-custom-clock.hpp
+void
+setCustomClocks(shared_ptr<CustomSteadyClock> steadyClock,
+                shared_ptr<CustomSystemClock> systemClock)
+{
+  g_systemClock = systemClock;
+  g_steadyClock = steadyClock;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+system_clock::time_point
+system_clock::now() noexcept
+{
+  if (g_systemClock == nullptr) {
+    // optimized default version
+    return time_point(boost::chrono::system_clock::now().time_since_epoch());
+  }
+  else {
+    return g_systemClock->getNow();
+  }
+}
+
+std::time_t
+system_clock::to_time_t(const time_point& t) noexcept
+{
+  return duration_cast<seconds>(t.time_since_epoch()).count();
+}
+
+system_clock::time_point
+system_clock::from_time_t(std::time_t t) noexcept
+{
+  return time_point(seconds(t));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef __APPLE__
+  // Note that on OS X platform boost::steady_clock is not truly monotonic, so we use
+  // system_clock instead.  Refer to https://svn.boost.org/trac/boost/ticket/7719)
+  typedef boost::chrono::system_clock base_steady_clock;
+#else
+  typedef boost::chrono::steady_clock base_steady_clock;
+#endif
+
+steady_clock::time_point
+steady_clock::now() noexcept
+{
+  if (g_steadyClock == nullptr) {
+    // optimized default version
+    return time_point(base_steady_clock::now().time_since_epoch());
+  }
+  else {
+    return g_steadyClock->getNow();
+  }
+}
+
+boost::posix_time::time_duration
+steady_clock::to_posix_duration(const duration& duration)
+{
+  if (g_steadyClock == nullptr) {
+    // optimized default version
+    return
+#ifdef BOOST_DATE_TIME_HAS_NANOSECONDS
+      boost::posix_time::nanoseconds(duration_cast<nanoseconds>(duration).count())
+#else
+      boost::posix_time::microseconds(duration_cast<microseconds>(duration).count())
+#endif
+      ;
+  }
+  else {
+    return g_steadyClock->toPosixDuration(duration);
+  }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+const system_clock::TimePoint&
+getUnixEpoch()
+{
+  static system_clock::TimePoint epoch = system_clock::from_time_t(0);
+  return epoch;
+}
+
+milliseconds
+toUnixTimestamp(const system_clock::TimePoint& point)
+{
+  return duration_cast<milliseconds>(point - getUnixEpoch());
+}
+
+system_clock::TimePoint
+fromUnixTimestamp(const milliseconds& duration)
+{
+  return getUnixEpoch() + duration;
+}
+
 std::string
 toIsoString(const system_clock::TimePoint& timePoint)
 {
@@ -100,3 +201,45 @@
 
 } // namespace time
 } // namespace ndn
+
+namespace boost {
+namespace chrono {
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+template<class CharT>
+std::basic_string<CharT>
+clock_string<ndn::time::system_clock, CharT>::since()
+{
+  if (ndn::time::g_systemClock == nullptr) {
+    // optimized default version
+    return clock_string<system_clock, CharT>::since();
+  }
+  else {
+    return ndn::time::g_systemClock->getSince();
+  }
+}
+
+template
+struct clock_string<ndn::time::system_clock, char>;
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+template<class CharT>
+std::basic_string<CharT>
+clock_string<ndn::time::steady_clock, CharT>::since()
+{
+  if (ndn::time::g_steadyClock == nullptr) {
+    // optimized default version
+    return clock_string<ndn::time::base_steady_clock, CharT>::since();
+  }
+  else {
+    return ndn::time::g_steadyClock->getSince();
+  }
+}
+
+template
+struct clock_string<ndn::time::steady_clock, char>;
+
+} // namespace chrono
+} // namespace boost