#include "base/time/time.h"
#include <atomic>
#include <cmath>
#include <limits>
#include <optional>
#include <ostream>
#include <tuple>
#include <utility>
#include "base/check.h"
#include "base/format_macros.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 {
TimeTicks g_shared_time_ticks_at_unix_epoch;
}
namespace internal {
std::atomic<TimeNowFunction> g_time_now_function{
&subtle::TimeNowIgnoringOverride};
std::atomic<TimeNowFunction> g_time_now_from_system_time_function{
&subtle::TimeNowFromSystemTimeIgnoringOverride};
std::atomic<TimeTicksNowFunction> g_time_ticks_now_function{
&subtle::TimeTicksNowIgnoringOverride};
std::atomic<TimeTicksLowResolutionNowFunction>
g_time_ticks_low_resolution_now_function{
&subtle::TimeTicksLowResolutionNowIgnoringOverride};
std::atomic<LiveTicksNowFunction> g_live_ticks_now_function{
&subtle::LiveTicksNowIgnoringOverride};
std::atomic<ThreadTicksNowFunction> g_thread_ticks_now_function{
&subtle::ThreadTicksNowIgnoringOverride};
}
TimeDelta TimeDelta::CeilToMultiple(TimeDelta interval) const {
if (is_inf() || interval.is_zero()) {
return *this;
}
const TimeDelta remainder = *this % interval;
if (delta_ < 0) {
return *this - remainder;
}
return remainder.is_zero() ? *this
: (*this - remainder + interval.magnitude());
}
TimeDelta TimeDelta::FloorToMultiple(TimeDelta interval) const {
if (is_inf() || interval.is_zero()) {
return *this;
}
const TimeDelta remainder = *this % interval;
if (delta_ < 0) {
return remainder.is_zero() ? *this
: (*this - remainder - interval.magnitude());
}
return *this - remainder;
}
TimeDelta TimeDelta::RoundToMultiple(TimeDelta interval) const {
if (is_inf() || interval.is_zero()) {
return *this;
}
if (interval.is_inf()) {
return TimeDelta();
}
const TimeDelta half = interval.magnitude() / 2;
return (delta_ < 0) ? (*this - half).CeilToMultiple(interval)
: (*this + half).FloorToMultiple(interval);
}
std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) {
return os << time_delta.InSecondsF() << " s";
}
Time Time::Now() {
return internal::g_time_now_function.load(std::memory_order_relaxed)();
}
Time Time::NowFromSystemTime() {
return internal::g_time_now_from_system_time_function.load(
std::memory_order_relaxed)();
}
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;
}
DCHECK(is_local);
exploded.hour = 1;
[[maybe_unused]] const bool result =
FromExploded(is_local, exploded, &out_time);
#if BUILDFLAG(IS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
#else
DCHECK(result);
#endif
return out_time;
}
bool Time::FromStringInternal(const char* time_string,
bool is_local,
Time* parsed_time) {
DCHECK(time_string);
DCHECK(parsed_time);
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 (result != PR_SUCCESS) {
return false;
}
*parsed_time = UnixEpoch() + Microseconds(result_time);
return true;
}
bool Time::ExplodedMostlyEquals(const Exploded& lhs, const Exploded& rhs) {
return std::tie(lhs.year, lhs.month, lhs.day_of_month, lhs.hour, lhs.minute,
lhs.second, lhs.millisecond) ==
std::tie(rhs.year, rhs.month, rhs.day_of_month, rhs.hour, rhs.minute,
rhs.second, rhs.millisecond);
}
bool Time::FromMillisecondsSinceUnixEpoch(int64_t unix_milliseconds,
Time* time) {
CheckedNumeric<int64_t> checked_microseconds_win_epoch = unix_milliseconds;
checked_microseconds_win_epoch *= kMicrosecondsPerMillisecond;
checked_microseconds_win_epoch += kTimeTToMicrosecondsOffset;
*time = Time(checked_microseconds_win_epoch.ValueOrDefault(0));
return checked_microseconds_win_epoch.IsValid();
}
int64_t Time::ToRoundedDownMillisecondsSinceUnixEpoch() const {
constexpr int64_t kEpochOffsetMillis =
kTimeTToMicrosecondsOffset / kMicrosecondsPerMillisecond;
static_assert(kTimeTToMicrosecondsOffset % kMicrosecondsPerMillisecond == 0,
"assumption: no epoch offset sub-milliseconds");
const int64_t millis = us_ / kMicrosecondsPerMillisecond;
const int64_t submillis = us_ % kMicrosecondsPerMillisecond;
return millis - kEpochOffsetMillis - (submillis < 0);
}
std::ostream& operator<<(std::ostream& os, Time time) {
Time::Exploded exploded;
time.UTCExplode(&exploded);
return os << StringPrintf("%04d-%02d-%02d %02d:%02d:%02d.%06" PRId64 " UTC",
exploded.year, exploded.month,
exploded.day_of_month, exploded.hour,
exploded.minute, exploded.second,
time.ToDeltaSinceWindowsEpoch().InMicroseconds() %
Time::kMicrosecondsPerSecond);
}
TimeTicks TimeTicks::Now() {
return internal::g_time_ticks_now_function.load(std::memory_order_relaxed)();
}
TimeTicks TimeTicks::LowResolutionNow() {
return internal::g_time_ticks_low_resolution_now_function.load(
std::memory_order_relaxed)();
}
void TimeTicks::SetSharedUnixEpoch(TimeTicks ticks_at_epoch) {
DCHECK(g_shared_time_ticks_at_unix_epoch.is_null());
g_shared_time_ticks_at_unix_epoch = ticks_at_epoch;
}
TimeTicks TimeTicks::UnixEpoch() {
struct StaticUnixEpoch {
StaticUnixEpoch()
: epoch(
g_shared_time_ticks_at_unix_epoch.is_null()
? subtle::TimeTicksNowIgnoringOverride() -
(subtle::TimeNowIgnoringOverride() - Time::UnixEpoch())
: g_shared_time_ticks_at_unix_epoch) {
g_shared_time_ticks_at_unix_epoch = TimeTicks::Max();
}
const TimeTicks epoch;
};
static StaticUnixEpoch static_epoch;
return static_epoch.epoch;
}
TimeTicks TimeTicks::SnappedToNextTick(TimeTicks tick_phase,
TimeDelta tick_interval) const {
TimeDelta interval_offset = (tick_phase - *this) % tick_interval;
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) {
const TimeDelta as_time_delta = time_ticks - TimeTicks();
return os << as_time_delta.InMicroseconds() << " bogo-microseconds";
}
LiveTicks LiveTicks::Now() {
return internal::g_live_ticks_now_function.load(std::memory_order_relaxed)();
}
#if !BUILDFLAG(IS_WIN)
namespace subtle {
LiveTicks LiveTicksNowIgnoringOverride() {
return LiveTicks() + (TimeTicks::Now() - TimeTicks());
}
}
#endif
std::ostream& operator<<(std::ostream& os, LiveTicks live_ticks) {
const TimeDelta as_time_delta = live_ticks - LiveTicks();
return os << as_time_delta.InMicroseconds() << " bogo-live-microseconds";
}
ThreadTicks ThreadTicks::Now() {
return internal::g_thread_ticks_now_function.load(
std::memory_order_relaxed)();
}
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";
}
bool Time::Exploded::HasValidValues() const {
return (1 <= month) && (month <= 12) &&
(0 <= day_of_week) && (day_of_week <= 6) &&
(1 <= day_of_month) && (day_of_month <= 31) &&
(0 <= hour) && (hour <= 23) &&
(0 <= minute) && (minute <= 59) &&
(0 <= second) && (second <= 60) &&
(0 <= millisecond) && (millisecond <= 999);
}
}