Support optional milliseconds in LocalTime.
authorCarl Hetherington <cth@carlh.net>
Thu, 4 Jun 2015 10:59:21 +0000 (11:59 +0100)
committerCarl Hetherington <cth@carlh.net>
Thu, 4 Jun 2015 10:59:21 +0000 (11:59 +0100)
src/local_time.cc
src/local_time.h
test/local_time_test.cc

index f5d79938e24f63ecb7de23c189b26244a538a08b..32e8e093bd2702b64fb828ccddc5320ccc8d6364 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "local_time.h"
 #include "exceptions.h"
+#include "dcp_assert.h"
 #include <boost/lexical_cast.hpp>
 #include <boost/date_time/c_local_time_adjustor.hpp>
 #include <cstdio>
@@ -43,6 +44,7 @@ LocalTime::LocalTime ()
        _hour = tm->tm_hour;
        _minute = tm->tm_min;
        _second = tm->tm_sec;
+       _millisecond = 0;
 
        set_local_time_zone ();
 }
@@ -58,6 +60,8 @@ LocalTime::LocalTime (boost::posix_time::ptime t)
        _hour = t.time_of_day().hours ();
        _minute = t.time_of_day().minutes ();
        _second = t.time_of_day().seconds ();
+       _millisecond = t.time_of_day().fractional_seconds () / 1000;
+       DCP_ASSERT (_millisecond < 1000);
 
        set_local_time_zone ();
 }
@@ -74,46 +78,59 @@ LocalTime::set_local_time_zone ()
        _tz_minute = offset.minutes ();
 }
 
