#ifndef CSHARP_TIME_HPP #define CSHARP_TIME_HPP #include <cstdint> #include <limits> #include <cmath> #include "exception.hpp" #include "sr.hpp" namespace csharp { struct TimeSpan { constexpr TimeSpan() = default; constexpr TimeSpan(int64_t ticks) : _ticks(ticks) {} constexpr TimeSpan(int32_t hours, int32_t minutes, int32_t seconds) { _ticks = TimeToTicks(hours, minutes, seconds); } constexpr TimeSpan(int32_t days, int32_t hours, int32_t minutes, int32_t seconds) : TimeSpan(days, hours, minutes, seconds, 0) { } constexpr TimeSpan(int32_t days, int32_t hours, int32_t minutes, int32_t seconds, int32_t milliseconds) : TimeSpan(days, hours, minutes, seconds, milliseconds, 0) { } constexpr TimeSpan(int32_t days, int32_t hours, int32_t minutes, int32_t seconds, int32_t milliseconds, int32_t microseconds) { const auto totalMicroseconds = (days * MicrosecondsPerDay) + (hours * MicrosecondsPerHour) + (minutes * MicrosecondsPerMinute) + (seconds * MicrosecondsPerSecond) + (milliseconds * MicrosecondsPerMillisecond) + microseconds; if ((totalMicroseconds > MaxMicroseconds) || (totalMicroseconds < MinMicroseconds)) { throw ArgumentOutOfRangeException(SR::Overflow_TimeSpanTooLong); } _ticks = totalMicroseconds * TicksPerMicrosecond; } static constexpr int64_t NanosecondsPerTick = 100; static constexpr int64_t TicksPerMicrosecond = 10; static constexpr int64_t TicksPerMillisecond = TicksPerMicrosecond * 1000; static constexpr int64_t TicksPerSecond = TicksPerMillisecond * 1000; static constexpr int64_t TicksPerMinute = TicksPerSecond * 60; static constexpr int64_t TicksPerHour = TicksPerMinute * 60; static constexpr int64_t TicksPerDay = TicksPerHour * 24; static constexpr int64_t MicrosecondsPerMillisecond = TicksPerMillisecond / TicksPerMicrosecond; static constexpr int64_t MicrosecondsPerSecond = TicksPerSecond / TicksPerMicrosecond; static constexpr int64_t MicrosecondsPerMinute = TicksPerMinute / TicksPerMicrosecond; static constexpr int64_t MicrosecondsPerHour = TicksPerHour / TicksPerMicrosecond; static constexpr int64_t MicrosecondsPerDay = TicksPerDay / TicksPerMicrosecond; static constexpr int64_t MillisecondsPerSecond = TicksPerSecond / TicksPerMillisecond; static constexpr int64_t MillisecondsPerMinute = TicksPerMinute / TicksPerMillisecond; static constexpr int64_t MillisecondsPerHour = TicksPerHour / TicksPerMillisecond; static constexpr int64_t MillisecondsPerDay = TicksPerDay / TicksPerMillisecond; static constexpr int64_t SecondsPerMinute = TicksPerMinute / TicksPerSecond; static constexpr int64_t SecondsPerHour = TicksPerHour / TicksPerSecond; static constexpr int64_t SecondsPerDay = TicksPerDay / TicksPerSecond; static constexpr int64_t MinutesPerHour = TicksPerHour / TicksPerMinute; static constexpr int64_t MinutesPerDay = TicksPerDay / TicksPerMinute; static constexpr int32_t HoursPerDay = static_cast<int32_t>(TicksPerDay / TicksPerHour); static constexpr int64_t MinTicks = (std::numeric_limits<int64_t>::min)(); static constexpr int64_t MaxTicks = (std::numeric_limits<int64_t>::max)(); static constexpr int64_t MinMicroseconds = MinTicks / TicksPerMicrosecond; static constexpr int64_t MaxMicroseconds = MaxTicks / TicksPerMicrosecond; static constexpr int64_t MinMilliseconds = MinTicks / TicksPerMillisecond; static constexpr int64_t MaxMilliseconds = MaxTicks / TicksPerMillisecond; static constexpr int64_t MinSeconds = MinTicks / TicksPerSecond; static constexpr int64_t MaxSeconds = MaxTicks / TicksPerSecond; static constexpr int64_t MinMinutes = MinTicks / TicksPerMinute; static constexpr int64_t MaxMinutes = MaxTicks / TicksPerMinute; static constexpr int64_t MinHours = MinTicks / TicksPerHour; static constexpr int64_t MaxHours = MaxTicks / TicksPerHour; static constexpr int64_t MinDays = MinTicks / TicksPerDay; static constexpr int64_t MaxDays = MaxTicks / TicksPerDay; static constexpr int64_t TicksPerTenthSecond = TicksPerMillisecond * 100; static constexpr TimeSpan Zero() { return TimeSpan(0); } static constexpr TimeSpan MaxValue() { return TimeSpan(MaxTicks); } static constexpr TimeSpan MinValue() { return TimeSpan(MinTicks); } constexpr int64_t Ticks() const { return _ticks; } constexpr int32_t Days() const { return static_cast<int32_t>(_ticks / TicksPerDay); } constexpr int32_t Hours() const { return static_cast<int32_t>(_ticks / TicksPerHour % HoursPerDay); } constexpr int32_t Milliseconds() const { return static_cast<int32_t>(_ticks / TicksPerMillisecond % MillisecondsPerSecond); } constexpr int32_t Microseconds() const { return static_cast<int32_t>(_ticks / TicksPerMicrosecond % MicrosecondsPerMillisecond); } constexpr int32_t Nanoseconds() const { return static_cast<int32_t>(_ticks % TicksPerMicrosecond * NanosecondsPerTick); } constexpr int32_t Minutes() const { return static_cast<int32_t>(_ticks / TicksPerMinute % MinutesPerHour); } constexpr int32_t Seconds() const { return static_cast<int32_t>(_ticks / TicksPerSecond % SecondsPerMinute); } constexpr double TotalDays() const { return static_cast<double>(_ticks) / TicksPerDay; } constexpr double TotalHours() const { return static_cast<double>(_ticks) / TicksPerHour; } constexpr double TotalMilliseconds() const { const double temp = static_cast<double>(_ticks) / TicksPerMillisecond; if (temp > MaxMilliseconds) { return MaxMilliseconds; } if (temp < MinMilliseconds) { return MinMilliseconds; } return temp; } constexpr double TotalMicroseconds() const { return static_cast<double>(_ticks) / TicksPerMicrosecond; } constexpr double TotalNanoseconds() const { return static_cast<double>(_ticks) * NanosecondsPerTick; } constexpr double TotalMinutes() const { return static_cast<double>(_ticks) / TicksPerMinute; } constexpr double TotalSeconds() const { return static_cast<double>(_ticks) / TicksPerSecond; } constexpr TimeSpan Add(TimeSpan const& ts) const { return *this + ts; } static constexpr TimeSpan FromDays(double value) { return Interval(value, TicksPerDay); } constexpr TimeSpan Duration() const { if (_ticks == MinTicks) { throw OverflowException(SR::Overflow_Duration); } return TimeSpan(_ticks >= 0 ? _ticks : -_ticks); } constexpr static int64_t TimeToTicks(int32_t hour, int32_t minute, int32_t second) { const auto totalSeconds = (hour * SecondsPerHour) + (minute * SecondsPerMinute) + second; if ((totalSeconds > MaxSeconds) || (totalSeconds < MinSeconds)) { throw ArgumentOutOfRangeException(SR::Overflow_TimeSpanTooLong); } return totalSeconds * TicksPerSecond; } static constexpr TimeSpan FromDays(int32_t days) { return FromUnits(days, TicksPerDay, MinDays, MaxDays); } static constexpr TimeSpan FromHours(int32_t hours) { return FromUnits(hours, TicksPerHour, MinHours, MaxHours); } static constexpr TimeSpan FromMinutes(int32_t minutes) { return FromUnits(minutes, TicksPerMinute, MinMinutes, MaxMinutes); } static constexpr TimeSpan FromSeconds(int32_t seconds) { return FromUnits(seconds, TicksPerSecond, MinSeconds, MaxSeconds); } static constexpr TimeSpan FromMicroseconds(int32_t microseconds) { return FromUnits(microseconds, TicksPerMicrosecond, MinMicroseconds, MaxMicroseconds); } static constexpr TimeSpan FromHours(double value) { return Interval(value, TicksPerHour); } static constexpr TimeSpan FromMilliseconds(double value) { return Interval(value, TicksPerMillisecond); } static constexpr TimeSpan FromMicroseconds(double value) { return Interval(value, TicksPerMicrosecond); } static constexpr TimeSpan FromMinutes(double value) { return Interval(value, TicksPerMinute); } constexpr TimeSpan Negate() const { return -(*this); } static constexpr TimeSpan FromSeconds(double value) { return Interval(value, TicksPerSecond); } constexpr TimeSpan Subtract(TimeSpan const& ts) const { return *this - ts; } TimeSpan Multiply(double factor) const { return *this * factor; } TimeSpan Divide(double divisor) const { return *this / divisor; } double Divide(TimeSpan const& ts) const { return *this / ts; } static constexpr TimeSpan FromTicks(int64_t value) { return TimeSpan(value); } constexpr TimeSpan operator-() const { if (_ticks == MinTicks) { throw OverflowException(SR::Overflow_NegateTwosCompNum); } return TimeSpan(_ticks); } friend constexpr TimeSpan operator-(TimeSpan const& t1, TimeSpan const t2) { const auto result = t1._ticks - t2._ticks; const auto t1Sign = t1._ticks >> 63; if ((t1Sign != (t2._ticks >> 63)) && (t1Sign != (result >> 63))) { throw OverflowException(SR::Overflow_TimeSpanTooLong); } return TimeSpan(result); } constexpr TimeSpan operator+() const { return *this; } friend constexpr TimeSpan operator+(TimeSpan const& t1, TimeSpan const t2) { long result = t1._ticks + t2._ticks; long t1Sign = t1._ticks >> 63; if ((t1Sign == (t2._ticks >> 63)) && (t1Sign != (result >> 63))) { throw OverflowException(SR::Overflow_TimeSpanTooLong); } return TimeSpan(result); } friend TimeSpan operator*(TimeSpan const& timeSpan, double factor) { const auto ticks = std::round(timeSpan.Ticks() * factor); return IntervalFromDoubleTicks(ticks); } friend TimeSpan operator*(double factor, TimeSpan const& timeSpan) { return timeSpan * factor; } friend TimeSpan operator/(TimeSpan const& timeSpan, double divisor) { const auto ticks = std::round(timeSpan.Ticks() / divisor); return IntervalFromDoubleTicks(ticks); } friend double operator/(TimeSpan const& t1, TimeSpan const t2) { return t1.Ticks() / static_cast<double>(t2.Ticks()); } constexpr bool operator==(TimeSpan const& other) const { return _ticks == other._ticks; } constexpr bool operator<(TimeSpan const& other) const { return _ticks < other._ticks; } constexpr bool operator<=(TimeSpan const& other) const { return _ticks <= other._ticks; } constexpr bool operator>(TimeSpan const& other) const { return _ticks > other._ticks; } constexpr bool operator>=(TimeSpan const& other) const { return _ticks >= other._ticks; } private: static constexpr TimeSpan FromUnits(int64_t units, int64_t ticksPerUnit, int64_t minUnits, int64_t maxUnits) { if (units > maxUnits || units < minUnits) { throw ArgumentOutOfRangeException(SR::Overflow_TimeSpanTooLong); } return TimeSpan::FromTicks(units * ticksPerUnit); } static constexpr TimeSpan Interval(double value, double scale) { return IntervalFromDoubleTicks(value * scale); } static constexpr TimeSpan IntervalFromDoubleTicks(double ticks) { if ((ticks > MaxTicks) || (ticks < MinTicks)) { throw OverflowException(SR::Overflow_TimeSpanTooLong); } if (ticks == MaxTicks) { return MaxValue(); } return TimeSpan(static_cast<int64_t>(ticks)); } private: int64_t _ticks{ 0 }; }; } #endif