text: // 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 <cmath>
  #include <limits>
  #include <ostream>
  #include <tuple>
  #include <utility>
  
  #include "base/no_destructor.h"
  #include "base/optional.h"
  #include "base/strings/string_util.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 {
  
  // Strips the |expected| prefix from the start of the given string, returning
  // |true| if the strip operation succeeded or false otherwise.
  //
  // Example:
  //
  //   StringPiece input("abc");
  //   EXPECT_TRUE(ConsumePrefix(input, "a"));
  //   EXPECT_EQ(input, "bc");
  //
  // Adapted from absl::ConsumePrefix():
  // https://cs.chromium.org/chromium/src/third_party/abseil-cpp/absl/strings/strip.h?l=45&rcl=2c22e9135f107a4319582ae52e2e3e6b201b6b7c
  bool ConsumePrefix(StringPiece& str, StringPiece expected) {
    if (!StartsWith(str, expected))
      return false;
    str.remove_prefix(expected.size());
    return true;
  }
  
  // Utility struct used by ConsumeDurationNumber() to parse decimal numbers.
  // A ParsedDecimal represents the number `int_part` + `frac_part`/`frac_scale`,
  // where:
  //  (i)  0 <= `frac_part` < `frac_scale` (implies `frac_part`/`frac_scale` < 1)
  //  (ii) `frac_scale` is 10^[number of digits after the decimal point]
  //
  // Example:
  //  -42 => {.int_part = -42, .frac_part = 0, .frac_scale = 1}
  //  1.23 => {.int_part = 1, .frac_part = 23, .frac_scale = 100}
  struct ParsedDecimal {
    int64_t int_part = 0;
    int64_t frac_part = 0;
    int64_t frac_scale = 1;
  };
  
  // A helper for FromString() that tries to parse a leading number from the given
  // StringPiece. |number_string| is modified to start from the first unconsumed
  // char.
  //
  // Adapted from absl:
  // https://cs.chromium.org/chromium/src/third_party/abseil-cpp/absl/time/duration.cc?l=807&rcl=2c22e9135f107a4319582ae52e2e3e6b201b6b7c
  constexpr Optional<ParsedDecimal> ConsumeDurationNumber(
      StringPiece& number_string) {
    ParsedDecimal res;
    StringPiece::const_iterator orig_start = number_string.begin();
    // Parse contiguous digits.
    for (; !number_string.empty(); number_string.remove_prefix(1)) {
      const int d = number_string.front() - '0';
      if (d < 0 || d >= 10)
        break;
  
      if (res.int_part > std::numeric_limits<int64_t>::max() / 10)
        return nullopt;
      res.int_part *= 10;
      if (res.int_part > std::numeric_limits<int64_t>::max() - d)
        return nullopt;
      res.int_part += d;
    }
    const bool int_part_empty = number_string.begin() == orig_start;
    if (number_string.empty() || number_string.front() != '.')
      return int_part_empty ? nullopt : make_optional(res);
  
    number_string.remove_prefix(1);  // consume '.'
    // Parse contiguous digits.
    for (; !number_string.empty(); number_string.remove_prefix(1)) {
      const int d = number_string.front() - '0';
      if (d < 0 || d >= 10)
        break;
      DCHECK_LT(res.frac_part, res.frac_scale);
      if (res.frac_scale <= std::numeric_limits<int64_t>::max() / 10) {
        // |frac_part| will not overflow because it is always < |frac_scale|.
        res.frac_part *= 10;
        res.frac_part += d;
        res.frac_scale *= 10;
      }
    }
  
    return int_part_empty && res.frac_scale == 1 ? nullopt : make_optional(res);
  }
  
  // A helper for FromString() that tries to parse a leading unit designator
  // (e.g., ns, us, ms, s, m, h) from the given StringPiece. |unit_string| is
  // modified to start from the first unconsumed char.
  //
  // Adapted from absl:
  // https://cs.chromium.org/chromium/src/third_party/abseil-cpp/absl/time/duration.cc?l=841&rcl=2c22e9135f107a4319582ae52e2e3e6b201b6b7c
  Optional<TimeDelta> ConsumeDurationUnit(StringPiece& unit_string) {
    for (const auto& str_delta : {
             std::make_pair("ns", TimeDelta::FromNanoseconds(1)),
             std::make_pair("us", TimeDelta::FromMicroseconds(1)),
             // Note: "ms" MUST be checked before "m" to ensure that milliseconds
             // are not parsed as minutes.
             std::make_pair("ms", TimeDelta::FromMilliseconds(1)),
             std::make_pair("s", TimeDelta::FromSeconds(1)),
             std::make_pair("m", TimeDelta::FromMinutes(1)),
             std::make_pair("h", TimeDelta::FromHours(1)),
         }) {
      if (ConsumePrefix(unit_string, str_delta.first))
        return str_delta.second;
    }
  
    return nullopt;
  }
  
  }  // namespace
  
  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 ------------------------------------------------------------------
  
  // static
  Optional<TimeDelta> TimeDelta::FromString(StringPiece duration_string) {
    int sign = 1;
    if (ConsumePrefix(duration_string, "-"))
      sign = -1;
    else
      ConsumePrefix(duration_string, "+");
    if (duration_string.empty())
      return nullopt;
  
    // Handle special-case values that don't require units.
    if (duration_string == "0")
      return TimeDelta();
    if (duration_string == "inf")
      return sign == 1 ? TimeDelta::Max() : TimeDelta::Min();
  
    TimeDelta delta;
    while (!duration_string.empty()) {
      Optional<ParsedDecimal> number_opt = ConsumeDurationNumber(duration_string);
      if (!number_opt.has_value())
        return nullopt;
      Optional<TimeDelta> unit_opt = ConsumeDurationUnit(duration_string);
      if (!unit_opt.has_value())
        return nullopt;
  
      ParsedDecimal number = number_opt.value();
      TimeDelta unit = unit_opt.value();
      if (number.int_part != 0)
        delta += sign * number.int_part * unit;
      if (number.frac_part != 0)
        delta += (double{sign} * number.frac_part / number.frac_scale) * unit;
    }
    return delta;
  }
  
  int TimeDelta::InDays() const {
    if (!is_inf())
      return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
    return (delta_ < 0) ? std::numeric_limits<int>::min()
                        : std::numeric_limits<int>::max();
  }
  
  int TimeDelta::InDaysFloored() const {
    if (!is_inf()) {
      const int result = delta_ / Time::kMicrosecondsPerDay;
      // Convert |result| from truncating to flooring.
      return (result * Time::kMicrosecondsPerDay > delta_) ? (result - 1)
                                                           : result;
    }
    return (delta_ < 0) ? std::numeric_limits<int>::min()
                        : std::numeric_limits<int>::max();
  }
  
  double TimeDelta::InSecondsF() const {
    if (!is_inf())
      return double{delta_} / Time::kMicrosecondsPerSecond;
    return (delta_ < 0) ? -std::numeric_limits<double>::infinity()
                        : std::numeric_limits<double>::infinity();
  }
  
  int64_t TimeDelta::InSeconds() const {
    return is_inf() ? delta_ : (delta_ / Time::kMicrosecondsPerSecond);
  }
  
  double TimeDelta::InMillisecondsF() const {
    if (!is_inf())
      return double{delta_} / Time::kMicrosecondsPerMillisecond;
    return (delta_ < 0) ? -std::numeric_limits<double>::infinity()
                        : std::numeric_limits<double>::infinity();
  }
  
  int64_t TimeDelta::InMilliseconds() const {
    if (!is_inf())
      return delta_ / Time::kMicrosecondsPerMillisecond;
    return (delta_ < 0) ? std::numeric_limits<int64_t>::min()
                        : std::numeric_limits<int64_t>::max();
  }
  
  int64_t TimeDelta::InMillisecondsRoundedUp() const {
    if (!is_inf()) {
      const int64_t result = delta_ / Time::kMicrosecondsPerMillisecond;
      // Convert |result| from truncating to ceiling.
      return (delta_ > result * Time::kMicrosecondsPerMillisecond) ? (result + 1)
                                                                   : result;
    }
    return delta_;
  }
  
  double TimeDelta::InMicrosecondsF() const {
    if (!is_inf())
      return double{delta_};
    return (delta_ < 0) ? -std::numeric_limits<double>::infinity()
                        : std::numeric_limits<double>::infinity();
  }
  
  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 -----------------------------------------------------------------------
  
  // 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.
    return (tt == std::numeric_limits<time_t>::max())
               ? Max()
               : (UnixEpoch() + 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_inf() && ((std::numeric_limits<int64_t>::max() -
                       kTimeTToMicrosecondsOffset) > us_))
      return (*this - UnixEpoch()).InSeconds();
    return (us_ < 0) ? std::numeric_limits<time_t>::min()
                     : std::numeric_limits<time_t>::max();
  }
  
  // static
  Time Time::FromDoubleT(double dt) {
    // Preserve 0 so we can tell it doesn't exist.
    return (dt == 0 || std::isnan(dt))
               ? Time()
               : (UnixEpoch() + TimeDelta::FromSecondsD(dt));
  }
  
  double Time::ToDoubleT() const {
    if (is_null())
      return 0;  // Preserve 0 so we can tell it doesn't exist.
    if (!is_inf())
      return (*this - UnixEpoch()).InSecondsF();
    return (us_ < 0) ? -std::numeric_limits<double>::infinity()
                     : std::numeric_limits<double>::infinity();
  }
  
  #if defined(OS_POSIX) || defined(OS_FUCHSIA)
  // static
  Time Time::FromTimeSpec(const timespec& ts) {
    return FromDoubleT(ts.tv_sec + double{ts.tv_nsec} / 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 UnixEpoch() + TimeDelta::FromMillisecondsD(ms_since_epoch);
  }
  
  double Time::ToJsTime() const {
    // Preserve 0 so the invalid result doesn't depend on the platform.
    return is_null() ? 0 : ToJsTimeIgnoringNull();
  }
  
  double Time::ToJsTimeIgnoringNull() const {
    // Preserve max and min without offset to prevent over/underflow.
    if (!is_inf())
      return (*this - UnixEpoch()).InMillisecondsF();
    return (us_ < 0) ? -std::numeric_limits<double>::infinity()
                     : std::numeric_limits<double>::infinity();
  }
  
  Time Time::FromJavaTime(int64_t ms_since_epoch) {
    return UnixEpoch() + TimeDelta::FromMilliseconds(ms_since_epoch);
  }
  
  int64_t Time::ToJavaTime() const {
    // Preserve 0 so the invalid result doesn't depend on the platform.
    if (is_null())
      return 0;
    if (!is_inf())
      return (*this - UnixEpoch()).InMilliseconds();
    return (us_ < 0) ? std::numeric_limits<int64_t>::min()
                     : std::numeric_limits<int64_t>::max();
  }
  
  // static
  Time Time::UnixEpoch() {
    return Time(kTimeTToMicrosecondsOffset);
  }
  
  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;
  
    // Reaching here means 00:00:00am of the current day does not exist (due to
    // Daylight Saving Time in some countries where clocks are shifted at
    // midnight). In this case, midnight should be defined as 01:00:00am.
    DCHECK(is_local);
    exploded.hour = 1;
    const bool result = FromExploded(is_local, exploded, &out_time);
    DCHECK(result);  // This function must not fail.
    return out_time;
  }
  
  // static
  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() + TimeDelta::FromMicroseconds(result_time);
    return true;
  }
  
  // static
  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);
  }
  
  // static
  bool Time::FromMillisecondsSinceUnixEpoch(int64_t unix_milliseconds,
                                            Time* time) {
    // Adjust the provided time from milliseconds since the Unix epoch (1970) to
    // microseconds since the Windows epoch (1601), avoiding overflows.
    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");
  
    // Compute the milliseconds since UNIX epoch without the possibility of
    // under/overflow. Round the result towards -infinity.
    //
    // If |us_| is negative and includes fractions of a millisecond, subtract one
    // more to effect the round towards -infinity. C-style integer truncation
    // takes care of all other cases.
    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);
    // 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 NoDestructor<TimeTicks> 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 -------------------------------------------------------------
  
  bool Time::Exploded::HasValidValues() const {
    // clang-format off
    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);
    // clang-format on
  }
  
  }  // namespace base
  