-/** @param s A string of the form 2013-01-05T18:06:59+04:00 */
+/** @param s A string of the form 2013-01-05T18:06:59[.123]+04:00 */
 LocalTime::LocalTime (string s)
 {
-       /* 2013-01-05T18:06:59+04:00 */
-        /* 0123456789012345678901234 */
+       /* 2013-01-05T18:06:59+04:00 or 2013-01-05T18:06:59.123+04:00 */
+        /* 0123456789012345678901234 or 01234567890123456789012345678 */
        
        if (s.length() < 25) {
                throw TimeFormatError (s);
        }
 
        /* Check incidental characters */
-       if (s[4] != '-' || s[7] != '-' || s[10] != 'T' || s[13] != ':' || s[16] != ':' || s[22] != ':') {
+       bool const common = s[4] == '-' && s[7] == '-' && s[10] == 'T' && s[13] == ':' && s[16] == ':';
+       bool const without_millisecond = common && s[22] == ':';
+       bool const with_millisecond = common && s[19] == '.' && s[26] == ':';
+
+       if (!with_millisecond && !without_millisecond) {
                throw TimeFormatError (s);
        }
-       
+
        _year = lexical_cast<int> (s.substr (0, 4));
        _month = lexical_cast<int> (s.substr (5, 2));
        _day = lexical_cast<int> (s.substr (8, 2));
        _hour = lexical_cast<int> (s.substr (11, 2));
        _minute = lexical_cast<int> (s.substr (14, 2));
        _second = lexical_cast<int> (s.substr (17, 2));
-       _tz_hour = lexical_cast<int> (s.substr (20, 2));
-       _tz_minute = lexical_cast<int> (s.substr (23, 2));
+       if (without_millisecond) {
+               _millisecond = 0;
+               _tz_hour = lexical_cast<int> (s.substr (20, 2));
+               _tz_minute = lexical_cast<int> (s.substr (23, 2));
+       } else {
+               _millisecond = lexical_cast<int> (s.substr (20, 3));
+               _tz_hour = lexical_cast<int> (s.substr (24, 2));
+               _tz_minute = lexical_cast<int> (s.substr (27, 2));
+       }
+
+       int const plus_minus_position = with_millisecond ? 23 : 19;
 
-       if (s[19] == '-') {
+       if (s[plus_minus_position] == '-') {
                _tz_hour = -_tz_hour;
-       } else if (s[19] != '+') {
+       } else if (s[plus_minus_position] != '+') {
                throw TimeFormatError (s);
        }
 }
 
-/** @return A string of the form 2013-01-05T18:06:59+04:00 */
+/** @return A string of the form 2013-01-05T18:06:59+04:00 or 2013-01-05T18:06:59.123+04:00 */
 string
-LocalTime::as_string () const
+LocalTime::as_string (bool with_millisecond) const
 {
        char buffer[32];
        snprintf (
                buffer, sizeof (buffer),
                "%sT%s%s%02d:%02d",
-               date().c_str(), time_of_day().c_str(), (_tz_hour >= 0 ? "+" : "-"), abs (_tz_hour), _tz_minute
+               date().c_str(), time_of_day(with_millisecond).c_str(), (_tz_hour >= 0 ? "+" : "-"), abs (_tz_hour), _tz_minute
                );
        return buffer;
 }
@@ -127,11 +144,15 @@ LocalTime::date () const
        return buffer;
 }
 
-/** @return The time in the form HH:MM:SS */
+/** @return The time in the form HH:MM:SS or HH:MM:SS.mmm */
 string
-LocalTime::time_of_day () const
+LocalTime::time_of_day (bool with_millisecond) const
 {
        char buffer[32];
-       snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d", _hour, _minute, _second);
+       if (with_millisecond) {
+               snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d.%03d", _hour, _minute, _second, _millisecond);
+       } else {
+               snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d", _hour, _minute, _second);
+       }
        return buffer;
 }
index c67aaee8a6d80330d7944f3bda5e0b0c97f4d873..b1751f8b0e884b8a620ad21ab24f196868fa7895 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -33,10 +33,12 @@ namespace dcp {
 
 /** @class LocalTime
  *  @brief A representation of a local time (down to the second), including its offset
- *  from GMT.
+ *  from GMT (equivalent to xs:dateTime).
  *
  *  I tried to use boost for this, really I did, but I could not get it
  *  to parse strings of the required format (those that include time zones).
+ *
+ *  See http://www.w3.org/TR/xmlschema-2/#dateTime
  */
 class LocalTime
 {
@@ -45,9 +47,9 @@ public:
        LocalTime (boost::posix_time::ptime);
        LocalTime (std::string);
 
-       std::string as_string () const;
+       std::string as_string (bool with_millisecond = false) const;
        std::string date () const;
-       std::string time_of_day () const;
+       std::string time_of_day (bool with_millisecond = false) const;
 
 private:
        friend class ::local_time_test;
@@ -61,6 +63,7 @@ private:
        int _hour;   ///< hour number of the day (0-23)
        int _minute; ///< minute number of the hour (0-59)
        int _second; ///< second number of the minute (0-59)
+       int _millisecond; ///< millisecond number of the second (0-999)
 
        int _tz_hour;   ///< hours by which this time is offset from UTC
        int _tz_minute; ///< minutes by which this time is offset from UTC
index 2c61fd14e8d31425b4f0e077afdb8a5a8304ee86..9c682a74263df0e3457a6aefe8c88c317c0c3f18 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -60,13 +60,41 @@ BOOST_AUTO_TEST_CASE (local_time_test)
                BOOST_CHECK_EQUAL (t.as_string(), "2011-11-20T01:06:59-09:30");
        }
 
-       /* Construction from boost::posix_time::ptime */
-       dcp::LocalTime b (boost::posix_time::time_from_string ("2002-01-20 19:03:56"));
-       BOOST_CHECK_EQUAL (b._year, 2002);
-       BOOST_CHECK_EQUAL (b._month, 1);
-       BOOST_CHECK_EQUAL (b._day, 20);
-       BOOST_CHECK_EQUAL (b._hour, 19);
-       BOOST_CHECK_EQUAL (b._minute, 3);
-       BOOST_CHECK_EQUAL (b._second, 56);
+       {
+               dcp::LocalTime t ("2011-11-20T01:06:59.456-09:30");
+               BOOST_CHECK_EQUAL (t._year, 2011);
+               BOOST_CHECK_EQUAL (t._month, 11);
+               BOOST_CHECK_EQUAL (t._day, 20);
+               BOOST_CHECK_EQUAL (t._hour, 1);
+               BOOST_CHECK_EQUAL (t._minute, 6);
+               BOOST_CHECK_EQUAL (t._second, 59);
+               BOOST_CHECK_EQUAL (t._millisecond, 456);
+               BOOST_CHECK_EQUAL (t._tz_hour, -9);
+               BOOST_CHECK_EQUAL (t._tz_minute, 30);
+               BOOST_CHECK_EQUAL (t.as_string(true), "2011-11-20T01:06:59.456-09:30");
+       }
+
+       {
+               /* Construction from boost::posix_time::ptime */
+               dcp::LocalTime b (boost::posix_time::time_from_string ("2002-01-20 19:03:56"));
+               BOOST_CHECK_EQUAL (b._year, 2002);
+               BOOST_CHECK_EQUAL (b._month, 1);
+               BOOST_CHECK_EQUAL (b._day, 20);
+               BOOST_CHECK_EQUAL (b._hour, 19);
+               BOOST_CHECK_EQUAL (b._minute, 3);
+               BOOST_CHECK_EQUAL (b._second, 56);
+       }
+
+       {
+               /* Construction from boost::posix_time::ptime with milliseconds */
+               dcp::LocalTime b (boost::posix_time::time_from_string ("2002-01-20 19:03:56.491"));
+               BOOST_CHECK_EQUAL (b._year, 2002);
+               BOOST_CHECK_EQUAL (b._month, 1);
+               BOOST_CHECK_EQUAL (b._day, 20);
+               BOOST_CHECK_EQUAL (b._hour, 19);
+               BOOST_CHECK_EQUAL (b._minute, 3);
+               BOOST_CHECK_EQUAL (b._second, 56);
+               BOOST_CHECK_EQUAL (b._millisecond, 491);
+       }
 }