// 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
