Commit e52c47fb authored by Peter Kasting's avatar Peter Kasting Committed by Commit Bot

Add TimeDelta::{Ceil,Floor,Round}ToMultiple(TimeDelta interval).

This function returns the current TimeDelta, snapped to the nearest
multiple of |interval| by the appropriate method.  This will be useful
for a variety of current callers of / and %.

Bug: 1104532
Change-Id: I3add5b875961938e7620e12b86127c508412c68d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2343509
Commit-Queue: Peter Kasting <pkasting@chromium.org>
Reviewed-by: default avatarYuri Wiitala <miu@chromium.org>
Auto-Submit: Peter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797928}
parent e19ef20a
......@@ -22,133 +22,6 @@
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<int>::max();
}
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int>::min();
}
return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
}
int TimeDelta::InDaysFloored() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<int>::max();
}
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int>::min();
}
int result = delta_ / Time::kMicrosecondsPerDay;
int64_t remainder = delta_ - (result * Time::kMicrosecondsPerDay);
if (remainder < 0) {
--result; // Use floor(), not trunc() rounding behavior.
}
return result;
}
double TimeDelta::InSecondsF() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<double>::infinity();
}
if (is_min()) {
// Preserve min to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond;
}
int64_t TimeDelta::InSeconds() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<int64_t>::max();
}
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
return delta_ / Time::kMicrosecondsPerSecond;
}
double TimeDelta::InMillisecondsF() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<double>::infinity();
}
if (is_min()) {
// Preserve min to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond;
}
int64_t TimeDelta::InMilliseconds() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<int64_t>::max();
}
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
return delta_ / Time::kMicrosecondsPerMillisecond;
}
int64_t TimeDelta::InMillisecondsRoundedUp() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<int64_t>::max();
}
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
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;
}
double TimeDelta::InMicrosecondsF() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<double>::infinity();
}
if (is_min()) {
// Preserve min to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_);
}
std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) {
return os << time_delta.InSecondsF() << " s";
}
namespace {
// Strips the |expected| prefix from the start of the given string, returning
......@@ -256,6 +129,23 @@ Optional<TimeDelta> ConsumeDurationUnit(StringPiece& unit_string) {
} // 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;
......@@ -291,6 +181,147 @@ Optional<TimeDelta> TimeDelta::FromString(StringPiece duration_string) {
return delta;
}
int TimeDelta::InDays() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<int>::max();
}
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int>::min();
}
return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
}
int TimeDelta::InDaysFloored() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<int>::max();
}
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int>::min();
}
int result = delta_ / Time::kMicrosecondsPerDay;
int64_t remainder = delta_ - (result * Time::kMicrosecondsPerDay);
if (remainder < 0) {
--result; // Use floor(), not trunc() rounding behavior.
}
return result;
}
double TimeDelta::InSecondsF() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<double>::infinity();
}
if (is_min()) {
// Preserve min to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond;
}
int64_t TimeDelta::InSeconds() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<int64_t>::max();
}
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
return delta_ / Time::kMicrosecondsPerSecond;
}
double TimeDelta::InMillisecondsF() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<double>::infinity();
}
if (is_min()) {
// Preserve min to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond;
}
int64_t TimeDelta::InMilliseconds() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<int64_t>::max();
}
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
return delta_ / Time::kMicrosecondsPerMillisecond;
}
int64_t TimeDelta::InMillisecondsRoundedUp() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<int64_t>::max();
}
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
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;
}
double TimeDelta::InMicrosecondsF() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<double>::infinity();
}
if (is_min()) {
// Preserve min to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_);
}
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
......
......@@ -203,6 +203,10 @@ class BASE_EXPORT TimeDelta {
// Returns the magnitude (absolute value) of this TimeDelta.
constexpr TimeDelta magnitude() const {
// The code below will not work correctly in this corner case.
if (is_min())
return Max();
// std::abs() is not currently constexpr. The following is a simple
// branchless implementation:
const int64_t mask = delta_ >> (sizeof(delta_) * 8 - 1);
......@@ -355,6 +359,12 @@ class BASE_EXPORT TimeDelta {
return delta_ >= other.delta_;
}
// Returns this delta, ceiled/floored/rounded-away-from-zero to the nearest
// multiple of |interval|.
TimeDelta CeilToMultiple(TimeDelta interval) const;
TimeDelta FloorToMultiple(TimeDelta interval) const;
TimeDelta RoundToMultiple(TimeDelta interval) const;
private:
friend constexpr int64_t time_internal::SaturatedAdd(int64_t value,
TimeDelta delta);
......
......@@ -1575,6 +1575,8 @@ TEST(TimeDelta, Magnitude) {
static_assert(TimeDelta::FromMicroseconds(max_int64_minus_one) ==
TimeDelta::FromMicroseconds(min_int64_plus_two).magnitude(),
"");
static_assert(TimeDelta::Max() == TimeDelta::Min().magnitude(), "");
}
TEST(TimeDelta, ZeroMinMax) {
......@@ -1967,6 +1969,166 @@ TEST(TimeDelta, Overflows) {
EXPECT_EQ(kOneSecond, (ticks_now + kOneSecond) - ticks_now);
}
TEST(TimeDelta, CeilToMultiple) {
for (const auto interval :
{TimeDelta::FromSeconds(10), TimeDelta::FromSeconds(-10)}) {
SCOPED_TRACE(interval);
EXPECT_EQ(TimeDelta().CeilToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(1).CeilToMultiple(interval),
TimeDelta::FromSeconds(10));
EXPECT_EQ(TimeDelta::FromSeconds(9).CeilToMultiple(interval),
TimeDelta::FromSeconds(10));
EXPECT_EQ(TimeDelta::FromSeconds(10).CeilToMultiple(interval),
TimeDelta::FromSeconds(10));
EXPECT_EQ(TimeDelta::FromSeconds(15).CeilToMultiple(interval),
TimeDelta::FromSeconds(20));
EXPECT_EQ(TimeDelta::FromSeconds(20).CeilToMultiple(interval),
TimeDelta::FromSeconds(20));
EXPECT_EQ(TimeDelta::Max().CeilToMultiple(interval), TimeDelta::Max());
EXPECT_EQ(TimeDelta::FromSeconds(-1).CeilToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(-9).CeilToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(-10).CeilToMultiple(interval),
TimeDelta::FromSeconds(-10));
EXPECT_EQ(TimeDelta::FromSeconds(-15).CeilToMultiple(interval),
TimeDelta::FromSeconds(-10));
EXPECT_EQ(TimeDelta::FromSeconds(-20).CeilToMultiple(interval),
TimeDelta::FromSeconds(-20));
EXPECT_EQ(TimeDelta::Min().CeilToMultiple(interval), TimeDelta::Min());
}
for (const auto interval : {TimeDelta::Max(), TimeDelta::Min()}) {
SCOPED_TRACE(interval);
EXPECT_EQ(TimeDelta().CeilToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(1).CeilToMultiple(interval),
TimeDelta::Max());
EXPECT_EQ(TimeDelta::FromSeconds(9).CeilToMultiple(interval),
TimeDelta::Max());
EXPECT_EQ(TimeDelta::FromSeconds(10).CeilToMultiple(interval),
TimeDelta::Max());
EXPECT_EQ(TimeDelta::FromSeconds(15).CeilToMultiple(interval),
TimeDelta::Max());
EXPECT_EQ(TimeDelta::FromSeconds(20).CeilToMultiple(interval),
TimeDelta::Max());
EXPECT_EQ(TimeDelta::Max().CeilToMultiple(interval), TimeDelta::Max());
EXPECT_EQ(TimeDelta::FromSeconds(-1).CeilToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(-9).CeilToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(-10).CeilToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(-15).CeilToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(-20).CeilToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::Min().CeilToMultiple(interval), TimeDelta::Min());
}
}
TEST(TimeDelta, FloorToMultiple) {
for (const auto interval :
{TimeDelta::FromSeconds(10), TimeDelta::FromSeconds(-10)}) {
SCOPED_TRACE(interval);
EXPECT_EQ(TimeDelta().FloorToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(1).FloorToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(9).FloorToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(10).FloorToMultiple(interval),
TimeDelta::FromSeconds(10));
EXPECT_EQ(TimeDelta::FromSeconds(15).FloorToMultiple(interval),
TimeDelta::FromSeconds(10));
EXPECT_EQ(TimeDelta::FromSeconds(20).FloorToMultiple(interval),
TimeDelta::FromSeconds(20));
EXPECT_EQ(TimeDelta::Max().FloorToMultiple(interval), TimeDelta::Max());
EXPECT_EQ(TimeDelta::FromSeconds(-1).FloorToMultiple(interval),
TimeDelta::FromSeconds(-10));
EXPECT_EQ(TimeDelta::FromSeconds(-9).FloorToMultiple(interval),
TimeDelta::FromSeconds(-10));
EXPECT_EQ(TimeDelta::FromSeconds(-10).FloorToMultiple(interval),
TimeDelta::FromSeconds(-10));
EXPECT_EQ(TimeDelta::FromSeconds(-15).FloorToMultiple(interval),
TimeDelta::FromSeconds(-20));
EXPECT_EQ(TimeDelta::FromSeconds(-20).FloorToMultiple(interval),
TimeDelta::FromSeconds(-20));
EXPECT_EQ(TimeDelta::Min().FloorToMultiple(interval), TimeDelta::Min());
}
for (const auto interval : {TimeDelta::Max(), TimeDelta::Min()}) {
SCOPED_TRACE(interval);
EXPECT_EQ(TimeDelta().FloorToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(1).FloorToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(9).FloorToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(10).FloorToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(15).FloorToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(20).FloorToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::Max().FloorToMultiple(interval), TimeDelta::Max());
EXPECT_EQ(TimeDelta::FromSeconds(-1).FloorToMultiple(interval),
TimeDelta::Min());
EXPECT_EQ(TimeDelta::FromSeconds(-9).FloorToMultiple(interval),
TimeDelta::Min());
EXPECT_EQ(TimeDelta::FromSeconds(-10).FloorToMultiple(interval),
TimeDelta::Min());
EXPECT_EQ(TimeDelta::FromSeconds(-15).FloorToMultiple(interval),
TimeDelta::Min());
EXPECT_EQ(TimeDelta::FromSeconds(-20).FloorToMultiple(interval),
TimeDelta::Min());
EXPECT_EQ(TimeDelta::Min().FloorToMultiple(interval), TimeDelta::Min());
}
}
TEST(TimeDelta, RoundToMultiple) {
for (const auto interval :
{TimeDelta::FromSeconds(10), TimeDelta::FromSeconds(-10)}) {
SCOPED_TRACE(interval);
EXPECT_EQ(TimeDelta().RoundToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(1).RoundToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(9).RoundToMultiple(interval),
TimeDelta::FromSeconds(10));
EXPECT_EQ(TimeDelta::FromSeconds(10).RoundToMultiple(interval),
TimeDelta::FromSeconds(10));
EXPECT_EQ(TimeDelta::FromSeconds(15).RoundToMultiple(interval),
TimeDelta::FromSeconds(20));
EXPECT_EQ(TimeDelta::FromSeconds(20).RoundToMultiple(interval),
TimeDelta::FromSeconds(20));
EXPECT_EQ(TimeDelta::Max().RoundToMultiple(interval), TimeDelta::Max());
EXPECT_EQ(TimeDelta::FromSeconds(-1).RoundToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(-9).RoundToMultiple(interval),
TimeDelta::FromSeconds(-10));
EXPECT_EQ(TimeDelta::FromSeconds(-10).RoundToMultiple(interval),
TimeDelta::FromSeconds(-10));
EXPECT_EQ(TimeDelta::FromSeconds(-15).RoundToMultiple(interval),
TimeDelta::FromSeconds(-20));
EXPECT_EQ(TimeDelta::FromSeconds(-20).RoundToMultiple(interval),
TimeDelta::FromSeconds(-20));
EXPECT_EQ(TimeDelta::Min().RoundToMultiple(interval), TimeDelta::Min());
}
for (const auto interval : {TimeDelta::Max(), TimeDelta::Min()}) {
SCOPED_TRACE(interval);
EXPECT_EQ(TimeDelta().RoundToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(1).RoundToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(9).RoundToMultiple(interval), TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(10).RoundToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(15).RoundToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(20).RoundToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::Max().RoundToMultiple(interval), TimeDelta::Max());
EXPECT_EQ(TimeDelta::FromSeconds(-1).RoundToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(-9).RoundToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(-10).RoundToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(-15).RoundToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::FromSeconds(-20).RoundToMultiple(interval),
TimeDelta());
EXPECT_EQ(TimeDelta::Min().RoundToMultiple(interval), TimeDelta::Min());
}
}
TEST(TimeBase, AddSubDeltaSaturates) {
constexpr TimeTicks kLargeTimeTicks =
TimeTicks::FromInternalValue(std::numeric_limits<int64_t>::max() - 1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment