From 3bc9a7b07a6e02c28fbcc440af6c36802244d619 Mon Sep 17 00:00:00 2001 From: Martchus Date: Sun, 25 Jun 2017 15:09:16 +0200 Subject: [PATCH] Fix handling negative time spans --- chrono/datetime.cpp | 40 ++++++++++++++++------------------ chrono/datetime.h | 14 ++++++------ chrono/timespan.cpp | 52 +++++++++++++++++++++++++++------------------ chrono/timespan.h | 38 ++++++++++++++++++++------------- 4 files changed, 79 insertions(+), 65 deletions(-) diff --git a/chrono/datetime.cpp b/chrono/datetime.cpp index f8d4ac0..205e2d2 100644 --- a/chrono/datetime.cpp +++ b/chrono/datetime.cpp @@ -69,7 +69,7 @@ DateTime DateTime::fromTimeStamp(time_t timeStamp) */ DateTime DateTime::fromTimeStampGmt(time_t timeStamp) { - return DateTime(DateTime::unixEpochStart().totalTicks() + static_cast(timeStamp) * TimeSpan::ticksPerSecond); + return DateTime(DateTime::unixEpochStart().totalTicks() + static_cast(timeStamp) * TimeSpan::m_ticksPerSecond); } /*! @@ -287,7 +287,7 @@ DateTime DateTime::exactGmtNow() struct timespec t; clock_gettime(CLOCK_REALTIME, &t); return DateTime( - DateTime::unixEpochStart().totalTicks() + static_cast(t.tv_sec) * TimeSpan::ticksPerSecond + static_cast(t.tv_nsec) / 100); + DateTime::unixEpochStart().totalTicks() + static_cast(t.tv_sec) * TimeSpan::m_ticksPerSecond + static_cast(t.tv_nsec) / 100); } #endif @@ -296,25 +296,21 @@ DateTime DateTime::exactGmtNow() */ uint64 DateTime::dateToTicks(int year, int month, int day) { - if (inRangeInclMax(year, 1, 9999)) { - if (inRangeInclMax(month, 1, 12)) { - const int *daysToMonth = isLeapYear(year) ? m_daysToMonth366 : m_daysToMonth365; - int passedMonth = month - 1; - if (inRangeInclMax(day, 1, daysToMonth[month] - daysToMonth[passedMonth])) { - int passedYears = year - 1; - int passedDays = day - 1; - return (passedYears * m_daysPerYear + passedYears / 4 - passedYears / 100 + passedYears / 400 + daysToMonth[passedMonth] + passedDays) - * TimeSpan::ticksPerDay; - } else { - throw ConversionException("day is out of range"); - } - } else { - throw ConversionException("month is out of range"); - } - } else { + if (!inRangeInclMax(year, 1, 9999)) { throw ConversionException("year is out of range"); } - return 0; + if (!inRangeInclMax(month, 1, 12)) { + throw ConversionException("month is out of range"); + } + const int *daysToMonth = isLeapYear(year) ? m_daysToMonth366 : m_daysToMonth365; + int passedMonth = month - 1; + if (!inRangeInclMax(day, 1, daysToMonth[month] - daysToMonth[passedMonth])) { + throw ConversionException("day is out of range"); + } + int passedYears = year - 1; + int passedDays = day - 1; + return (passedYears * m_daysPerYear + passedYears / 4 - passedYears / 100 + passedYears / 400 + daysToMonth[passedMonth] + passedDays) + * TimeSpan::m_ticksPerDay; } /*! @@ -334,8 +330,8 @@ uint64 DateTime::timeToTicks(int hour, int minute, int second, double millisecon if (!inRangeExclMax(millisecond, 0.0, 1000.0)) { throw ConversionException("millisecond is out of range"); } - return (hour * TimeSpan::ticksPerHour) + (minute * TimeSpan::ticksPerMinute) + (second * TimeSpan::ticksPerSecond) - + (uint64)(millisecond * (double)TimeSpan::ticksPerMillisecond); + return (hour * TimeSpan::m_ticksPerHour) + (minute * TimeSpan::m_ticksPerMinute) + (second * TimeSpan::m_ticksPerSecond) + + (uint64)(millisecond * (double)TimeSpan::m_ticksPerMillisecond); } /*! @@ -344,7 +340,7 @@ uint64 DateTime::timeToTicks(int hour, int minute, int second, double millisecon */ int DateTime::getDatePart(DatePart part) const { - int fullDays = m_ticks / TimeSpan::ticksPerDay; + int fullDays = m_ticks / TimeSpan::m_ticksPerDay; int full400YearBlocks = fullDays / m_daysPer400Years; int daysMinusFull400YearBlocks = fullDays - full400YearBlocks * m_daysPer400Years; int full100YearBlocks = daysMinusFull400YearBlocks / m_daysPer100Years; diff --git a/chrono/datetime.h b/chrono/datetime.h index ca02fb6..ebd223e 100644 --- a/chrono/datetime.h +++ b/chrono/datetime.h @@ -243,7 +243,7 @@ inline int DateTime::dayOfYear() const */ constexpr inline DayOfWeek DateTime::dayOfWeek() const { - return static_cast((m_ticks / TimeSpan::ticksPerDay) % 7l); + return static_cast((m_ticks / TimeSpan::m_ticksPerDay) % 7l); } /*! @@ -251,7 +251,7 @@ constexpr inline DayOfWeek DateTime::dayOfWeek() const */ constexpr inline int DateTime::hour() const { - return m_ticks / TimeSpan::ticksPerHour % 24ul; + return m_ticks / TimeSpan::m_ticksPerHour % 24ul; } /*! @@ -259,7 +259,7 @@ constexpr inline int DateTime::hour() const */ constexpr inline int DateTime::minute() const { - return m_ticks / TimeSpan::ticksPerMinute % 60ul; + return m_ticks / TimeSpan::m_ticksPerMinute % 60ul; } /*! @@ -267,7 +267,7 @@ constexpr inline int DateTime::minute() const */ constexpr inline int DateTime::second() const { - return m_ticks / TimeSpan::ticksPerSecond % 60ul; + return m_ticks / TimeSpan::m_ticksPerSecond % 60ul; } /*! @@ -275,7 +275,7 @@ constexpr inline int DateTime::second() const */ constexpr inline int DateTime::millisecond() const { - return m_ticks / TimeSpan::ticksPerMillisecond % 1000ul; + return m_ticks / TimeSpan::m_ticksPerMillisecond % 1000ul; } /*! @@ -292,7 +292,7 @@ constexpr inline bool DateTime::isNull() const */ constexpr inline TimeSpan DateTime::timeOfDay() const { - return TimeSpan(m_ticks % TimeSpan::ticksPerDay); + return TimeSpan(m_ticks % TimeSpan::m_ticksPerDay); } /*! @@ -332,7 +332,7 @@ inline int DateTime::daysInMonth(int year, int month) */ constexpr inline bool DateTime::isSameDay(const DateTime &other) const { - return (m_ticks / TimeSpan::ticksPerDay) == (other.m_ticks / TimeSpan::ticksPerDay); + return (m_ticks / TimeSpan::m_ticksPerDay) == (other.m_ticks / TimeSpan::m_ticksPerDay); } /*! diff --git a/chrono/timespan.cpp b/chrono/timespan.cpp index f077b03..0b00a96 100644 --- a/chrono/timespan.cpp +++ b/chrono/timespan.cpp @@ -22,6 +22,10 @@ using namespace ConversionUtilities; */ TimeSpan TimeSpan::fromString(const char *str, char separator) { + if (!*str) { + return TimeSpan(); + } + vector parts; size_t partsSize = 1; for (const char *i = str; *i; ++i) { @@ -42,8 +46,6 @@ TimeSpan TimeSpan::fromString(const char *str, char separator) } switch (parts.size()) { - case 0: - return TimeSpan(); case 1: return TimeSpan::fromSeconds(parts.front()); case 2: @@ -79,32 +81,40 @@ string TimeSpan::toString(TimeSpanOutputFormat format, bool noMilliseconds) cons void TimeSpan::toString(string &result, TimeSpanOutputFormat format, bool noMilliseconds) const { stringstream s(stringstream::in | stringstream::out); - if (isNegative()) - s << "- "; + TimeSpan positive(m_ticks); + if (positive.isNegative()) { + s << '-'; + positive.m_ticks = -positive.m_ticks; + } switch (format) { case TimeSpanOutputFormat::Normal: - s << setfill('0') << setw(2) << floor(fabs(totalHours())) << ":" << setw(2) << minutes() << ":" << setw(2) << seconds() << " "; + s << setfill('0') << setw(2) << floor(positive.totalHours()) << ":" << setw(2) << positive.minutes() << ":" << setw(2) << positive.seconds() + << " "; break; case TimeSpanOutputFormat::WithMeasures: if (isNull()) { s << "0 s "; - } else if (totalMilliseconds() < 1.0) { - s << setprecision(2) << (m_ticks / 10.0) << " µs "; } else { - if (days()) { - s << days() << " d "; - } - if (hours()) { - s << hours() << " h "; - } - if (minutes()) { - s << minutes() << " min "; - } - if (seconds()) { - s << seconds() << " s "; - } - if (!noMilliseconds && milliseconds()) { - s << milliseconds() << " ms "; + if (positive.totalMilliseconds() < 1.0) { + s << setprecision(2) << (m_ticks / 10.0) << " µs "; + } else { + if (const int days = positive.days()) { + s << days << " d "; + } + if (const int hours = positive.hours()) { + s << hours << " h "; + } + if (const int minutes = positive.minutes()) { + s << minutes << " min "; + } + if (const int seconds = positive.seconds()) { + s << seconds << " s "; + } + if (!noMilliseconds) { + if (const int milliseconds = positive.milliseconds()) { + s << milliseconds << " ms "; + } + } } } break; diff --git a/chrono/timespan.h b/chrono/timespan.h index 6e799e5..4994766 100644 --- a/chrono/timespan.h +++ b/chrono/timespan.h @@ -72,12 +72,20 @@ public: constexpr bool isNegativeInfinity() const; constexpr bool isInfinity() const; + // TODO: make those public constants signed in next major release and remove private ones then static constexpr uint64 ticksPerMillisecond = 10000uL; static constexpr uint64 ticksPerSecond = 10000000uL; static constexpr uint64 ticksPerMinute = 600000000uL; static constexpr uint64 ticksPerHour = 36000000000uL; static constexpr uint64 ticksPerDay = 864000000000uL; +private: + static constexpr int64 m_ticksPerMillisecond = 10000L; + static constexpr int64 m_ticksPerSecond = 10000000L; + static constexpr int64 m_ticksPerMinute = 600000000L; + static constexpr int64 m_ticksPerHour = 36000000000L; + static constexpr int64 m_ticksPerDay = 864000000000L; + private: int64 m_ticks; }; @@ -103,7 +111,7 @@ constexpr inline TimeSpan::TimeSpan(int64 ticks) */ constexpr inline TimeSpan TimeSpan::fromMilliseconds(double milliseconds) { - return TimeSpan(static_cast(milliseconds * static_cast(ticksPerMillisecond))); + return TimeSpan(static_cast(milliseconds * static_cast(m_ticksPerMillisecond))); } /*! @@ -111,7 +119,7 @@ constexpr inline TimeSpan TimeSpan::fromMilliseconds(double milliseconds) */ constexpr inline TimeSpan TimeSpan::fromSeconds(double seconds) { - return TimeSpan(static_cast(seconds * static_cast(ticksPerSecond))); + return TimeSpan(static_cast(seconds * static_cast(m_ticksPerSecond))); } /*! @@ -119,7 +127,7 @@ constexpr inline TimeSpan TimeSpan::fromSeconds(double seconds) */ constexpr inline TimeSpan TimeSpan::fromMinutes(double minutes) { - return TimeSpan(static_cast(minutes * static_cast(ticksPerMinute))); + return TimeSpan(static_cast(minutes * static_cast(m_ticksPerMinute))); } /*! @@ -127,7 +135,7 @@ constexpr inline TimeSpan TimeSpan::fromMinutes(double minutes) */ constexpr inline TimeSpan TimeSpan::fromHours(double hours) { - return TimeSpan(static_cast(hours * static_cast(ticksPerHour))); + return TimeSpan(static_cast(hours * static_cast(m_ticksPerHour))); } /*! @@ -135,7 +143,7 @@ constexpr inline TimeSpan TimeSpan::fromHours(double hours) */ constexpr inline TimeSpan TimeSpan::fromDays(double days) { - return TimeSpan(static_cast(days * static_cast(ticksPerDay))); + return TimeSpan(static_cast(days * static_cast(m_ticksPerDay))); } /*! @@ -175,7 +183,7 @@ constexpr inline int64 TimeSpan::totalTicks() const */ constexpr inline double TimeSpan::totalMilliseconds() const { - return static_cast(m_ticks) / static_cast(ticksPerMillisecond); + return static_cast(m_ticks) / static_cast(m_ticksPerMillisecond); } /*! @@ -183,7 +191,7 @@ constexpr inline double TimeSpan::totalMilliseconds() const */ constexpr inline double TimeSpan::totalSeconds() const { - return static_cast(m_ticks) / static_cast(ticksPerSecond); + return static_cast(m_ticks) / static_cast(m_ticksPerSecond); } /*! @@ -191,7 +199,7 @@ constexpr inline double TimeSpan::totalSeconds() const */ constexpr inline double TimeSpan::totalMinutes() const { - return static_cast(m_ticks) / static_cast(ticksPerMinute); + return static_cast(m_ticks) / static_cast(m_ticksPerMinute); } /*! @@ -199,7 +207,7 @@ constexpr inline double TimeSpan::totalMinutes() const */ constexpr inline double TimeSpan::totalHours() const { - return static_cast(m_ticks) / static_cast(ticksPerHour); + return static_cast(m_ticks) / static_cast(m_ticksPerHour); } /*! @@ -207,7 +215,7 @@ constexpr inline double TimeSpan::totalHours() const */ constexpr inline double TimeSpan::totalDays() const { - return static_cast(m_ticks) / static_cast(ticksPerDay); + return static_cast(m_ticks) / static_cast(m_ticksPerDay); } /*! @@ -215,7 +223,7 @@ constexpr inline double TimeSpan::totalDays() const */ constexpr inline int TimeSpan::milliseconds() const { - return (m_ticks / ticksPerMillisecond) % 1000l; + return (m_ticks / m_ticksPerMillisecond) % 1000l; } /*! @@ -223,7 +231,7 @@ constexpr inline int TimeSpan::milliseconds() const */ constexpr inline int TimeSpan::seconds() const { - return (m_ticks / ticksPerSecond) % 60l; + return (m_ticks / m_ticksPerSecond) % 60l; } /*! @@ -231,7 +239,7 @@ constexpr inline int TimeSpan::seconds() const */ constexpr inline int TimeSpan::minutes() const { - return (m_ticks / ticksPerMinute) % 60l; + return (m_ticks / m_ticksPerMinute) % 60l; } /*! @@ -239,7 +247,7 @@ constexpr inline int TimeSpan::minutes() const */ constexpr inline int TimeSpan::hours() const { - return (m_ticks / ticksPerHour) % 24l; + return (m_ticks / m_ticksPerHour) % 24l; } /*! @@ -247,7 +255,7 @@ constexpr inline int TimeSpan::hours() const */ constexpr inline int TimeSpan::days() const { - return (m_ticks / ticksPerDay); + return (m_ticks / m_ticksPerDay); } /*!