// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/time/time.h" #include #include #include #include #include #include "base/logging.h" #include "base/macros.h" #include "base/no_destructor.h" #include "base/strings/stringprintf.h" #include "base/third_party/nspr/prtime.h" #include "base/time/time_override.h" #include "build/build_config.h" namespace base { namespace internal { TimeNowFunction g_time_now_function = &subtle::TimeNowIgnoringOverride; TimeNowFunction g_time_now_from_system_time_function = &subtle::TimeNowFromSystemTimeIgnoringOverride; TimeTicksNowFunction g_time_ticks_now_function = &subtle::TimeTicksNowIgnoringOverride; ThreadTicksNowFunction g_thread_ticks_now_function = &subtle::ThreadTicksNowIgnoringOverride; } // namespace internal // TimeDelta ------------------------------------------------------------------ int TimeDelta::InDays() const { if (is_max()) { // Preserve max to prevent overflow. return std::numeric_limits::max(); } return static_cast(delta_ / Time::kMicrosecondsPerDay); } int TimeDelta::InDaysFloored() const { if (is_max()) { // Preserve max to prevent overflow. return std::numeric_limits::max(); } int result = delta_ / Time::kMicrosecondsPerDay; int64_t remainder = delta_ - (result * Time::kMicrosecondsPerDay); if (remainder < 0) { --result; // Use floor(), not trunc() rounding behavior. } return result; } int TimeDelta::InHours() const { if (is_max()) { // Preserve max to prevent overflow. return std::numeric_limits::max(); } return static_cast(delta_ / Time::kMicrosecondsPerHour); } int TimeDelta::InMinutes() const { if (is_max()) { // Preserve max to prevent overflow. return std::numeric_limits::max(); } return static_cast(delta_ / Time::kMicrosecondsPerMinute); } double TimeDelta::InSecondsF() const { if (is_max()) { // Preserve max to prevent overflow. return std::numeric_limits::infinity(); } return static_cast(delta_) / Time::kMicrosecondsPerSecond; } int64_t TimeDelta::InSeconds() const { if (is_max()) { // Preserve max to prevent overflow. return std::numeric_limits::max(); } return delta_ / Time::kMicrosecondsPerSecond; } double TimeDelta::InMillisecondsF() const { if (is_max()) { // Preserve max to prevent overflow. return std::numeric_limits::infinity(); } return static_cast(delta_) / Time::kMicrosecondsPerMillisecond; } int64_t TimeDelta::InMilliseconds() const { if (is_max()) { // Preserve max to prevent overflow. return std::numeric_limits::max(); } return delta_ / Time::kMicrosecondsPerMillisecond; } int64_t TimeDelta::InMillisecondsRoundedUp() const { if (is_max()) { // Preserve max to prevent overflow. return std::numeric_limits::max(); } int64_t result = delta_ / Time::kMicrosecondsPerMillisecond; int64_t remainder = delta_ - (result * Time::kMicrosecondsPerMillisecond); if (remainder > 0) { ++result; // Use ceil(), not trunc() rounding behavior. } return result; } int64_t TimeDelta::InMicroseconds() const { if (is_max()) { // Preserve max to prevent overflow. return std::numeric_limits::max(); } return delta_; } double TimeDelta::InMicrosecondsF() const { if (is_max()) { // Preserve max to prevent overflow. return std::numeric_limits::infinity(); } return static_cast(delta_); } int64_t TimeDelta::InNanoseconds() const { if (is_max()) { // Preserve max to prevent overflow. return std::numeric_limits::max(); } return delta_ * Time::kNanosecondsPerMicrosecond; } namespace time_internal { int64_t SaturatedAdd(TimeDelta delta, int64_t value) { CheckedNumeric rv(delta.delta_); rv += value; if (rv.IsValid()) return rv.ValueOrDie(); // Positive RHS overflows. Negative RHS underflows. if (value < 0) return std::numeric_limits::min(); return std::numeric_limits::max(); } int64_t SaturatedSub(TimeDelta delta, int64_t value) { CheckedNumeric rv(delta.delta_); rv -= value; if (rv.IsValid()) return rv.ValueOrDie(); // Negative RHS overflows. Positive RHS underflows. if (value < 0) return std::numeric_limits::max(); return std::numeric_limits::min(); } } // namespace time_internal std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) { return os << time_delta.InSecondsF() << " s"; } // Time ----------------------------------------------------------------------- // static Time Time::Now() { return internal::g_time_now_function(); } // static Time Time::NowFromSystemTime() { // Just use g_time_now_function because it returns the system time. return internal::g_time_now_from_system_time_function(); } // static Time Time::FromDeltaSinceWindowsEpoch(TimeDelta delta) { return Time(delta.InMicroseconds()); } TimeDelta Time::ToDeltaSinceWindowsEpoch() const { return TimeDelta::FromMicroseconds(us_); } // static Time Time::FromTimeT(time_t tt) { if (tt == 0) return Time(); // Preserve 0 so we can tell it doesn't exist. if (tt == std::numeric_limits::max()) return Max(); return Time(kTimeTToMicrosecondsOffset) + TimeDelta::FromSeconds(tt); } time_t Time::ToTimeT() const { if (is_null()) return 0; // Preserve 0 so we can tell it doesn't exist. if (is_max()) { // Preserve max without offset to prevent overflow. return std::numeric_limits::max(); } if (std::numeric_limits::max() - kTimeTToMicrosecondsOffset <= us_) { DLOG(WARNING) << "Overflow when converting base::Time with internal " << "value " << us_ << " to time_t."; return std::numeric_limits::max(); } return (us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerSecond; } // static Time Time::FromDoubleT(double dt) { if (dt == 0 || std::isnan(dt)) return Time(); // Preserve 0 so we can tell it doesn't exist. return Time(kTimeTToMicrosecondsOffset) + TimeDelta::FromSecondsD(dt); } double Time::ToDoubleT() const { if (is_null()) return 0; // Preserve 0 so we can tell it doesn't exist. if (is_max()) { // Preserve max without offset to prevent overflow. return std::numeric_limits::infinity(); } return (static_cast(us_ - kTimeTToMicrosecondsOffset) / static_cast(kMicrosecondsPerSecond)); } #if defined(OS_POSIX) // static Time Time::FromTimeSpec(const timespec& ts) { return FromDoubleT(ts.tv_sec + static_cast(ts.tv_nsec) / base::Time::kNanosecondsPerSecond); } #endif // static Time Time::FromJsTime(double ms_since_epoch) { // The epoch is a valid time, so this constructor doesn't interpret // 0 as the null time. return Time(kTimeTToMicrosecondsOffset) + TimeDelta::FromMillisecondsD(ms_since_epoch); } double Time::ToJsTime() const { if (is_null()) { // Preserve 0 so the invalid result doesn't depend on the platform. return 0; } if (is_max()) { // Preserve max without offset to prevent overflow. return std::numeric_limits::infinity(); } return (static_cast(us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerMillisecond); } Time Time::FromJavaTime(int64_t ms_since_epoch) { return base::Time::UnixEpoch() + base::TimeDelta::FromMilliseconds(ms_since_epoch); } int64_t Time::ToJavaTime() const { if (is_null()) { // Preserve 0 so the invalid result doesn't depend on the platform. return 0; } if (is_max()) { // Preserve max without offset to prevent overflow. return std::numeric_limits::max(); } return ((us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerMillisecond); } // static Time Time::UnixEpoch() { Time time; time.us_ = kTimeTToMicrosecondsOffset; return time; } Time Time::Midnight(bool is_local) const { Exploded exploded; Explode(is_local, &exploded); exploded.hour = 0; exploded.minute = 0; exploded.second = 0; exploded.millisecond = 0; Time out_time; if (FromExploded(is_local, exploded, &out_time)) return out_time; // This function must not fail. NOTREACHED(); return Time(); } // static bool Time::FromStringInternal(const char* time_string, bool is_local, Time* parsed_time) { DCHECK((time_string != nullptr) && (parsed_time != nullptr)); if (time_string[0] == '\0') return false; PRTime result_time = 0; PRStatus result = PR_ParseTimeString(time_string, is_local ? PR_FALSE : PR_TRUE, &result_time); if (PR_SUCCESS != result) return false; result_time += kTimeTToMicrosecondsOffset; *parsed_time = Time(result_time); return true; } // static bool Time::ExplodedMostlyEquals(const Exploded& lhs, const Exploded& rhs) { return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day_of_month == rhs.day_of_month && lhs.hour == rhs.hour && lhs.minute == rhs.minute && lhs.second == rhs.second && lhs.millisecond == rhs.millisecond; } std::ostream& operator<<(std::ostream& os, Time time) { Time::Exploded exploded; time.UTCExplode(&exploded); // Use StringPrintf because iostreams formatting is painful. return os << StringPrintf("%04d-%02d-%02d %02d:%02d:%02d.%03d UTC", exploded.year, exploded.month, exploded.day_of_month, exploded.hour, exploded.minute, exploded.second, exploded.millisecond); } // TimeTicks ------------------------------------------------------------------ // static TimeTicks TimeTicks::Now() { return internal::g_time_ticks_now_function(); } // static TimeTicks TimeTicks::UnixEpoch() { static const base::NoDestructor epoch([]() { return subtle::TimeTicksNowIgnoringOverride() - (subtle::TimeNowIgnoringOverride() - Time::UnixEpoch()); }()); return *epoch; } TimeTicks TimeTicks::SnappedToNextTick(TimeTicks tick_phase, TimeDelta tick_interval) const { // |interval_offset| is the offset from |this| to the next multiple of // |tick_interval| after |tick_phase|, possibly negative if in the past. TimeDelta interval_offset = (tick_phase - *this) % tick_interval; // If |this| is exactly on the interval (i.e. offset==0), don't adjust. // Otherwise, if |tick_phase| was in the past, adjust forward to the next // tick after |this|. if (!interval_offset.is_zero() && tick_phase < *this) interval_offset += tick_interval; return *this + interval_offset; } std::ostream& operator<<(std::ostream& os, TimeTicks time_ticks) { // This function formats a TimeTicks object as "bogo-microseconds". // The origin and granularity of the count are platform-specific, and may very // from run to run. Although bogo-microseconds usually roughly correspond to // real microseconds, the only real guarantee is that the number never goes // down during a single run. const TimeDelta as_time_delta = time_ticks - TimeTicks(); return os << as_time_delta.InMicroseconds() << " bogo-microseconds"; } // ThreadTicks ---------------------------------------------------------------- // static ThreadTicks ThreadTicks::Now() { return internal::g_thread_ticks_now_function(); } std::ostream& operator<<(std::ostream& os, ThreadTicks thread_ticks) { const TimeDelta as_time_delta = thread_ticks - ThreadTicks(); return os << as_time_delta.InMicroseconds() << " bogo-thread-microseconds"; } // Time::Exploded ------------------------------------------------------------- inline bool is_in_range(int value, int lo, int hi) { return lo <= value && value <= hi; } bool Time::Exploded::HasValidValues() const { return is_in_range(month, 1, 12) && is_in_range(day_of_week, 0, 6) && is_in_range(day_of_month, 1, 31) && is_in_range(hour, 0, 23) && is_in_range(minute, 0, 59) && is_in_range(second, 0, 60) && is_in_range(millisecond, 0, 999); } } // namespace base