// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine License // Agreement. #ifndef WVCDM_UTIL_WV_DURATION_H_ #define WVCDM_UTIL_WV_DURATION_H_ #include #include #include #include #include #include "wv_class_utils.h" namespace wvutil { // Wrappers around various std::chono::duration units. using Nanoseconds = std::chrono::nanoseconds; using Microseconds = std::chrono::microseconds; using Milliseconds = std::chrono::milliseconds; using Seconds = std::chrono::seconds; using Minutes = std::chrono::minutes; using Hours = std::chrono::hours; #if __cplusplus >= 202002L // C++20 using Days = std::chrono::days; #else // Days is not declared in C++17, the standard C++ library // uses the following implementation for C++20. using Days = std::chrono::duration >; #endif // A high-level duration struct for storing a duration and // easily accessing the components. // A duration measures a concrete amount of time between two // different points in time. // // Precision of Duration is in milliseconds. class Duration final { public: constexpr Duration() = default; // Initialize duration from some kind of chrono duration type that // can easily convert to milliseconds. // Compiler will not allow irreconcilable duration types to be used // without caller explicitly chrono::duration_cast. template constexpr Duration(const std::chrono::duration& total_duration) : total_milliseconds_(total_duration) {} WVCDM_CONSTEXPR_DEFAULT_COPY_AND_MOVE(Duration); private: // Note: This private section is needed here. // clang++ with -Wundefined-inline will complain about certain // inline members not being declared before use in other inline // methods. // Helper function for performing truncate (round towards zero) // operation on chrono::duration types. // The standard C++ chrono library does not provide such a function. template static constexpr ToDuration DurationTruncate( const std::chrono::duration& duration) { return duration >= std::chrono::duration::zero() ? std::chrono::floor(duration) : std::chrono::ceil(duration); } // Helper function for performing truncate on Duration class's // |total_milliseconds_|, returning it in a chrono::duration type // which is specified by template parameter |ToDuration|. template constexpr Duration GetTruncateInternal() const { return Duration(DurationTruncate(total_milliseconds_)); } public: // == Special Initializers == static constexpr Duration Zero() { return Duration(); } static constexpr Duration FromMilliseconds(int64_t milliseconds) { return Duration(Milliseconds(milliseconds)); } static constexpr Duration FromSeconds(int64_t seconds) { return Duration(Seconds(seconds)); } constexpr bool IsZero() const { return total_milliseconds_ == Milliseconds::zero(); } constexpr bool IsNegative() const { return total_milliseconds_ < Milliseconds::zero(); } constexpr bool IsPositive() const { return total_milliseconds_ > Milliseconds::zero(); } constexpr bool IsNonNegative() const { return total_milliseconds_ >= Milliseconds::zero(); } // Basic accessor and conversions (rounds down). constexpr Milliseconds total_milliseconds() const { return total_milliseconds_; } constexpr Seconds total_seconds() const { return DurationTruncate(total_milliseconds_); } constexpr Minutes total_minutes() const { return DurationTruncate(total_milliseconds_); } constexpr Hours total_hours() const { return DurationTruncate(total_milliseconds_); } constexpr Days total_days() const { return DurationTruncate(total_milliseconds_); } // Access duration absolute components bounded within a // particular unit. // Milliseconds modulo milliseconds per-second (0-999) constexpr uint32_t milliseconds() const { if (IsNegative()) return GetAbsolute().milliseconds(); return static_cast( (total_milliseconds() - total_seconds()).count()); } // Seconds modulo seconds per-minute (0-59) constexpr uint32_t seconds() const { if (IsNegative()) return GetAbsolute().seconds(); return static_cast((total_seconds() - total_minutes()).count()); } // Minutes modulo minutes per-hour (0-59). constexpr uint32_t minutes() const { if (IsNegative()) return GetAbsolute().minutes(); return static_cast((total_minutes() - total_hours()).count()); } // Hours modulo hours per-day (0-23) constexpr uint32_t hours() const { if (IsNegative()) return GetAbsolute().hours(); return static_cast((total_hours() - total_days()).count()); } // Days (total). constexpr uint32_t days() const { if (IsNegative()) return GetAbsolute().days(); return static_cast(total_days().count()); } // Obtain the absolute value. constexpr Duration GetAbsolute() const { return Duration(std::chrono::abs(total_milliseconds_)); } // Obtain the truncated (math term for rounding towards zero) // value by units. constexpr Duration GetTruncateBySeconds() const { return GetTruncateInternal(); } constexpr Duration GetTruncateByMinutes() const { return GetTruncateInternal(); } constexpr Duration GetTruncateByHours() const { return GetTruncateInternal(); } constexpr Duration GetTruncateByDays() const { return GetTruncateInternal(); } // Comparison operators (between another Duration class) constexpr bool IsEqualTo(const Duration& other) const { return total_milliseconds_ == other.total_milliseconds_; } constexpr int64_t CompareTo(const Duration& other) const { return static_cast(total_milliseconds_.count() - other.total_milliseconds_.count()); } WVCDM_DEFINE_CONSTEXPR_EQ_AND_CMP_OPERATORS(Duration); // Comparison operators (between other duration types) template constexpr bool operator==( const std::chrono::duration& other) const { return total_milliseconds_ == other; } template constexpr bool operator!=( const std::chrono::duration& other) const { return total_milliseconds_ != other; } template constexpr bool operator<=( const std::chrono::duration& other) const { return total_milliseconds_ <= other; } template constexpr bool operator<( const std::chrono::duration& other) const { return total_milliseconds_ < other; } template constexpr bool operator>=( const std::chrono::duration& other) const { return total_milliseconds_ >= other; } template constexpr bool operator>( const std::chrono::duration& other) const { return total_milliseconds_ > other; } // Unary plus/minus operators constexpr Duration operator+() const { return Duration(total_milliseconds_); } constexpr Duration operator-() const { return Duration(-total_milliseconds_); } // Add/subtract operators (between another Duration class) constexpr Duration operator+(const Duration& other) const { return Duration(total_milliseconds_ + other.total_milliseconds_); } constexpr Duration operator-(const Duration& other) const { return Duration(total_milliseconds_ - other.total_milliseconds_); } // Add/subtract operators (between other duration types) // Note 1: This is intentionally implemented as a member function to // force the caller the put the Duration class first in the // expression. // Note 2: These operators are not defined for duration types that // smaller than chrono::milliseconds. This could cause // unexpected behavior as operations would be truncated. // Ex: (Duration(5s) + 500us + 500us) = Duration(5s) // Compiler will not allow such operations. template constexpr Duration operator+( const std::chrono::duration& other) const { return Duration(total_milliseconds_ + other); } template constexpr Duration operator-( const std::chrono::duration& other) const { return Duration(total_milliseconds_ - other); } // Increment/decrement operators (between another Duration class) constexpr Duration& operator+=(const Duration& other) { total_milliseconds_ += other.total_milliseconds_; return *this; } constexpr Duration& operator-=(const Duration& other) { total_milliseconds_ -= other.total_milliseconds_; return *this; } // Increment/decrement operators (between another duration types) // Note: These operators are not defined for duration types that // smaller than chrono::milliseconds (see '+' operator // comment for details). template constexpr Duration& operator+=( const std::chrono::duration& other) { total_milliseconds_ += other; return *this; } template constexpr Duration& operator-=( const std::chrono::duration& other) { total_milliseconds_ -= other; return *this; } // To string operator. // Converts the Duration to its string representation. // Examples: // Duration::FromMilliseconds(4000123).ToString() // ==> "1h6m40s123ms" // Duration::FromSeconds(4000).ToString() // ==> "1h6m40s" bool PrintTo(std::ostream* out) const; std::string ToString() const; private: // Internally, the Duration class stores durations in milliseconds. Milliseconds total_milliseconds_ = Milliseconds::zero(); }; // class Duration // == GTest Printer == void PrintTo(const Duration& duration, std::ostream* out); } // namespace wvutil #endif // WVCDM_UTIL_WV_DURATION_H_