Add support for parsing time strings

This lets us take time strings as command line arguments pretty easily
and manipulate them

Change-Id: I7971cce24a71933c9c1fa9fd13555848efc5964e
diff --git a/aos/time/time.cc b/aos/time/time.cc
index 03fe45b..89f1c4d 100644
--- a/aos/time/time.cc
+++ b/aos/time/time.cc
@@ -3,6 +3,7 @@
 #include <inttypes.h>
 #include <string.h>
 
+#include <algorithm>
 #include <chrono>
 #include <ctime>
 #include <iomanip>
@@ -80,6 +81,67 @@
   return stream;
 }
 
+std::optional<monotonic_clock::time_point> monotonic_clock::FromString(
+    const std::string_view now) {
+  // This should undo the operator << above.
+  if (now.size() < 14) {
+    return std::nullopt;
+  }
+
+  if (now.substr(now.size() - 3, now.size()) != "sec") {
+    return std::nullopt;
+  }
+
+  if (now.substr(now.size() - 13, 1) != ".") {
+    return std::nullopt;
+  }
+
+  bool negative = now.substr(0, 1) == "-";
+
+  std::string sec(
+      now.substr(negative ? 1 : 0, now.size() - (negative ? 14 : 13)));
+  std::string nsec(now.substr(now.size() - 12, 9));
+
+  if (!std::all_of(sec.begin(), sec.end(), ::isdigit) ||
+      !std::all_of(nsec.begin(), nsec.end(), ::isdigit)) {
+    return std::nullopt;
+  }
+
+  return monotonic_clock::time_point(
+      std::chrono::seconds((negative ? -1 : 1) * atoll(sec.c_str())) +
+      std::chrono::nanoseconds((negative ? -1 : 1) * atoll(nsec.c_str())));
+}
+
+std::optional<realtime_clock::time_point> realtime_clock::FromString(
+    const std::string_view now) {
+  // This should undo the operator << above.
+
+  if (now.size() < 25) {
+    return std::nullopt;
+  }
+
+  if (now.substr(now.size() - 10, 1) != ".") {
+    return std::nullopt;
+  }
+
+  std::string nsec(now.substr(now.size() - 9, 9));
+
+  if (!std::all_of(nsec.begin(), nsec.end(), ::isdigit)) {
+    return std::nullopt;
+  }
+
+  struct tm tm;
+  std::istringstream ss(std::string(now.substr(0, now.size() - 10)));
+  ss >> std::get_time(&tm, "%Y-%m-%d_%H-%M-%S");
+  tm.tm_isdst = -1;
+
+  time_t seconds = mktime(&tm);
+
+  return realtime_clock::time_point(
+      std::chrono::seconds(seconds) +
+      std::chrono::nanoseconds(atoll(nsec.c_str())));
+}
+
 std::ostream &operator<<(std::ostream &stream,
                          const aos::realtime_clock::time_point &now) {
   std::tm tm;
diff --git a/aos/time/time.h b/aos/time/time.h
index e31de5c..0bd0708 100644
--- a/aos/time/time.h
+++ b/aos/time/time.h
@@ -6,6 +6,7 @@
 #include <time.h>
 
 #include <chrono>
+#include <optional>
 #include <ostream>
 #include <thread>
 #include <type_traits>
@@ -27,6 +28,11 @@
   // not steady.
   static constexpr bool is_steady = false;
 
+  // Converts the time string to a time_point if it is well formatted.  This is
+  // designed to reverse operator <<.
+  static std::optional<monotonic_clock::time_point> FromString(
+      const std::string_view now);
+
   // Returns the epoch (0).
   static constexpr monotonic_clock::time_point epoch() {
     return time_point(zero());
@@ -52,6 +58,11 @@
 #endif  // __linux__
   static constexpr bool is_steady = false;
 
+  // Converts the time string to a time_point if it is well formatted.  This is
+  // designed to reverse operator <<.
+  static std::optional<realtime_clock::time_point> FromString(
+      const std::string_view now);
+
   // Returns the epoch (0).
   static constexpr realtime_clock::time_point epoch() {
     return time_point(zero());
diff --git a/aos/time/time_test.cc b/aos/time/time_test.cc
index c5eb634..6ff1085 100644
--- a/aos/time/time_test.cc
+++ b/aos/time/time_test.cc
@@ -89,6 +89,7 @@
     s << t;
 
     EXPECT_EQ(s.str(), "-13.085000000sec");
+    EXPECT_EQ(monotonic_clock::FromString(s.str()).value(), t);
   }
   {
     const monotonic_clock::time_point t =
@@ -98,6 +99,7 @@
     s << t;
 
     EXPECT_EQ(s.str(), "-0.000000001sec");
+    EXPECT_EQ(monotonic_clock::FromString(s.str()).value(), t);
   }
   {
     const monotonic_clock::time_point t =
@@ -107,6 +109,7 @@
     s << t;
 
     EXPECT_EQ(s.str(), "-1.000000001sec");
+    EXPECT_EQ(monotonic_clock::FromString(s.str()).value(), t);
   }
   {
     const monotonic_clock::time_point t =
@@ -116,6 +119,7 @@
     s << t;
 
     EXPECT_EQ(s.str(), "-2.000000001sec");
+    EXPECT_EQ(monotonic_clock::FromString(s.str()).value(), t);
   }
 }
 
@@ -127,6 +131,7 @@
   s << t;
 
   EXPECT_EQ(s.str(), "-9223372036.854775808sec");
+  EXPECT_EQ(monotonic_clock::FromString(s.str()).value(), t);
 }
 
 // Test that << works with the epoch on the realtime clock.
@@ -137,6 +142,7 @@
   s << t;
 
   EXPECT_EQ(s.str(), "1970-01-01_00-00-00.000000000");
+  EXPECT_EQ(realtime_clock::FromString(s.str()).value(), t);
 }
 
 // Test that << works with positive time on the realtime clock.
@@ -149,6 +155,7 @@
   s << t;
 
   EXPECT_EQ(s.str(), "1970-01-06_00-00-11.005000000");
+  EXPECT_EQ(realtime_clock::FromString(s.str()).value(), t);
 }
 
 // Test that << works with negative time on the realtime clock.
@@ -161,6 +168,7 @@
     s << t;
 
     EXPECT_EQ(s.str(), "1969-12-31_23-59-59.999999999");
+    EXPECT_EQ(realtime_clock::FromString(s.str()).value(), t);
   }
   {
     const realtime_clock::time_point t =
@@ -170,6 +178,7 @@
     s << t;
 
     EXPECT_EQ(s.str(), "1969-12-31_23-59-59.000000001");
+    EXPECT_EQ(realtime_clock::FromString(s.str()).value(), t);
   }
   {
     const realtime_clock::time_point t = realtime_clock::epoch() -
@@ -180,6 +189,7 @@
     s << t;
 
     EXPECT_EQ(s.str(), "1969-12-31_23-59-58.000000001");
+    EXPECT_EQ(realtime_clock::FromString(s.str()).value(), t);
   }
   {
     const realtime_clock::time_point t =
@@ -190,6 +200,7 @@
     s << t;
 
     EXPECT_EQ(s.str(), "1969-12-27_00-00-10.995000000");
+    EXPECT_EQ(realtime_clock::FromString(s.str()).value(), t);
   }
 }