From 9fb1cf5a54b423f42c9f94b4e3eeff3bfd0c227b Mon Sep 17 00:00:00 2001 From: fgerlits Date: Tue, 26 Jul 2005 10:36:01 +0000 Subject: [PATCH] added parseTimeDuration() function, to replace boost::posix_time:: duration_from_string(), which is badly broken --- .../include/LiveSupport/Core/TimeConversion.h | 76 ++++++++++- .../modules/core/src/TimeConversion.cxx | 121 +++++++++++++++++- .../modules/core/src/TimeConversionTest.cxx | 54 +++++++- .../modules/core/src/TimeConversionTest.h | 13 +- 4 files changed, 257 insertions(+), 7 deletions(-) diff --git a/livesupport/modules/core/include/LiveSupport/Core/TimeConversion.h b/livesupport/modules/core/include/LiveSupport/Core/TimeConversion.h index e5f12fe85..0d7d0996f 100644 --- a/livesupport/modules/core/include/LiveSupport/Core/TimeConversion.h +++ b/livesupport/modules/core/include/LiveSupport/Core/TimeConversion.h @@ -22,7 +22,7 @@ Author : $Author: fgerlits $ - Version : $Revision: 1.9 $ + Version : $Revision: 1.10 $ Location : $Source: /home/paul/cvs2svn-livesupport/newcvsrepo/livesupport/modules/core/include/LiveSupport/Core/TimeConversion.h,v $ ------------------------------------------------------------------------------*/ @@ -71,15 +71,57 @@ using namespace LiveSupport; * A helper object holding static time conversion functions. * * @author $Author: fgerlits $ - * @version $Revision: 1.9 $ + * @version $Revision: 1.10 $ */ class TimeConversion { private: + /** + * Parse a time string. + * + * It cuts off the portion between the end of the string and the last + * occurrence of the separator character. For example, if called with + * the parameters "00:01:02" and ':', the function returns "02" and + * truncates the original string to "00:01". + * + * If the separator character is not found in timeString, + * a copy of the whole timeString is returned, and the + * original timeString is changed to the empty string. + * + * @param timeString the input argument; on return, the rest of the + * string, after the return value cut off + * @param separator a separator character, usually ':' + * @return the part of the string which was cut off + */ + static Ptr::Ref + nextNumberFromEnd(Ptr::Ref timeString, + char separator) throw (); + + /** + * Parse a decimal string. + * + * It cuts off the portion between the end of the string and the last + * occurrence of the separator character. For example, if called with + * the parameters "1.23" and '.', the function returns "23" and + * truncates the original string to "1". + * + * If the separator character is not found in timeString, + * then an empty string is returned, and the + * original timeString remains unchanged. + * + * @param decimalString the input argument; on return, the rest of + * the string, after the return value cut off + * @param separator a separator character, usually '.' + * @return the part of the string which was cut off + */ + static Ptr::Ref + nextNumberFromStart(Ptr::Ref decimalString, + char separator) throw (); + /** * The default constructor. */ - TimeConversion(void) throw () + TimeConversion(void) throw () { } @@ -157,10 +199,38 @@ class TimeConversion * more than two characters wide, e.g.: "8765:48:45". * * @param duration the time duration to convert. + * @return the time duration in string format */ static Ptr::Ref timeDurationToHhMmSsString(Ptr::Ref duration) throw (); + + /** + * Parse a string to a time_duration. + * Similar to boost::posix_time::duration_from_string(), only + * not broken quite as badly. + * + * Parsing is right-to-left, starting with seconds: for example, + * 5 means 5 seconds; 01:02.03 means 1m 2.03s; 1:2:3 means 1h 2m 3s. + * + * If the time format is invalid, no exception is thrown, but the + * result is undefined (usually 00:00:00). + * TODO: fix this, by adding a format check + * + * @param durationString the duration as string + * @return the duration as a time_duration + */ + static Ptr::Ref + parseTimeDuration(Ptr::Ref durationString) + throw (); + + /** + * Get the number of digits used for fractional seconds + * in time durations. + * Returns the constant 6, for microsecond precision. + */ + static int + getNumberOfDigitsPrecision(void) throw (); }; diff --git a/livesupport/modules/core/src/TimeConversion.cxx b/livesupport/modules/core/src/TimeConversion.cxx index ac5cecac7..f367a6ad6 100644 --- a/livesupport/modules/core/src/TimeConversion.cxx +++ b/livesupport/modules/core/src/TimeConversion.cxx @@ -22,7 +22,7 @@ Author : $Author: fgerlits $ - Version : $Revision: 1.9 $ + Version : $Revision: 1.10 $ Location : $Source: /home/paul/cvs2svn-livesupport/newcvsrepo/livesupport/modules/core/src/TimeConversion.cxx,v $ ------------------------------------------------------------------------------*/ @@ -33,6 +33,8 @@ #include "configure.h" #endif +#include + #include "LiveSupport/Core/TimeConversion.h" @@ -46,6 +48,10 @@ using namespace LiveSupport::Core; /* ================================================ local constants & macros */ +/** + * The number of digits used for fractional seconds in time durations. + */ +static const int numberOfDigitsPrecision = 6; /* =============================================== local function prototypes */ @@ -207,3 +213,116 @@ TimeConversion :: timeDurationToHhMmSsString( return result; } + +/*------------------------------------------------------------------------------ + * Parse a string to a time_duration. + *----------------------------------------------------------------------------*/ +Ptr::Ref +TimeConversion :: parseTimeDuration(Ptr::Ref durationString) + throw () +{ + int micros = 0; + int seconds = 0; + int minutes = 0; + int hours = 0; + + Ptr::Ref temp(new std::string(*durationString)); + + if (temp->length() > 0) { + Ptr::Ref secondsString = nextNumberFromEnd(temp, ':'); + Ptr::Ref fractionsString = nextNumberFromStart( + secondsString, '.'); + if (fractionsString->length() > 0) { + std::stringstream fractionsStream; + fractionsStream << std::left + << std::setw( + TimeConversion::getNumberOfDigitsPrecision() ) + << std::setfill('0') + << *fractionsString; + fractionsStream >> micros; + } + if (secondsString->length() > 0) { + std::stringstream secondsStream(*secondsString); + secondsStream >> seconds; + } + } + + if (temp->length() > 0) { + Ptr::Ref minutesString = nextNumberFromEnd(temp, ':'); + std::stringstream minutesStream(*minutesString); + minutesStream >> minutes; + } + + if (temp->length() > 0) { + std::stringstream hoursStream(*temp); + hoursStream >> hours; + } + + Ptr::Ref result(new time_duration( + hours, minutes, seconds, micros )); + return result; +} + + +/*------------------------------------------------------------------------------ + * Parse a time string. + *----------------------------------------------------------------------------*/ +Ptr::Ref +TimeConversion :: nextNumberFromEnd(Ptr::Ref timeString, + char separator) + throw () +{ + Ptr::Ref result; + unsigned int pos = timeString->find_last_of(separator); + + if (pos != std::string::npos) { + if (pos != timeString->length()-1) { + result.reset(new std::string(*timeString, pos+1)); + } else { + result.reset(new std::string); + } + *timeString = timeString->substr(0, pos); + } else { + result.reset(new std::string(*timeString)); + *timeString = std::string(""); + } + + return result; +} + + +/*------------------------------------------------------------------------------ + * Parse a decimal string. + *----------------------------------------------------------------------------*/ +Ptr::Ref +TimeConversion :: nextNumberFromStart(Ptr::Ref timeString, + char separator) + throw () +{ + Ptr::Ref result; + unsigned int pos = timeString->find(separator); + + if (pos != std::string::npos) { + if (pos != timeString->length()-1) { + result.reset(new std::string(*timeString, pos+1)); + } else { + result.reset(new std::string); + } + *timeString = timeString->substr(0, pos); + } else { + result.reset(new std::string("")); + } + + return result; +} + + +/*------------------------------------------------------------------------------ + * Get the number of digits used for fractional seconds in time durations. + *----------------------------------------------------------------------------*/ +int +TimeConversion :: getNumberOfDigitsPrecision(void) throw () +{ + return numberOfDigitsPrecision; +} + diff --git a/livesupport/modules/core/src/TimeConversionTest.cxx b/livesupport/modules/core/src/TimeConversionTest.cxx index a97803f52..bc248f048 100644 --- a/livesupport/modules/core/src/TimeConversionTest.cxx +++ b/livesupport/modules/core/src/TimeConversionTest.cxx @@ -22,7 +22,7 @@ Author : $Author: fgerlits $ - Version : $Revision: 1.8 $ + Version : $Revision: 1.9 $ Location : $Source: /home/paul/cvs2svn-livesupport/newcvsrepo/livesupport/modules/core/src/TimeConversionTest.cxx,v $ ------------------------------------------------------------------------------*/ @@ -239,3 +239,55 @@ TimeConversionTest :: durationToStringTest(void) CPPUNIT_ASSERT_EQUAL(std::string("111:22:33"), *hhMmSsString); } + +/*------------------------------------------------------------------------------ + * Test the parseTimeDuration() function. + *----------------------------------------------------------------------------*/ +void +TimeConversionTest :: parseTimeDurationTest(void) + throw (CPPUNIT_NS::Exception) +{ + // legal arguments + Ptr::Ref timeString(new std::string("01:02:03.503700")); + Ptr::Ref duration; + CPPUNIT_ASSERT_NO_THROW( + duration = TimeConversion::parseTimeDuration(timeString) + ); + CPPUNIT_ASSERT(duration); + CPPUNIT_ASSERT_EQUAL(std::string("01:02:03.503700"), + to_simple_string(*duration)); + + timeString.reset(new std::string("02:03.5")); + CPPUNIT_ASSERT_NO_THROW( + duration = TimeConversion::parseTimeDuration(timeString) + ); + CPPUNIT_ASSERT(duration); + CPPUNIT_ASSERT_EQUAL(std::string("00:02:03.500000"), + to_simple_string(*duration)); + + timeString.reset(new std::string("77")); + CPPUNIT_ASSERT_NO_THROW( + duration = TimeConversion::parseTimeDuration(timeString) + ); + CPPUNIT_ASSERT(duration); + CPPUNIT_ASSERT_EQUAL(std::string("00:01:17"), + to_simple_string(*duration)); + + // illegal arguments + timeString.reset(new std::string("5 minutes and 2 seconds")); + CPPUNIT_ASSERT_NO_THROW( + duration = TimeConversion::parseTimeDuration(timeString) + ); + CPPUNIT_ASSERT(duration); + CPPUNIT_ASSERT_EQUAL(std::string("00:00:05"), // bad! + to_simple_string(*duration)); + + timeString.reset(new std::string("1.2.3")); + CPPUNIT_ASSERT_NO_THROW( + duration = TimeConversion::parseTimeDuration(timeString) + ); + CPPUNIT_ASSERT(duration); + CPPUNIT_ASSERT_EQUAL(std::string("00:00:01.000002"), // bad! + to_simple_string(*duration)); +} + diff --git a/livesupport/modules/core/src/TimeConversionTest.h b/livesupport/modules/core/src/TimeConversionTest.h index 3d6a7f922..64e3ce4ab 100644 --- a/livesupport/modules/core/src/TimeConversionTest.h +++ b/livesupport/modules/core/src/TimeConversionTest.h @@ -22,7 +22,7 @@ Author : $Author: fgerlits $ - Version : $Revision: 1.7 $ + Version : $Revision: 1.8 $ Location : $Source: /home/paul/cvs2svn-livesupport/newcvsrepo/livesupport/modules/core/src/TimeConversionTest.h,v $ ------------------------------------------------------------------------------*/ @@ -58,7 +58,7 @@ namespace Core { * Unit test for the TimeConversion class. * * @author $Author: fgerlits $ - * @version $Revision: 1.7 $ + * @version $Revision: 1.8 $ * @see TimeConversion */ class TimeConversionTest : public CPPUNIT_NS::TestFixture @@ -70,6 +70,7 @@ class TimeConversionTest : public CPPUNIT_NS::TestFixture CPPUNIT_TEST(nowTest); CPPUNIT_TEST(sleepTest); CPPUNIT_TEST(durationToStringTest); + CPPUNIT_TEST(parseTimeDurationTest); CPPUNIT_TEST_SUITE_END(); protected: @@ -123,6 +124,14 @@ class TimeConversionTest : public CPPUNIT_NS::TestFixture void durationToStringTest(void) throw (CPPUNIT_NS::Exception); + /** + * Test the parseTimeDuration() function. + * + * @exception CPPUNIT_NS::Exception on test failures. + */ + void + parseTimeDurationTest(void) throw (CPPUNIT_NS::Exception); + public: