// 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_TIMESTAMP_H_ #define WVCDM_UTIL_WV_TIMESTAMP_H_ #include #include #include "wv_class_utils.h" #include "wv_duration.h" namespace wvutil { // The Timestamp class is a light-weight representation of a // time point. // // Internally, the time is measured in Unix Time (milliseconds // since January 1st, 1970 UTC (epoch)). // // For this library, we are not concerned about time points before // January 1st, 1970; this class does not support negative epoch // seconds. In addition, zero is treated as a special value // which indicates an uninitialized time point. // // Min Timestamp: 1970-01-01 00:00:00.001 (epoch ms = 1) // Max Timestamp: 9999-12-31 23:59:59.999 (epoch ms = 253402300799999) class Timestamp { public: constexpr Timestamp() = default; // Defaults to "unset". WVCDM_CONSTEXPR_DEFAULT_COPY_AND_MOVE(Timestamp); // Create a Timestamp instance from Unix Time in epoch seconds. // Optionally allowed to include |millisecond| (0 to 999). // If |epoch_seconds| or |millisecond| are invalid values, then an // uninitlaized Timestamp instance is returned. static constexpr Timestamp FromUnixSeconds(uint64_t epoch_seconds, uint32_t milliseconds = 0) { if (milliseconds > 999u) return Timestamp(); return Timestamp(Seconds(epoch_seconds) + Milliseconds(milliseconds)); } static constexpr Timestamp FromUnixSeconds(const Seconds& epoch_seconds, uint32_t milliseconds = 0) { if (milliseconds > 999u) return Timestamp(); return Timestamp(epoch_seconds + Milliseconds(milliseconds)); } // Create a Timestamp instance from Unix Time in epoch milliseconds. // If |epoch_milliseconds| is an invalid value (zero), then an // uninitlaized Timestamp instance is returned. static constexpr Timestamp FromUnixMilliseconds(uint64_t epoch_milliseconds) { return Timestamp(Milliseconds(epoch_milliseconds)); } static constexpr Timestamp FromUnixMilliseconds( const Milliseconds& epoch_milliseconds) { return Timestamp(epoch_milliseconds); } // Obtain the minimum and maximum representable Timestamp. static constexpr Timestamp Min() { return Timestamp(kMinMsDuration); } static constexpr Timestamp Max() { return Timestamp(kMaxMsDuration); } // == General Accessors == // Get the epoch seconds duration (rounding down milliseconds). constexpr Seconds epoch_seconds() const { return std::chrono::floor(epoch_milliseconds_); } // Get the milliseconds fraction of the epoch time (0-999). constexpr uint32_t milliseconds() const { return static_cast( (epoch_milliseconds_ - epoch_seconds()).count()); } constexpr Milliseconds epoch_milliseconds() const { return epoch_milliseconds_; } // As noted, the timestamp is considered "unset" if zero. constexpr bool IsSet() const { return epoch_milliseconds_ > Milliseconds::zero(); } explicit constexpr operator bool() const { return IsSet(); } constexpr void Clear() { epoch_milliseconds_ = kUnsetMsDuration; } // == Comparisons == constexpr bool IsEqualTo(const Timestamp& other) const { return epoch_milliseconds_ == other.epoch_milliseconds_; } constexpr int64_t CompareTo(const Timestamp& other) const { return static_cast(epoch_milliseconds_.count() - other.epoch_milliseconds_.count()); } WVCDM_DEFINE_CONSTEXPR_EQ_AND_CMP_OPERATORS(Timestamp); // == Arithmetic Operations == // Duration-based addition/subtraction operations. // Returns a new Timestamp instance with time point adjusted by // the provided Duration. // If current Timestamp instance is unset, or if result value // is outside the range of valid Timestamp values, then an unset // Timestamp is returned. constexpr Timestamp operator+(const Duration& duration) const { if (!IsSet()) return Timestamp(); return Timestamp(epoch_milliseconds_ + duration.total_milliseconds()); } constexpr Timestamp operator-(const Duration& duration) const { if (!IsSet()) return Timestamp(); return Timestamp(epoch_milliseconds_ - duration.total_milliseconds()); } // Duration-based increment/decrement operators. // Increment or decrement the Timestamp by the provided Duration // amount. // If current Timestamp instance is unset, then no action will // occur. If result value is outside the range of valid Timestamp // values, then Timestamp will be unset. constexpr Timestamp& operator+=(const Duration& duration) { if (!IsSet()) return *this; epoch_milliseconds_ = Normalize(epoch_milliseconds_ + duration.total_milliseconds()); return *this; } constexpr Timestamp& operator-=(const Duration& duration) { if (!IsSet()) return *this; epoch_milliseconds_ = Normalize(epoch_milliseconds_ - duration.total_milliseconds()); return *this; } // Timestamp difference. // Returns the duration between the two provided Timestamp // instances. // If either Timestamp instance is unset, then the resulting // Duration is zero. constexpr Duration operator-(const Timestamp& other) const { if (!IsSet() || !other.IsSet()) return Duration(); return Duration(epoch_milliseconds_ - other.epoch_milliseconds_); } private: static constexpr const uint64_t kMinMs = 1; static constexpr const uint64_t kMaxMs = 253402300799999; static constexpr const Milliseconds kMinMsDuration = Milliseconds(kMinMs); static constexpr const Milliseconds kMaxMsDuration = Milliseconds(kMaxMs); static constexpr const Milliseconds kUnsetMsDuration = Milliseconds::zero(); static constexpr bool IsInRange(const Milliseconds& epoch_milliseconds) { return epoch_milliseconds >= kMinMsDuration && epoch_milliseconds <= kMaxMsDuration; } // Ensures the provided |epoch_milliseconds| is in range of the // Timestamp class; otherwise returns an unset duration value. static constexpr Milliseconds Normalize( const Milliseconds& epoch_milliseconds) { return IsInRange(epoch_milliseconds) ? epoch_milliseconds : kUnsetMsDuration; } constexpr Timestamp(const Milliseconds& epoch_milliseconds) : epoch_milliseconds_(Normalize(epoch_milliseconds)) {} Milliseconds epoch_milliseconds_ = kUnsetMsDuration; }; // class Timestamp } // namespace wvutil #endif // WVCDM_UTIL_WV_TIMESTAMP_H_