| // Protocol Buffers - Google's data interchange format | |
| // Copyright 2008 Google Inc. All rights reserved. | |
| // | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file or at | |
| // https://developers.google.com/open-source/licenses/bsd | |
| #include "google/protobuf/util/time_util.h" | |
| #include <cstdint> | |
| #include <cstdlib> | |
| #include <ctime> | |
| #include <string> | |
| #include "google/protobuf/duration.pb.h" | |
| #include "google/protobuf/timestamp.pb.h" | |
| #include "absl/log/absl_check.h" | |
| #include "absl/numeric/int128.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/str_format.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/time/clock.h" | |
| #include "absl/time/time.h" | |
| #include "google/protobuf/util/internal_timeval.h" // IWYU pragma: keep for timeval | |
| // Must go after other includes. | |
| #include "google/protobuf/port_def.inc" | |
| namespace google { | |
| namespace protobuf { | |
| namespace util { | |
| using google::protobuf::Duration; | |
| using google::protobuf::Timestamp; | |
| namespace { | |
| static constexpr int32_t kNanosPerSecond = 1000000000; | |
| static constexpr int32_t kMicrosPerSecond = 1000000; | |
| static constexpr int32_t kMillisPerSecond = 1000; | |
| static constexpr int32_t kNanosPerMillisecond = 1000000; | |
| static constexpr int32_t kNanosPerMicrosecond = 1000; | |
| static constexpr int32_t kSecondsPerMinute = | |
| 60; // Note that we ignore leap seconds. | |
| static constexpr int32_t kSecondsPerHour = 3600; | |
| Timestamp CreateNormalizedTimestamp(int64_t seconds, int32_t nanos) { | |
| ABSL_DCHECK(seconds >= TimeUtil::kTimestampMinSeconds && | |
| seconds <= TimeUtil::kTimestampMaxSeconds) | |
| << "Timestamp seconds are outside of the valid range"; | |
| // Make sure nanos is in the range. | |
| if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) { | |
| seconds += nanos / kNanosPerSecond; | |
| nanos = nanos % kNanosPerSecond; | |
| } | |
| // For Timestamp nanos should be in the range [0, 999999999] | |
| if (nanos < 0) { | |
| seconds -= 1; | |
| nanos += kNanosPerSecond; | |
| } | |
| ABSL_DCHECK(seconds >= TimeUtil::kTimestampMinSeconds && | |
| seconds <= TimeUtil::kTimestampMaxSeconds && | |
| nanos >= TimeUtil::kTimestampMinNanoseconds && | |
| nanos <= TimeUtil::kTimestampMaxNanoseconds) | |
| << "Timestamp is outside of the valid range"; | |
| Timestamp result; | |
| result.set_seconds(seconds); | |
| result.set_nanos(nanos); | |
| return result; | |
| } | |
| Duration CreateNormalizedDuration(int64_t seconds, int32_t nanos) { | |
| ABSL_DCHECK(seconds >= TimeUtil::kDurationMinSeconds && | |
| seconds <= TimeUtil::kDurationMaxSeconds) | |
| << "Duration seconds are outside of the valid range"; | |
| // Make sure nanos is in the range. | |
| if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) { | |
| seconds += nanos / kNanosPerSecond; | |
| nanos = nanos % kNanosPerSecond; | |
| } | |
| // nanos should have the same sign as seconds. | |
| if (seconds < 0 && nanos > 0) { | |
| seconds += 1; | |
| nanos -= kNanosPerSecond; | |
| } else if (seconds > 0 && nanos < 0) { | |
| seconds -= 1; | |
| nanos += kNanosPerSecond; | |
| } | |
| ABSL_DCHECK(seconds >= TimeUtil::kDurationMinSeconds && | |
| seconds <= TimeUtil::kDurationMaxSeconds && | |
| nanos >= TimeUtil::kDurationMinNanoseconds && | |
| nanos <= TimeUtil::kDurationMaxNanoseconds) | |
| << "Duration is outside of the valid range"; | |
| Duration result; | |
| result.set_seconds(seconds); | |
| result.set_nanos(nanos); | |
| return result; | |
| } | |
| // Format nanoseconds with either 3, 6, or 9 digits depending on the required | |
| // precision to represent the exact value. | |
| std::string FormatNanos(int32_t nanos) { | |
| if (nanos % kNanosPerMillisecond == 0) { | |
| return absl::StrFormat("%03d", nanos / kNanosPerMillisecond); | |
| } else if (nanos % kNanosPerMicrosecond == 0) { | |
| return absl::StrFormat("%06d", nanos / kNanosPerMicrosecond); | |
| } else { | |
| return absl::StrFormat("%09d", nanos); | |
| } | |
| } | |
| std::string FormatTime(int64_t seconds, int32_t nanos) { | |
| static constexpr absl::string_view kTimestampFormat = "%E4Y-%m-%dT%H:%M:%S"; | |
| timespec spec; | |
| spec.tv_sec = seconds; | |
| // We only use absl::FormatTime to format the seconds part because we need | |
| // finer control over the precision of nanoseconds. | |
| spec.tv_nsec = 0; | |
| std::string result = absl::FormatTime( | |
| kTimestampFormat, absl::TimeFromTimespec(spec), absl::UTCTimeZone()); | |
| // We format the nanoseconds part separately to meet the precision | |
| // requirement. | |
| if (nanos != 0) { | |
| absl::StrAppend(&result, ".", FormatNanos(nanos)); | |
| } | |
| absl::StrAppend(&result, "Z"); | |
| return result; | |
| } | |
| bool ParseTime(absl::string_view value, int64_t* seconds, int32_t* nanos) { | |
| absl::Time result; | |
| if (!absl::ParseTime(absl::RFC3339_full, value, &result, nullptr)) { | |
| return false; | |
| } | |
| *seconds = absl::ToUnixSeconds(result); | |
| absl::Duration remainder = result - absl::FromUnixSeconds(*seconds); | |
| *nanos = static_cast<int32_t>(absl::ToInt64Nanoseconds(remainder)); | |
| return true; | |
| } | |
| void CurrentTime(int64_t* seconds, int32_t* nanos) { | |
| absl::Time now = absl::Now(); | |
| timespec spec = absl::ToTimespec(now); | |
| *seconds = spec.tv_sec; | |
| *nanos = spec.tv_nsec; | |
| } | |
| // Truncates the remainder part after division. | |
| int64_t RoundTowardZero(int64_t value, int64_t divider) { | |
| int64_t result = value / divider; | |
| int64_t remainder = value % divider; | |
| // Before C++11, the sign of the remainder is implementation dependent if | |
| // any of the operands is negative. Here we try to enforce C++11's "rounded | |
| // toward zero" semantics. For example, for (-5) / 2 an implementation may | |
| // give -3 as the result with the remainder being 1. This function ensures | |
| // we always return -2 (closer to zero) regardless of the implementation. | |
| if (result < 0 && remainder > 0) { | |
| return result + 1; | |
| } else { | |
| return result; | |
| } | |
| } | |
| } // namespace | |
| // Actually define these static const integers. Required by C++ standard (but | |
| // some compilers don't like it). | |
| #ifndef _MSC_VER | |
| constexpr int64_t TimeUtil::kTimestampMinSeconds; | |
| constexpr int64_t TimeUtil::kTimestampMaxSeconds; | |
| constexpr int32_t TimeUtil::kTimestampMinNanoseconds; | |
| constexpr int32_t TimeUtil::kTimestampMaxNanoseconds; | |
| constexpr int64_t TimeUtil::kDurationMaxSeconds; | |
| constexpr int64_t TimeUtil::kDurationMinSeconds; | |
| constexpr int32_t TimeUtil::kDurationMaxNanoseconds; | |
| constexpr int32_t TimeUtil::kDurationMinNanoseconds; | |
| #endif // !_MSC_VER | |
| std::string TimeUtil::ToString(const Timestamp& timestamp) { | |
| return FormatTime(timestamp.seconds(), timestamp.nanos()); | |
| } | |
| bool TimeUtil::FromString(absl::string_view value, Timestamp* timestamp) { | |
| int64_t seconds; | |
| int32_t nanos; | |
| if (!ParseTime(value, &seconds, &nanos)) { | |
| return false; | |
| } | |
| // Validate before CreateNormalizedTimestamp, which has a DCHECK on range. | |
| if (seconds < kTimestampMinSeconds || seconds > kTimestampMaxSeconds) { | |
| return false; | |
| } | |
| *timestamp = CreateNormalizedTimestamp(seconds, nanos); | |
| if (!IsTimestampValid(*timestamp)) { | |
| timestamp->Clear(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| Timestamp TimeUtil::GetCurrentTime() { | |
| int64_t seconds; | |
| int32_t nanos; | |
| CurrentTime(&seconds, &nanos); | |
| return CreateNormalizedTimestamp(seconds, nanos); | |
| } | |
| Timestamp TimeUtil::GetEpoch() { return Timestamp(); } | |
| std::string TimeUtil::ToString(const Duration& duration) { | |
| std::string result; | |
| int64_t seconds = duration.seconds(); | |
| int32_t nanos = duration.nanos(); | |
| if (seconds < 0 || nanos < 0) { | |
| result = "-"; | |
| seconds = -seconds; | |
| nanos = -nanos; | |
| } | |
| absl::StrAppend(&result, seconds); | |
| if (nanos != 0) { | |
| absl::StrAppend(&result, ".", FormatNanos(nanos)); | |
| } | |
| absl::StrAppend(&result, "s"); | |
| return result; | |
| } | |
| static int64_t Pow(int64_t x, int y) { | |
| int64_t result = 1; | |
| for (int i = 0; i < y; ++i) { | |
| result *= x; | |
| } | |
| return result; | |
| } | |
| bool TimeUtil::FromString(absl::string_view value, Duration* duration) { | |
| if (value.length() <= 1 || value[value.length() - 1] != 's') { | |
| return false; | |
| } | |
| bool negative = (value[0] == '-'); | |
| size_t sign_length = (negative ? 1 : 0); | |
| // Parse the duration value as two integers rather than a float value | |
| // to avoid precision loss. | |
| std::string seconds_part, nanos_part; | |
| size_t pos = value.find_last_of('.'); | |
| if (pos == std::string::npos) { | |
| seconds_part = std::string( | |
| value.substr(sign_length, value.length() - 1 - sign_length)); | |
| nanos_part = "0"; | |
| } else { | |
| seconds_part = std::string(value.substr(sign_length, pos - sign_length)); | |
| nanos_part = std::string(value.substr(pos + 1, value.length() - pos - 2)); | |
| } | |
| char* end; | |
| static_assert(sizeof(int64_t) == sizeof(long long), | |
| "sizeof int64_t is not sizeof long long"); | |
| int64_t seconds = std::strtoll(seconds_part.c_str(), &end, 10); | |
| if (end != seconds_part.c_str() + seconds_part.length()) { | |
| return false; | |
| } | |
| int64_t nanos = std::strtoll(nanos_part.c_str(), &end, 10); | |
| if (end != nanos_part.c_str() + nanos_part.length()) { | |
| return false; | |
| } | |
| nanos = nanos * Pow(10, static_cast<int>(9 - nanos_part.length())); | |
| if (negative) { | |
| // If a Duration is negative, both seconds and nanos should be negative. | |
| seconds = -seconds; | |
| nanos = -nanos; | |
| } | |
| duration->set_seconds(seconds); | |
| duration->set_nanos(static_cast<int32_t>(nanos)); | |
| if (!IsDurationValid(*duration)) { | |
| duration->Clear(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| Duration TimeUtil::NanosecondsToDuration(int64_t nanos) { | |
| return CreateNormalizedDuration(nanos / kNanosPerSecond, | |
| nanos % kNanosPerSecond); | |
| } | |
| Duration TimeUtil::MicrosecondsToDuration(int64_t micros) { | |
| return CreateNormalizedDuration( | |
| micros / kMicrosPerSecond, | |
| (micros % kMicrosPerSecond) * kNanosPerMicrosecond); | |
| } | |
| Duration TimeUtil::MillisecondsToDuration(int64_t millis) { | |
| return CreateNormalizedDuration( | |
| millis / kMillisPerSecond, | |
| (millis % kMillisPerSecond) * kNanosPerMillisecond); | |
| } | |
| Duration TimeUtil::SecondsToDuration(int64_t seconds) { | |
| return CreateNormalizedDuration(seconds, 0); | |
| } | |
| Duration TimeUtil::MinutesToDuration(int64_t minutes) { | |
| ABSL_DCHECK(minutes >= TimeUtil::kDurationMinSeconds / kSecondsPerMinute && | |
| minutes <= TimeUtil::kDurationMaxSeconds / kSecondsPerMinute) | |
| << "Duration minutes are outside of the valid range"; | |
| return SecondsToDuration(minutes * kSecondsPerMinute); | |
| } | |
| Duration TimeUtil::HoursToDuration(int64_t hours) { | |
| ABSL_DCHECK(hours >= TimeUtil::kDurationMinSeconds / kSecondsPerHour && | |
| hours <= TimeUtil::kDurationMaxSeconds / kSecondsPerHour) | |
| << "Duration hours are outside of the valid range"; | |
| return SecondsToDuration(hours * kSecondsPerHour); | |
| } | |
| int64_t TimeUtil::DurationToNanoseconds(const Duration& duration) { | |
| ABSL_DCHECK(IsDurationValid(duration)) | |
| << "Duration is outside of the valid range"; | |
| return duration.seconds() * kNanosPerSecond + duration.nanos(); | |
| } | |
| int64_t TimeUtil::DurationToMicroseconds(const Duration& duration) { | |
| return DurationToSeconds(duration) * kMicrosPerSecond + | |
| RoundTowardZero(duration.nanos(), kNanosPerMicrosecond); | |
| } | |
| int64_t TimeUtil::DurationToMilliseconds(const Duration& duration) { | |
| return DurationToSeconds(duration) * kMillisPerSecond + | |
| RoundTowardZero(duration.nanos(), kNanosPerMillisecond); | |
| } | |
| int64_t TimeUtil::DurationToSeconds(const Duration& duration) { | |
| ABSL_DCHECK(IsDurationValid(duration)) | |
| << "Duration is outside of the valid range"; | |
| return duration.seconds(); | |
| } | |
| int64_t TimeUtil::DurationToMinutes(const Duration& duration) { | |
| return RoundTowardZero(DurationToSeconds(duration), kSecondsPerMinute); | |
| } | |
| int64_t TimeUtil::DurationToHours(const Duration& duration) { | |
| return RoundTowardZero(DurationToSeconds(duration), kSecondsPerHour); | |
| } | |
| Timestamp TimeUtil::NanosecondsToTimestamp(int64_t nanos) { | |
| return CreateNormalizedTimestamp(nanos / kNanosPerSecond, | |
| nanos % kNanosPerSecond); | |
| } | |
| Timestamp TimeUtil::MicrosecondsToTimestamp(int64_t micros) { | |
| return CreateNormalizedTimestamp( | |
| micros / kMicrosPerSecond, | |
| micros % kMicrosPerSecond * kNanosPerMicrosecond); | |
| } | |
| Timestamp TimeUtil::MillisecondsToTimestamp(int64_t millis) { | |
| return CreateNormalizedTimestamp( | |
| millis / kMillisPerSecond, | |
| millis % kMillisPerSecond * kNanosPerMillisecond); | |
| } | |
| Timestamp TimeUtil::SecondsToTimestamp(int64_t seconds) { | |
| return CreateNormalizedTimestamp(seconds, 0); | |
| } | |
| int64_t TimeUtil::TimestampToNanoseconds(const Timestamp& timestamp) { | |
| ABSL_DCHECK(IsTimestampValid(timestamp)) | |
| << "Timestamp is outside of the valid range"; | |
| return timestamp.seconds() * kNanosPerSecond + timestamp.nanos(); | |
| } | |
| int64_t TimeUtil::TimestampToMicroseconds(const Timestamp& timestamp) { | |
| ABSL_DCHECK(IsTimestampValid(timestamp)) | |
| << "Timestamp is outside of the valid range"; | |
| return timestamp.seconds() * kMicrosPerSecond + | |
| RoundTowardZero(timestamp.nanos(), kNanosPerMicrosecond); | |
| } | |
| int64_t TimeUtil::TimestampToMilliseconds(const Timestamp& timestamp) { | |
| ABSL_DCHECK(IsTimestampValid(timestamp)) | |
| << "Timestamp is outside of the valid range"; | |
| return timestamp.seconds() * kMillisPerSecond + | |
| RoundTowardZero(timestamp.nanos(), kNanosPerMillisecond); | |
| } | |
| int64_t TimeUtil::TimestampToSeconds(const Timestamp& timestamp) { | |
| ABSL_DCHECK(IsTimestampValid(timestamp)) | |
| << "Timestamp is outside of the valid range"; | |
| return timestamp.seconds(); | |
| } | |
| Timestamp TimeUtil::TimeTToTimestamp(time_t value) { | |
| return CreateNormalizedTimestamp(static_cast<int64_t>(value), 0); | |
| } | |
| time_t TimeUtil::TimestampToTimeT(const Timestamp& value) { | |
| return static_cast<time_t>(value.seconds()); | |
| } | |
| Timestamp TimeUtil::TimevalToTimestamp(const struct timeval& value) { | |
| return CreateNormalizedTimestamp(value.tv_sec, | |
| value.tv_usec * kNanosPerMicrosecond); | |
| } | |
| struct timeval TimeUtil::TimestampToTimeval(const Timestamp& value) { | |
| struct timeval result; | |
| result.tv_sec = value.seconds(); | |
| result.tv_usec = RoundTowardZero(value.nanos(), kNanosPerMicrosecond); | |
| return result; | |
| } | |
| Duration TimeUtil::TimevalToDuration(const struct timeval& value) { | |
| return CreateNormalizedDuration(value.tv_sec, | |
| value.tv_usec * kNanosPerMicrosecond); | |
| } | |
| struct timeval TimeUtil::DurationToTimeval(const Duration& value) { | |
| struct timeval result; | |
| result.tv_sec = value.seconds(); | |
| result.tv_usec = RoundTowardZero(value.nanos(), kNanosPerMicrosecond); | |
| // timeval.tv_usec's range is [0, 1000000) | |
| if (result.tv_usec < 0) { | |
| result.tv_sec -= 1; | |
| result.tv_usec += kMicrosPerSecond; | |
| } | |
| return result; | |
| } | |
| } // namespace util | |
| } // namespace protobuf | |
| } // namespace google | |
| namespace google { | |
| namespace protobuf { | |
| namespace { | |
| using ::google::protobuf::util::CreateNormalizedDuration; | |
| using ::google::protobuf::util::CreateNormalizedTimestamp; | |
| using ::google::protobuf::util::kNanosPerSecond; | |
| // Convert a Duration to uint128. | |
| void ToUint128(const Duration& value, absl::uint128* result, bool* negative) { | |
| if (value.seconds() < 0 || value.nanos() < 0) { | |
| *negative = true; | |
| *result = static_cast<uint64_t>(-value.seconds()); | |
| *result = *result * kNanosPerSecond + static_cast<uint32_t>(-value.nanos()); | |
| } else { | |
| *negative = false; | |
| *result = static_cast<uint64_t>(value.seconds()); | |
| *result = *result * kNanosPerSecond + static_cast<uint32_t>(value.nanos()); | |
| } | |
| } | |
| void ToDuration(const absl::uint128 value, bool negative, Duration* duration) { | |
| int64_t seconds = | |
| static_cast<int64_t>(absl::Uint128Low64(value / kNanosPerSecond)); | |
| int32_t nanos = | |
| static_cast<int32_t>(absl::Uint128Low64(value % kNanosPerSecond)); | |
| if (negative) { | |
| seconds = -seconds; | |
| nanos = -nanos; | |
| } | |
| duration->set_seconds(seconds); | |
| duration->set_nanos(nanos); | |
| } | |
| } // namespace | |
| Duration& operator+=(Duration& d1, const Duration& d2) { | |
| d1 = CreateNormalizedDuration(d1.seconds() + d2.seconds(), | |
| d1.nanos() + d2.nanos()); | |
| return d1; | |
| } | |
| Duration& operator-=(Duration& d1, const Duration& d2) { // NOLINT | |
| d1 = CreateNormalizedDuration(d1.seconds() - d2.seconds(), | |
| d1.nanos() - d2.nanos()); | |
| return d1; | |
| } | |
| Duration& operator*=(Duration& d, int64_t r) { // NOLINT | |
| bool negative; | |
| absl::uint128 value; | |
| ToUint128(d, &value, &negative); | |
| if (r > 0) { | |
| value *= static_cast<uint64_t>(r); | |
| } else { | |
| negative = !negative; | |
| value *= static_cast<uint64_t>(-r); | |
| } | |
| ToDuration(value, negative, &d); | |
| return d; | |
| } | |
| Duration& operator*=(Duration& d, double r) { // NOLINT | |
| double result = | |
| (static_cast<double>(d.seconds()) + d.nanos() * (1.0 / kNanosPerSecond)) * | |
| r; | |
| int64_t seconds = static_cast<int64_t>(result); | |
| int32_t nanos = static_cast<int32_t>((result - static_cast<double>(seconds)) * | |
| kNanosPerSecond); | |
| // Note that we normalize here not just because nanos can have a different | |
| // sign from seconds but also that nanos can be any arbitrary value when | |
| // overflow happens (i.e., the result is a much larger value than what | |
| // int64 can represent). | |
| d = CreateNormalizedDuration(seconds, nanos); | |
| return d; | |
| } | |
| Duration& operator/=(Duration& d, int64_t r) { // NOLINT | |
| bool negative; | |
| absl::uint128 value; | |
| ToUint128(d, &value, &negative); | |
| if (r > 0) { | |
| value /= static_cast<uint64_t>(r); | |
| } else { | |
| negative = !negative; | |
| value /= static_cast<uint64_t>(-r); | |
| } | |
| ToDuration(value, negative, &d); | |
| return d; | |
| } | |
| Duration& operator/=(Duration& d, double r) { // NOLINT | |
| return d *= 1.0 / r; | |
| } | |
| Duration& operator%=(Duration& d1, const Duration& d2) { // NOLINT | |
| bool negative1, negative2; | |
| absl::uint128 value1, value2; | |
| ToUint128(d1, &value1, &negative1); | |
| ToUint128(d2, &value2, &negative2); | |
| absl::uint128 result = value1 % value2; | |
| // When negative values are involved in division, we round the division | |
| // result towards zero. With this semantics, sign of the remainder is the | |
| // same as the dividend. For example: | |
| // -5 / 10 = 0, -5 % 10 = -5 | |
| // -5 / (-10) = 0, -5 % (-10) = -5 | |
| // 5 / (-10) = 0, 5 % (-10) = 5 | |
| ToDuration(result, negative1, &d1); | |
| return d1; | |
| } | |
| int64_t operator/(const Duration& d1, const Duration& d2) { | |
| bool negative1, negative2; | |
| absl::uint128 value1, value2; | |
| ToUint128(d1, &value1, &negative1); | |
| ToUint128(d2, &value2, &negative2); | |
| int64_t result = absl::Uint128Low64(value1 / value2); | |
| if (negative1 != negative2) { | |
| result = -result; | |
| } | |
| return result; | |
| } | |
| Timestamp& operator+=(Timestamp& t, const Duration& d) { // NOLINT | |
| t = CreateNormalizedTimestamp(t.seconds() + d.seconds(), | |
| t.nanos() + d.nanos()); | |
| return t; | |
| } | |
| Timestamp& operator-=(Timestamp& t, const Duration& d) { // NOLINT | |
| t = CreateNormalizedTimestamp(t.seconds() - d.seconds(), | |
| t.nanos() - d.nanos()); | |
| return t; | |
| } | |
| Duration operator-(const Timestamp& t1, const Timestamp& t2) { | |
| return CreateNormalizedDuration(t1.seconds() - t2.seconds(), | |
| t1.nanos() - t2.nanos()); | |
| } | |
| } // namespace protobuf | |
| } // namespace google | |
| #include "google/protobuf/port_undef.inc" |