// 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_DATE_TIME_H_ #define WVCDM_UTIL_DATE_TIME_H_ #include #include #include #include #include #include "wv_class_utils.h" #include "wv_duration.h" #include "wv_timestamp.h" namespace wvutil { // The DateTime class represents a time point measured in UTC // Gregorian calendar dates and 24-hour timekeeping system. // // Internally, the time is measured in Unix Time (milliseconds // since January 1st, 1970 (epoch)). // // For the CDM, 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 DateTime: 1970-01-01 00:00:00.001 (epoch ms = 1) // Max DateTime: 9999-12-31 23:59:59.999 (epoch ms = 253402300799999) // // Converting from Unix Time to datetime components is a potentially // expensive operation for their amount of utility; to reduce this // overhead date components are calculated once per change, and stored // individually. // // For testing, use the DateTime::Builder class for create DateTime // instances from their individual components. // // The DateTime class provides a PrintTo()/ToString() method which // returns an initialized DateTime in ISO 8601 format without timezone // indicator and with milliseconds only being printed if non-zero. // Use DateTime::Formatter for specific formatting needs. class DateTime { public: constexpr DateTime() = default; // Uninitialized DateTime. WVCDM_CONSTEXPR_DEFAULT_COPY_AND_MOVE(DateTime); 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. template constexpr ToDuration EpochFloor() const { return std::chrono::floor(epoch_milliseconds()); } public: static DateTime FromTimestamp(const Timestamp& timestamp); // Create a DateTime 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 DateTime instance is returned. static DateTime FromUnixSeconds(uint64_t epoch_seconds, uint32_t milliseconds = 0) { return FromTimestamp( Timestamp::FromUnixSeconds(epoch_seconds, milliseconds)); } static DateTime FromUnixSeconds(const Seconds& epoch_seconds, uint32_t milliseconds = 0) { return FromTimestamp( Timestamp::FromUnixSeconds(epoch_seconds, milliseconds)); } // Create a DateTime instance from Unix Time in epoch milliseconds. // If |epoch_milliseconds| is an invalid value (zero), then an // uninitlaized DateTime instance is returned. static DateTime FromUnixMilliseconds(uint64_t epoch_milliseconds) { return FromTimestamp(Timestamp::FromUnixMilliseconds(epoch_milliseconds)); } static DateTime FromUnixMilliseconds(const Milliseconds& epoch_milliseconds) { return FromTimestamp(Timestamp::FromUnixMilliseconds(epoch_milliseconds)); } // Obtain the minimum and maximum representable DateTime. static DateTime Min() { return FromTimestamp(Timestamp::Min()); } static DateTime Max() { return FromTimestamp(Timestamp::Max()); } constexpr const Timestamp& timestamp() const { return timestamp_; } // == Epoch Accessors == constexpr Seconds epoch_seconds() const { return timestamp_.epoch_seconds(); } constexpr Milliseconds epoch_milliseconds() const { return timestamp_.epoch_milliseconds(); } // == Component Accessors == // Year on Gregorian calendar (1970 - 9999; 0 if unset) constexpr uint32_t year() const { return year_; } // Month of the year (January = 1, December = 12; 0 if unset) constexpr uint32_t month() const { return month_; } // Day of the month (1 - 28/29/30/31; 0 if unset) constexpr uint32_t day() const { return day_; } // Day of the year (1 - 365/366; 0 if unset) constexpr uint32_t day_of_year() const { return day_of_year_; } // Day of the week (1 (Sunday) - 7 (Saturday); 0 if unset) constexpr uint32_t day_of_week() const { return day_of_week_; } // Time component accessors. // Hour of day (0 - 23; 0 if unset) constexpr uint32_t hour() const { if (!IsSet()) return 0; return static_cast( (EpochFloor() - EpochFloor()).count()); } // Minute of hour (0 - 59; 0 if unset) constexpr uint32_t minute() const { if (!IsSet()) return 0; return static_cast( (EpochFloor() - EpochFloor()).count()); } // Second of minute (0 - 59; 0 if unset) constexpr uint32_t second() const { if (!IsSet()) return 0; return static_cast( (EpochFloor() - EpochFloor()).count()); } // Millisecond of second (0 - 999; 0 if unset) constexpr uint32_t millisecond() const { return timestamp_.milliseconds(); } // Checks if the DateTime instance is set. constexpr bool IsSet() const { return timestamp_.IsSet(); } constexpr explicit operator bool() const { return IsSet(); } constexpr void Clear() { timestamp_.Clear(); ClearComponents(); } // == Comparison Operators == constexpr bool IsEqualTo(const DateTime& other) const { return timestamp_.IsEqualTo(other.timestamp_); } constexpr int64_t CompareTo(const DateTime& other) const { return timestamp_.CompareTo(other.timestamp_); } WVCDM_DEFINE_CONSTEXPR_EQ_AND_CMP_OPERATORS(DateTime); constexpr bool IsEqualTo(const Timestamp& other) const { return timestamp_.IsEqualTo(other); } constexpr int64_t CompareTo(const Timestamp& other) const { return timestamp_.CompareTo(other); } WVCDM_DEFINE_CONSTEXPR_EQ_AND_CMP_OPERATORS(Timestamp); // == Duration Operators == // Duration-based addition/subtraction operations. // Returns a new DateTime instance with time point adjusted by // the provided Duration. // If current DateTime instance is unset, or if result value // is outside the range of valid DateTime values, then an unset // DateTime is returned. DateTime operator+(const Duration& duration) const; DateTime operator-(const Duration& duration) const; // DateTime difference. // Returns the duration between the two provided DateTime // instances. // If either DateTime instance is unset, then the resulting // Duration is zero. Duration operator-(const DateTime& other) const; // Duration-based increment/decrement operators. // Increment or decrement the DateTime by the provided Duration // amount. // If current DateTime instance is unset, then no action will // occur. If result value is outside the range of valid DateTime // values, then DateTime will be unset. DateTime& operator+=(const Duration& duration); DateTime& operator-=(const Duration& duration); // For initialized DateTime instances, returns the datetime // in ISO 8601 format. Milliseconds are only printed // if non-zero. // An uninitialized DateTime will return an empty string. // Ex: // Without milli: 2024-01-19T14:49:13Z // With milli: 2024-01-19T14:49:13.507Z // Use DateTime::Formatter for more formatting options. bool PrintTo(std::ostream* out) const; std::string ToString() const; private: // Special constructor. explicit DateTime(const Timestamp& timestamp); // Attempts to calculate subcomponents. // Failure to calculate subcomponents will clear |timestamp_|. void UpdateTimestamp(const Timestamp& new_timestamp); constexpr void ClearComponents() { year_ = month_ = day_ = day_of_year_ = day_of_week_ = 0; } // Source of truth for DateTime class. Timestamp timestamp_; // Components (derived from |timestamp_|). uint16_t year_ = 0; uint8_t month_ = 0; uint8_t day_ = 0; uint16_t day_of_year_ = 0; uint8_t day_of_week_ = 0; }; // class DateTime // == GTest Printer == void PrintTo(const DateTime& date_time, std::ostream* out); } // namespace wvutil #endif // WVCDM_UTIL_DATE_TIME_H_