--- /dev/null
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Conversions between std::chrono types and POSIX time types.
+ *
+ * These conversions will fail with a ConversionError if an overflow would
+ * occur performing the conversion. (e.g., if the input value cannot fit in
+ * the destination type). However they allow loss of precision (e.g.,
+ * converting nanoseconds to a struct timeval which only has microsecond
+ * granularity, or a struct timespec to std::chrono::minutes).
+ */
+
+#pragma once
+
+#include <chrono>
+#include <type_traits>
+
+#include <folly/Conv.h>
+#include <folly/Expected.h>
+
+namespace folly {
+namespace detail {
+
+template <typename T>
+struct is_duration : std::false_type {};
+template <typename Rep, typename Period>
+struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type {};
+template <typename T>
+struct is_time_point : std::false_type {};
+template <typename Clock, typename Duration>
+struct is_time_point<std::chrono::time_point<Clock, Duration>>
+ : std::true_type {};
+template <typename T>
+struct is_std_chrono_type {
+ static constexpr bool value =
+ is_duration<T>::value || is_time_point<T>::value;
+};
+template <typename T>
+struct is_posix_time_type {
+ static constexpr bool value = std::is_same<T, struct timespec>::value ||
+ std::is_same<T, struct timeval>::value;
+};
+template <typename Tgt, typename Src>
+struct is_chrono_conversion {
+ static constexpr bool value =
+ ((is_std_chrono_type<Tgt>::value && is_posix_time_type<Src>::value) ||
+ (is_posix_time_type<Tgt>::value && is_std_chrono_type<Src>::value));
+};
+
+/**
+ * This converts a number in some input type to time_t while ensuring that it
+ * fits in the range of numbers representable by time_t.
+ *
+ * This is similar to the normal folly::tryTo() behavior when converting
+ * arthmetic types to an integer type, except that it does not complain about
+ * floating point conversions losing precision.
+ */
+template <typename Src>
+Expected<time_t, ConversionCode> chronoRangeCheck(Src value) {
+ if (value > std::numeric_limits<time_t>::max()) {
+ return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);
+ }
+ if (std::is_signed<Src>::value) {
+ if (value < std::numeric_limits<time_t>::lowest()) {
+ return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
+ }
+ }
+
+ return static_cast<time_t>(value);
+}
+
+/**
+ * Convert a std::chrono::duration with second granularity to a pair of
+ * (seconds, subseconds)
+ *
+ * The SubsecondRatio template parameter specifies what type of subseconds to
+ * return. This must have a numerator of 1.
+ */
+template <typename SubsecondRatio, typename Rep>
+static Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(
+ const std::chrono::duration<Rep, std::ratio<1, 1>>& duration) {
+ static_assert(
+ SubsecondRatio::num == 1, "subsecond numerator should always be 1");
+
+ auto sec = chronoRangeCheck(duration.count());
+ if (sec.hasError()) {
+ return makeUnexpected(sec.error());
+ }
+
+ time_t secValue = sec.value();
+ long subsec = 0L;
+ if (std::is_floating_point<Rep>::value) {
+ auto fraction = (duration.count() - secValue);
+ subsec = static_cast<long>(fraction * SubsecondRatio::den);
+ if (duration.count() < 0 && fraction < 0) {
+ if (secValue == std::numeric_limits<time_t>::lowest()) {
+ return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
+ }
+ secValue -= 1;
+ subsec += SubsecondRatio::den;
+ }
+ }
+ return std::pair<time_t, long>{secValue, subsec};
+}
+
+/**
+ * Convert a std::chrono::duration with subsecond granularity to a pair of
+ * (seconds, subseconds)
+ */
+template <typename SubsecondRatio, typename Rep, std::intmax_t Denominator>
+static Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(
+ const std::chrono::duration<Rep, std::ratio<1, Denominator>>& duration) {
+ static_assert(Denominator != 1, "special case expecting den != 1");
+ static_assert(
+ SubsecondRatio::num == 1, "subsecond numerator should always be 1");
+
+ auto sec = chronoRangeCheck(duration.count() / Denominator);
+ if (sec.hasError()) {
+ return makeUnexpected(sec.error());
+ }
+ auto secTimeT = sec.value();
+
+ auto remainder = duration.count() - (secTimeT * Denominator);
+ auto subsec = (remainder * SubsecondRatio::den) / Denominator;
+ if (UNLIKELY(duration.count() < 0) && remainder != 0) {
+ if (secTimeT == std::numeric_limits<time_t>::lowest()) {
+ return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
+ }
+ secTimeT -= 1;
+ subsec += SubsecondRatio::den;
+ }
+
+ return std::pair<time_t, long>{secTimeT, subsec};
+}
+
+/**
+ * Convert a std::chrono::duration with coarser-than-second granularity to a
+ * pair of (seconds, subseconds)
+ */
+template <typename SubsecondRatio, typename Rep, std::intmax_t Numerator>
+static Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(
+ const std::chrono::duration<Rep, std::ratio<Numerator, 1>>& duration) {
+ static_assert(Numerator != 1, "special case expecting num!=1");
+ static_assert(
+ SubsecondRatio::num == 1, "subsecond numerator should always be 1");
+
+ constexpr auto maxValue = std::numeric_limits<time_t>::max() / Numerator;
+ constexpr auto minValue = std::numeric_limits<time_t>::lowest() / Numerator;
+ if (duration.count() > maxValue) {
+ return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);
+ }
+ if (duration.count() < minValue) {
+ return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
+ }
+
+ // Note that we can't use chronoRangeCheck() here since we have to check
+ // if (duration.count() * Numerator) would overflow (which we do above).
+ auto secOriginalRep = (duration.count() * Numerator);
+ auto sec = static_cast<time_t>(secOriginalRep);
+
+ long subsec = 0L;
+ if (std::is_floating_point<Rep>::value) {
+ auto fraction = secOriginalRep - sec;
+ subsec = static_cast<long>(fraction * SubsecondRatio::den);
+ if (duration.count() < 0 && fraction < 0) {
+ if (sec == std::numeric_limits<time_t>::lowest()) {
+ return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
+ }
+ sec -= 1;
+ subsec += SubsecondRatio::den;
+ }
+ }
+ return std::pair<time_t, long>{sec, subsec};
+}
+
+/**
+ * Convert a std::chrono::duration to a pair of (seconds, subseconds)
+ *
+ * This overload is only used for unusual durations where neither the numerator
+ * nor denominator are 1.
+ */
+template <typename SubsecondRatio, typename Rep, typename Period>
+Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(
+ const std::chrono::duration<Rep, Period>& duration) {
+ static_assert(Period::num != 1, "should use special-case code when num==1");
+ static_assert(Period::den != 1, "should use special-case code when den==1");
+ static_assert(
+ SubsecondRatio::num == 1, "subsecond numerator should always be 1");
+
+ // TODO: We need to implement an overflow-checking tryTo() function for
+ // duration-to-duration casts for the code above to work.
+ //
+ // For now this is unimplemented, and we just have a static_assert that
+ // will always fail if someone tries to instantiate this. Unusual duration
+ // types should be extremely rare, and I'm not aware of any code at the
+ // moment that actually needs this.
+ static_assert(
+ Period::num == 1,
+ "conversion from unusual duration types is not implemented yet");
+ (void)duration;
+ return makeUnexpected(ConversionCode::SUCCESS);
+}
+
+/**
+ * Check for overflow when converting to a duration type that is second
+ * granularity or finer (e.g., nanoseconds, milliseconds, seconds)
+ *
+ * This assumes the input is normalized, with subseconds >= 0 and subseconds
+ * less than 1 second.
+ */
+template <bool IsFloatingPoint>
+struct CheckOverflowToDuration {
+ template <
+ typename Tgt,
+ typename SubsecondRatio,
+ typename Seconds,
+ typename Subseconds>
+ static ConversionCode check(Seconds seconds, Subseconds subseconds) {
+ static_assert(
+ Tgt::period::num == 1,
+ "this implementation should only be used for subsecond granularity "
+ "duration types");
+ static_assert(
+ !std::is_floating_point<typename Tgt::rep>::value, "incorrect usage");
+ static_assert(
+ SubsecondRatio::num == 1, "subsecond numerator should always be 1");
+
+ if (LIKELY(seconds >= 0)) {
+ constexpr auto maxCount = std::numeric_limits<typename Tgt::rep>::max();
+ constexpr auto maxSeconds = maxCount / Tgt::period::den;
+
+ auto unsignedSeconds =
+ static_cast<typename std::make_unsigned<Seconds>::type>(seconds);
+ if (LIKELY(unsignedSeconds < maxSeconds)) {
+ return ConversionCode::SUCCESS;
+ }
+
+ if (UNLIKELY(unsignedSeconds == maxSeconds)) {
+ constexpr auto maxRemainder =
+ maxCount - (maxSeconds * Tgt::period::den);
+ constexpr auto maxSubseconds =
+ (maxRemainder * SubsecondRatio::den) / Tgt::period::den;
+ if (subseconds <= 0) {
+ return ConversionCode::SUCCESS;
+ }
+ if (static_cast<typename std::make_unsigned<Subseconds>::type>(
+ subseconds) <= maxSubseconds) {
+ return ConversionCode::SUCCESS;
+ }
+ }
+ return ConversionCode::POSITIVE_OVERFLOW;
+ } else if (std::is_unsigned<typename Tgt::rep>::value) {
+ return ConversionCode::NEGATIVE_OVERFLOW;
+ } else {
+ constexpr auto minCount =
+ static_cast<typename std::make_signed<typename Tgt::rep>::type>(
+ std::numeric_limits<typename Tgt::rep>::lowest());
+ constexpr auto minSeconds = (minCount / Tgt::period::den);
+ if (LIKELY(seconds >= minSeconds)) {
+ return ConversionCode::SUCCESS;
+ }
+
+ if (UNLIKELY(seconds == minSeconds - 1)) {
+ constexpr auto maxRemainder =
+ minCount - (minSeconds * Tgt::period::den) + Tgt::period::den;
+ constexpr auto maxSubseconds =
+ (maxRemainder * SubsecondRatio::den) / Tgt::period::den;
+ if (subseconds <= 0) {
+ return ConversionCode::NEGATIVE_OVERFLOW;
+ }
+ if (subseconds >= maxSubseconds) {
+ return ConversionCode::SUCCESS;
+ }
+ }
+ return ConversionCode::NEGATIVE_OVERFLOW;
+ }
+ }
+};
+
+template <>
+struct CheckOverflowToDuration<true> {
+ template <
+ typename Tgt,
+ typename SubsecondRatio,
+ typename Seconds,
+ typename Subseconds>
+ static ConversionCode check(
+ Seconds /* seconds */,
+ Subseconds /* subseconds */) {
+ static_assert(
+ std::is_floating_point<typename Tgt::rep>::value, "incorrect usage");
+ static_assert(
+ SubsecondRatio::num == 1, "subsecond numerator should always be 1");
+
+ // We expect floating point types to have much a wider representable range
+ // than integer types, so we don't bother actually checking the input
+ // integer value here.
+ static_assert(
+ std::numeric_limits<typename Tgt::rep>::max() >=
+ std::numeric_limits<Seconds>::max(),
+ "unusually limited floating point type");
+ static_assert(
+ std::numeric_limits<typename Tgt::rep>::lowest() <=
+ std::numeric_limits<Seconds>::lowest(),
+ "unusually limited floating point type");
+
+ return ConversionCode::SUCCESS;
+ }
+};
+
+/**
+ * Helper class to convert a POSIX-style pair of (seconds, subseconds)
+ * to a std::chrono::duration type.
+ *
+ * The SubsecondRatio template parameter specifies what type of subseconds to
+ * return. This must have a numerator of 1.
+ *
+ * The input must be in normalized form: the subseconds field must be greater
+ * than or equal to 0, and less than SubsecondRatio::den (i.e., less than 1
+ * second).
+ *
+ * This default implementation is only used for unusual std::chrono::duration
+ * types where neither the numerator nor denominator are 1.
+ */
+template <typename Tgt>
+struct PosixTimeToDuration {
+ template <typename SubsecondRatio, typename Seconds, typename Subseconds>
+ static Expected<Tgt, ConversionCode> cast(
+ Seconds seconds,
+ Subseconds subseconds);
+};
+
+/**
+ * Convert a timeval or a timespec to a std::chrono::duration with second
+ * granularity.
+ */
+template <typename Rep>
+struct PosixTimeToDuration<std::chrono::duration<Rep, std::ratio<1, 1>>> {
+ using Tgt = std::chrono::duration<Rep, std::ratio<1, 1>>;
+
+ template <typename SubsecondRatio, typename Seconds, typename Subseconds>
+ static Expected<Tgt, ConversionCode> cast(
+ Seconds seconds,
+ Subseconds subseconds) {
+ static_assert(Tgt::period::num == 1, "special case expecting num==1");
+ static_assert(Tgt::period::den == 1, "special case expecting den==1");
+ static_assert(
+ SubsecondRatio::num == 1, "subsecond numerator should always be 1");
+
+ auto outputSeconds = tryTo<typename Tgt::rep>(seconds);
+ if (outputSeconds.hasError()) {
+ return makeUnexpected(outputSeconds.error());
+ }
+
+ if (std::is_floating_point<typename Tgt::rep>::value) {
+ return Tgt{typename Tgt::rep(seconds) +
+ (typename Tgt::rep(subseconds) / SubsecondRatio::den)};
+ }
+
+ // If the value is negative, we have to round up a non-zero subseconds value
+ if (UNLIKELY(outputSeconds.value() < 0) && subseconds > 0) {
+ if (UNLIKELY(
+ outputSeconds.value() ==
+ std::numeric_limits<typename Tgt::rep>::lowest())) {
+ return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
+ }
+ return Tgt{outputSeconds.value() + 1};
+ }
+
+ return Tgt{outputSeconds.value()};
+ }
+};
+
+/**
+ * Convert a timeval or a timespec to a std::chrono::duration with subsecond
+ * granularity
+ */
+template <typename Rep, std::intmax_t Denominator>
+struct PosixTimeToDuration<
+ std::chrono::duration<Rep, std::ratio<1, Denominator>>> {
+ using Tgt = std::chrono::duration<Rep, std::ratio<1, Denominator>>;
+
+ template <typename SubsecondRatio, typename Seconds, typename Subseconds>
+ static Expected<Tgt, ConversionCode> cast(
+ Seconds seconds,
+ Subseconds subseconds) {
+ static_assert(Tgt::period::num == 1, "special case expecting num==1");
+ static_assert(Tgt::period::den != 1, "special case expecting den!=1");
+ static_assert(
+ SubsecondRatio::num == 1, "subsecond numerator should always be 1");
+
+ auto errorCode = detail::CheckOverflowToDuration<
+ std::is_floating_point<typename Tgt::rep>::value>::
+ template check<Tgt, SubsecondRatio>(seconds, subseconds);
+ if (errorCode != ConversionCode::SUCCESS) {
+ return makeUnexpected(errorCode);
+ }
+
+ if (LIKELY(seconds >= 0)) {
+ return std::chrono::duration_cast<Tgt>(
+ std::chrono::duration<typename Tgt::rep>{seconds}) +
+ std::chrono::duration_cast<Tgt>(
+ std::chrono::duration<typename Tgt::rep, SubsecondRatio>{
+ subseconds});
+ } else {
+ // For negative numbers we have to round subseconds up towards zero, even
+ // though it is a positive value, since the overall value is negative.
+ return std::chrono::duration_cast<Tgt>(
+ std::chrono::duration<typename Tgt::rep>{seconds + 1}) -
+ std::chrono::duration_cast<Tgt>(
+ std::chrono::duration<typename Tgt::rep, SubsecondRatio>{
+ SubsecondRatio::den - subseconds});
+ }
+ }
+};
+
+/**
+ * Convert a timeval or a timespec to a std::chrono::duration with
+ * granularity coarser than 1 second.
+ */
+template <typename Rep, std::intmax_t Numerator>
+struct PosixTimeToDuration<
+ std::chrono::duration<Rep, std::ratio<Numerator, 1>>> {
+ using Tgt = std::chrono::duration<Rep, std::ratio<Numerator, 1>>;
+
+ template <typename SubsecondRatio, typename Seconds, typename Subseconds>
+ static Expected<Tgt, ConversionCode> cast(
+ Seconds seconds,
+ Subseconds subseconds) {
+ static_assert(Tgt::period::num != 1, "special case expecting num!=1");
+ static_assert(Tgt::period::den == 1, "special case expecting den==1");
+ static_assert(
+ SubsecondRatio::num == 1, "subsecond numerator should always be 1");
+
+ if (UNLIKELY(seconds < 0) && subseconds > 0) {
+ // Increment seconds by one to handle truncation of negative numbers
+ // properly.
+ if (UNLIKELY(seconds == std::numeric_limits<Seconds>::lowest())) {
+ return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
+ }
+ seconds += 1;
+ }
+
+ if (std::is_floating_point<typename Tgt::rep>::value) {
+ // Convert to the floating point type before performing the division
+ return Tgt{static_cast<typename Tgt::rep>(seconds) / Tgt::period::num};
+ } else {
+ // Perform the division as an integer, and check that the result fits in
+ // the output integer type
+ auto outputValue = (seconds / Tgt::period::num);
+ auto expectedOuput = tryTo<typename Tgt::rep>(outputValue);
+ if (expectedOuput.hasError()) {
+ return makeUnexpected(expectedOuput.error());
+ }
+
+ return Tgt{expectedOuput.value()};
+ }
+ }
+};
+
+/**
+ * PosixTimeToDuration::cast() implementation for the default case
+ * with unusual durations where neither the numerator nor denominator are 1.
+ */
+template <typename Tgt>
+template <typename SubsecondRatio, typename Seconds, typename Subseconds>
+Expected<Tgt, ConversionCode> PosixTimeToDuration<Tgt>::cast(
+ Seconds seconds,
+ Subseconds subseconds) {
+ static_assert(
+ Tgt::period::num != 1, "should use special-case code when num==1");
+ static_assert(
+ Tgt::period::den != 1, "should use special-case code when den==1");
+ static_assert(
+ SubsecondRatio::num == 1, "subsecond numerator should always be 1");
+
+ // TODO: We need to implement an overflow-checking tryTo() function for
+ // duration-to-duration casts for the code above to work.
+ //
+ // For now this is unimplemented, and we just have a static_assert that
+ // will always fail if someone tries to instantiate this. Unusual duration
+ // types should be extremely rare, and I'm not aware of any code at the
+ // moment that actually needs this.
+ static_assert(
+ Tgt::period::num == 1,
+ "conversion to unusual duration types is not implemented yet");
+ (void)seconds;
+ (void)subseconds;
+ return makeUnexpected(ConversionCode::SUCCESS);
+}
+
+template <
+ typename Tgt,
+ typename SubsecondRatio,
+ typename Seconds,
+ typename Subseconds>
+Expected<Tgt, ConversionCode> tryPosixTimeToDuration(
+ Seconds seconds,
+ Subseconds subseconds) {
+ static_assert(
+ SubsecondRatio::num == 1, "subsecond numerator should always be 1");
+
+ // Normalize the input if required
+ if (UNLIKELY(subseconds < 0)) {
+ const auto overflowSeconds = (subseconds / SubsecondRatio::den);
+ const auto remainder = (subseconds % SubsecondRatio::den);
+ if (std::numeric_limits<Seconds>::lowest() + 1 - overflowSeconds >
+ seconds) {
+ return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
+ }
+ seconds = seconds - 1 + overflowSeconds;
+ subseconds = remainder + SubsecondRatio::den;
+ } else if (UNLIKELY(subseconds >= SubsecondRatio::den)) {
+ const auto overflowSeconds = (subseconds / SubsecondRatio::den);
+ const auto remainder = (subseconds % SubsecondRatio::den);
+ if (std::numeric_limits<Seconds>::max() - overflowSeconds < seconds) {
+ return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);
+ }
+ seconds += overflowSeconds;
+ subseconds = remainder;
+ }
+
+ using Converter = PosixTimeToDuration<Tgt>;
+ return Converter::template cast<SubsecondRatio>(seconds, subseconds);
+}
+
+} // namespace detail
+
+/**
+ * struct timespec to std::chrono::duration
+ */
+template <typename Tgt>
+typename std::enable_if<
+ detail::is_duration<Tgt>::value,
+ Expected<Tgt, ConversionCode>>::type
+tryTo(const struct timespec& ts) {
+ return detail::tryPosixTimeToDuration<Tgt, std::nano>(ts.tv_sec, ts.tv_nsec);
+}
+
+/**
+ * struct timeval to std::chrono::duration
+ */
+template <typename Tgt>
+typename std::enable_if<
+ detail::is_duration<Tgt>::value,
+ Expected<Tgt, ConversionCode>>::type
+tryTo(const struct timeval& tv) {
+ return detail::tryPosixTimeToDuration<Tgt, std::micro>(tv.tv_sec, tv.tv_usec);
+}
+
+/**
+ * timespec or timeval to std::chrono::time_point
+ */
+template <typename Tgt, typename Src>
+typename std::enable_if<
+ detail::is_time_point<Tgt>::value && detail::is_posix_time_type<Src>::value,
+ Expected<Tgt, ConversionCode>>::type
+tryTo(const Src& value) {
+ return tryTo<typename Tgt::duration>(value).then(
+ [](typename Tgt::duration result) { return Tgt(result); });
+}
+
+/**
+ * std::chrono::duration to struct timespec
+ */
+template <typename Tgt, typename Rep, typename Period>
+typename std::enable_if<
+ std::is_same<Tgt, struct timespec>::value,
+ Expected<Tgt, ConversionCode>>::type
+tryTo(const std::chrono::duration<Rep, Period>& duration) {
+ auto result = detail::durationToPosixTime<std::nano>(duration);
+ if (result.hasError()) {
+ return makeUnexpected(result.error());
+ }
+
+ struct timespec ts;
+ ts.tv_sec = result.value().first;
+ ts.tv_nsec = result.value().second;
+ return ts;
+}
+
+/**
+ * std::chrono::duration to struct timeval
+ */
+template <typename Tgt, typename Rep, typename Period>
+typename std::enable_if<
+ std::is_same<Tgt, struct timeval>::value,
+ Expected<Tgt, ConversionCode>>::type
+tryTo(const std::chrono::duration<Rep, Period>& duration) {
+ auto result = detail::durationToPosixTime<std::micro>(duration);
+ if (result.hasError()) {
+ return makeUnexpected(result.error());
+ }
+
+ struct timeval tv;
+ tv.tv_sec = result.value().first;
+ tv.tv_usec = result.value().second;
+ return tv;
+}
+
+/**
+ * std::chrono::time_point to timespec or timeval
+ */
+template <typename Tgt, typename Clock, typename Duration>
+typename std::enable_if<
+ detail::is_posix_time_type<Tgt>::value,
+ Expected<Tgt, ConversionCode>>::type
+tryTo(const std::chrono::time_point<Clock, Duration>& timePoint) {
+ return tryTo<Tgt>(timePoint.time_since_epoch());
+}
+
+/**
+ * For all chrono conversions, to() wraps tryTo()
+ */
+template <typename Tgt, typename Src>
+typename std::enable_if<detail::is_chrono_conversion<Tgt, Src>::value, Tgt>::
+ type
+ to(const Src& value) {
+ return tryTo<Tgt>(value).thenOrThrow(
+ [](Tgt res) { return res; },
+ [&](ConversionCode e) { return makeConversionError(e, StringPiece{}); });
+}
+
+} // namespace folly
--- /dev/null
+/*
+ * Copyright 2004-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <folly/chrono/Conv.h>
+
+#include <folly/portability/GTest.h>
+
+using namespace folly;
+using namespace std::chrono;
+using namespace std::chrono_literals;
+
+namespace {
+/**
+ * A helper function to create a time_point even if the input duration type has
+ * finer resolution than the clock duration type.
+ */
+template <typename Clock, typename Duration>
+typename Clock::time_point createTimePoint(const Duration& d) {
+ return typename Clock::time_point(
+ std::chrono::duration_cast<typename Clock::duration>(d));
+}
+} // namespace
+
+TEST(Conv, timespecToStdChrono) {
+ struct timespec ts;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 10;
+ EXPECT_EQ(10ns, to<nanoseconds>(ts));
+ EXPECT_EQ(0us, to<microseconds>(ts));
+ EXPECT_EQ(0ms, to<milliseconds>(ts));
+ EXPECT_EQ(0s, to<seconds>(ts));
+
+ ts.tv_sec = 1;
+ ts.tv_nsec = 10;
+ EXPECT_EQ(1000000010ns, to<nanoseconds>(ts));
+ EXPECT_EQ(1000000us, to<microseconds>(ts));
+ EXPECT_EQ(1000ms, to<milliseconds>(ts));
+ EXPECT_EQ(1s, to<seconds>(ts));
+ EXPECT_EQ(
+ createTimePoint<system_clock>(1000000010ns),
+ to<system_clock::time_point>(ts));
+ EXPECT_EQ(
+ createTimePoint<steady_clock>(1000000010ns),
+ to<steady_clock::time_point>(ts));
+
+ // Test a non-canonical value with tv_nsec larger than 1 second
+ ts.tv_sec = 5;
+ ts.tv_nsec = 3219876543;
+ // Beware about using std::chrono_literals suffixes with very literals:
+ // older versions of GCC are buggy and would truncate these to 32-bits.
+ EXPECT_EQ(8219876543LL, to<nanoseconds>(ts).count());
+ EXPECT_EQ(8219876us, to<microseconds>(ts));
+ EXPECT_EQ(8219ms, to<milliseconds>(ts));
+ EXPECT_EQ(8s, to<seconds>(ts));
+ EXPECT_EQ(
+ createTimePoint<system_clock>(nanoseconds(8219876543LL)),
+ to<system_clock::time_point>(ts));
+ EXPECT_EQ(
+ createTimePoint<steady_clock>(nanoseconds(8219876543LL)),
+ to<steady_clock::time_point>(ts));
+
+ // Test negative values
+ // When going to coarser grained types these should be rounded up towards 0.
+ ts.tv_sec = -5;
+ ts.tv_nsec = 123456;
+ EXPECT_EQ(-4999876544, to<nanoseconds>(ts).count());
+ EXPECT_EQ(-4999876544, duration_cast<nanoseconds>(-5s + 123456ns).count());
+ EXPECT_EQ(-4999876, to<microseconds>(ts).count());
+ EXPECT_EQ(-4999876, duration_cast<microseconds>(-5s + 123456ns).count());
+ EXPECT_EQ(-4999, to<milliseconds>(ts).count());
+ EXPECT_EQ(-4999, duration_cast<milliseconds>(-5s + 123456ns).count());
+ EXPECT_EQ(-4s, to<seconds>(ts));
+ EXPECT_EQ(-4, duration_cast<seconds>(-5s + 123456ns).count());
+ ts.tv_sec = -7200;
+ ts.tv_nsec = 123456;
+ EXPECT_EQ(-1h, to<hours>(ts));
+ EXPECT_EQ(
+ -1,
+ duration_cast<hours>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec})
+ .count());
+ ts.tv_sec = -7000;
+ ts.tv_nsec = 123456;
+ EXPECT_EQ(-1h, to<hours>(ts));
+ EXPECT_EQ(
+ -1,
+ duration_cast<hours>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec})
+ .count());
+ ts.tv_sec = -7201;
+ ts.tv_nsec = 123456;
+ EXPECT_EQ(-2h, to<hours>(ts));
+ EXPECT_EQ(
+ -2,
+ duration_cast<hours>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec})
+ .count());
+
+ // Test converions to floating point durations
+ ts.tv_sec = 1;
+ ts.tv_nsec = 500000000;
+ EXPECT_EQ(1.5, to<duration<double>>(ts).count());
+ ts.tv_sec = -1;
+ ts.tv_nsec = 500000000;
+ EXPECT_EQ(-0.5, to<duration<double>>(ts).count());
+ ts.tv_sec = -1;
+ ts.tv_nsec = -500000000;
+ EXPECT_EQ(-1.5, to<duration<double>>(ts).count());
+ ts.tv_sec = 1;
+ ts.tv_nsec = 500000000;
+ auto doubleNanos = to<duration<double, std::nano>>(ts);
+ EXPECT_EQ(1500000000, doubleNanos.count());
+ ts.tv_sec = 90;
+ ts.tv_nsec = 0;
+ auto doubleMinutes = to<duration<double, std::ratio<60>>>(ts);
+ EXPECT_EQ(1.5, doubleMinutes.count());
+}
+
+TEST(Conv, timespecToStdChronoOverflow) {
+ struct timespec ts;
+
+ // All of our boundary conditions below assume time_t is int64_t.
+ // This is true on most modern platforms.
+ if (!std::is_same<decltype(ts.tv_sec), int64_t>::value) {
+ LOG(INFO) << "skipping most overflow tests: time_t is not int64_t";
+ } else {
+ // Test the upper boundary of conversion to uint64_t nanoseconds
+ using nsec_u64 = std::chrono::duration<uint64_t, std::nano>;
+ ts.tv_sec = 18446744073;
+ ts.tv_nsec = 709551615;
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max(), to<nsec_u64>(ts).count());
+
+ ts.tv_nsec += 1;
+ EXPECT_THROW(to<nsec_u64>(ts), std::range_error);
+
+ // Test the lower boundary of conversion to uint64_t nanoseconds
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ EXPECT_EQ(0, to<nsec_u64>(ts).count());
+ ts.tv_sec = -1;
+ ts.tv_nsec = 0;
+ EXPECT_THROW(to<nsec_u64>(ts), std::range_error);
+
+ // Test the upper boundary of conversion to int64_t microseconds
+ using usec_i64 = std::chrono::duration<int64_t, std::micro>;
+ ts.tv_sec = 9223372036854LL;
+ ts.tv_nsec = 775807000;
+ EXPECT_EQ(std::numeric_limits<int64_t>::max(), to<usec_i64>(ts).count());
+
+ ts.tv_nsec += 1;
+ EXPECT_THROW(to<usec_i64>(ts), std::range_error);
+
+ // Test the lower boundary of conversion to int64_t microseconds
+ ts.tv_sec = -9223372036855LL;
+ ts.tv_nsec = 224192000;
+ EXPECT_EQ(std::numeric_limits<int64_t>::min(), to<usec_i64>(ts).count());
+
+ ts.tv_nsec -= 1;
+ EXPECT_THROW(to<usec_i64>(ts), std::range_error);
+
+ // Test the boundaries of conversion to int32_t seconds
+ using sec_i32 = std::chrono::duration<int32_t>;
+ ts.tv_sec = 2147483647;
+ ts.tv_nsec = 0;
+ EXPECT_EQ(std::numeric_limits<int32_t>::max(), to<sec_i32>(ts).count());
+ ts.tv_nsec = 1000000000;
+ EXPECT_THROW(to<sec_i32>(ts), std::range_error);
+ ts.tv_sec = -2147483648;
+ ts.tv_nsec = 0;
+ EXPECT_EQ(std::numeric_limits<int32_t>::min(), to<sec_i32>(ts).count());
+ ts.tv_sec = -2147483649;
+ ts.tv_nsec = 999999999;
+ EXPECT_THROW(to<sec_i32>(ts), std::range_error);
+ ts.tv_sec = -2147483649;
+ ts.tv_nsec = 0;
+ EXPECT_THROW(to<sec_i32>(ts), std::range_error);
+ ts.tv_sec = -2147483650;
+ ts.tv_nsec = 0;
+ EXPECT_THROW(to<sec_i32>(ts), std::range_error);
+
+ // Test the upper boundary of conversion to uint32_t hours
+ using hours_u32 = std::chrono::duration<uint32_t, std::ratio<3600>>;
+ ts.tv_sec = 15461882262000LL;
+ ts.tv_nsec = 0;
+ EXPECT_EQ(std::numeric_limits<uint32_t>::max(), to<hours_u32>(ts).count());
+ ts.tv_sec = 15461882265599LL;
+ EXPECT_EQ(std::numeric_limits<uint32_t>::max(), to<hours_u32>(ts).count());
+ ts.tv_sec = 15461882265600LL;
+ EXPECT_THROW(to<hours_u32>(ts), std::range_error);
+
+ using nsec_i64 = std::chrono::duration<int64_t, std::nano>;
+ ts.tv_sec = std::numeric_limits<int64_t>::max();
+ ts.tv_nsec = std::numeric_limits<int64_t>::max();
+ EXPECT_THROW(to<nsec_i64>(ts), std::range_error);
+
+ ts.tv_sec = std::numeric_limits<int64_t>::min();
+ ts.tv_nsec = std::numeric_limits<int64_t>::min();
+ EXPECT_THROW(to<nsec_i64>(ts), std::range_error);
+
+ // Test some non-normal inputs near the int64_t limit
+ ts.tv_sec = 0;
+ ts.tv_nsec = std::numeric_limits<int64_t>::min();
+ EXPECT_EQ(std::numeric_limits<int64_t>::min(), to<nsec_i64>(ts).count());
+ ts.tv_sec = -1;
+ ts.tv_nsec = std::numeric_limits<int64_t>::min() + std::nano::den;
+ EXPECT_EQ(std::numeric_limits<int64_t>::min(), to<nsec_i64>(ts).count());
+ ts.tv_sec = -1;
+ ts.tv_nsec = std::numeric_limits<int64_t>::min() + std::nano::den - 1;
+ EXPECT_THROW(to<nsec_i64>(ts), std::range_error);
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = std::numeric_limits<int64_t>::max();
+ EXPECT_EQ(std::numeric_limits<int64_t>::max(), to<nsec_i64>(ts).count());
+ ts.tv_sec = 1;
+ ts.tv_nsec = std::numeric_limits<int64_t>::max() - std::nano::den;
+ EXPECT_EQ(std::numeric_limits<int64_t>::max(), to<nsec_i64>(ts).count());
+ ts.tv_sec = 1;
+ ts.tv_nsec = std::numeric_limits<int64_t>::max() - std::nano::den + 1;
+ EXPECT_THROW(to<nsec_i64>(ts), std::range_error);
+ }
+
+ // Theoretically conversion is representable in the output type,
+ // but we normalize the input first, and normalization would trigger an
+ // overflow.
+ using hours_u64 = std::chrono::duration<uint64_t, std::ratio<3600>>;
+ ts.tv_sec = std::numeric_limits<decltype(ts.tv_sec)>::max();
+ ts.tv_nsec = 1000000000;
+ EXPECT_THROW(to<hours_u64>(ts), std::range_error);
+ // If we drop it back down to the normal range it should succeed
+ ts.tv_nsec = 999999999;
+ EXPECT_EQ(
+ std::numeric_limits<decltype(ts.tv_sec)>::max() / 3600,
+ to<hours_u64>(ts).count());
+}
+
+TEST(Conv, timevalToStdChrono) {
+ struct timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 10;
+ EXPECT_EQ(10000ns, to<nanoseconds>(tv));
+ EXPECT_EQ(10us, to<microseconds>(tv));
+ EXPECT_EQ(0ms, to<milliseconds>(tv));
+ EXPECT_EQ(0s, to<seconds>(tv));
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 10;
+ EXPECT_EQ(1000010000ns, to<nanoseconds>(tv));
+ EXPECT_EQ(1000010us, to<microseconds>(tv));
+ EXPECT_EQ(1000ms, to<milliseconds>(tv));
+ EXPECT_EQ(1s, to<seconds>(tv));
+ EXPECT_EQ(
+ createTimePoint<system_clock>(1000010000ns),
+ to<system_clock::time_point>(tv));
+ EXPECT_EQ(
+ createTimePoint<steady_clock>(1000010000ns),
+ to<steady_clock::time_point>(tv));
+
+ // Test a non-canonical value with tv_usec larger than 1 second
+ tv.tv_sec = 5;
+ tv.tv_usec = 3219876;
+ EXPECT_EQ(8219876000LL, to<nanoseconds>(tv).count());
+ EXPECT_EQ(8219876us, to<microseconds>(tv));
+ EXPECT_EQ(8219ms, to<milliseconds>(tv));
+ EXPECT_EQ(8s, to<seconds>(tv));
+ EXPECT_EQ(
+ createTimePoint<system_clock>(nanoseconds(8219876000LL)),
+ to<system_clock::time_point>(tv));
+ EXPECT_EQ(
+ createTimePoint<steady_clock>(nanoseconds(8219876000LL)),
+ to<steady_clock::time_point>(tv));
+
+ // Test for overflow.
+ if (std::numeric_limits<decltype(tv.tv_sec)>::max() >=
+ std::numeric_limits<int64_t>::max()) {
+ // Use our own type alias here rather than std::chrono::nanoseconds
+ // to ensure we have 64-bit rep type.
+ using nsec_i64 = std::chrono::duration<int64_t, std::nano>;
+ tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::max();
+ tv.tv_usec = std::numeric_limits<decltype(tv.tv_usec)>::max();
+ EXPECT_THROW(to<nsec_i64>(tv), std::range_error);
+
+ tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::min();
+ tv.tv_usec = std::numeric_limits<decltype(tv.tv_usec)>::max();
+ EXPECT_THROW(to<nsec_i64>(tv), std::range_error);
+ }
+}
+
+TEST(Conv, stdChronoToTimespec) {
+ auto ts = to<struct timespec>(10ns);
+ EXPECT_EQ(0, ts.tv_sec);
+ EXPECT_EQ(10, ts.tv_nsec);
+
+ // We don't use std::chrono_literals suffixes here since older
+ // gcc versions silently truncate the literals to 32-bits.
+ ts = to<struct timespec>(nanoseconds(987654321012LL));
+ EXPECT_EQ(987, ts.tv_sec);
+ EXPECT_EQ(654321012, ts.tv_nsec);
+
+ ts = to<struct timespec>(nanoseconds(-987654321012LL));
+ EXPECT_EQ(-988, ts.tv_sec);
+ EXPECT_EQ(345678988, ts.tv_nsec);
+
+ ts = to<struct timespec>(microseconds(987654321012LL));
+ EXPECT_EQ(987654, ts.tv_sec);
+ EXPECT_EQ(321012000, ts.tv_nsec);
+
+ ts = to<struct timespec>(milliseconds(987654321012LL));
+ EXPECT_EQ(987654321, ts.tv_sec);
+ EXPECT_EQ(12000000, ts.tv_nsec);
+
+ ts = to<struct timespec>(seconds(987654321012LL));
+ EXPECT_EQ(987654321012, ts.tv_sec);
+ EXPECT_EQ(0, ts.tv_nsec);
+
+ ts = to<struct timespec>(10h);
+ EXPECT_EQ(36000, ts.tv_sec);
+ EXPECT_EQ(0, ts.tv_nsec);
+
+ ts = to<struct timespec>(createTimePoint<steady_clock>(123ns));
+ EXPECT_EQ(0, ts.tv_sec);
+ EXPECT_EQ(123, ts.tv_nsec);
+
+ ts = to<struct timespec>(createTimePoint<system_clock>(123ns));
+ EXPECT_EQ(0, ts.tv_sec);
+ EXPECT_EQ(123, ts.tv_nsec);
+}
+
+TEST(Conv, stdChronoToTimespecOverflow) {
+ EXPECT_THROW(to<uint8_t>(1234), std::range_error);
+
+ struct timespec ts;
+ if (!std::is_same<decltype(ts.tv_sec), int64_t>::value) {
+ LOG(INFO) << "skipping most overflow tests: time_t is not int64_t";
+ } else {
+ // Check for overflow converting from uint64_t seconds to time_t
+ using sec_u64 = duration<uint64_t>;
+ ts = to<struct timespec>(sec_u64(9223372036854775807ULL));
+ EXPECT_EQ(ts.tv_sec, 9223372036854775807ULL);
+ EXPECT_EQ(ts.tv_nsec, 0);
+
+ EXPECT_THROW(
+ to<struct timespec>(sec_u64(9223372036854775808ULL)), std::range_error);
+
+ // Check for overflow converting from int64_t hours to time_t
+ using hours_i64 = duration<int64_t, std::ratio<3600>>;
+ ts = to<struct timespec>(hours_i64(2562047788015215LL));
+ EXPECT_EQ(ts.tv_sec, 9223372036854774000LL);
+ EXPECT_EQ(ts.tv_nsec, 0);
+ EXPECT_THROW(
+ to<struct timespec>(hours_i64(2562047788015216LL)), std::range_error);
+ }
+
+ // Test for overflow.
+ // Use a custom hours type using time_t as the underlying storage type to
+ // guarantee that we can overflow.
+ using hours_timet = std::chrono::duration<time_t, std::ratio<3600>>;
+ EXPECT_THROW(
+ to<struct timespec>(hours_timet(std::numeric_limits<time_t>::max())),
+ std::range_error);
+}
+
+TEST(Conv, stdChronoToTimeval) {
+ auto tv = to<struct timeval>(10ns);
+ EXPECT_EQ(0, tv.tv_sec);
+ EXPECT_EQ(0, tv.tv_usec);
+
+ tv = to<struct timeval>(10us);
+ EXPECT_EQ(0, tv.tv_sec);
+ EXPECT_EQ(10, tv.tv_usec);
+
+ tv = to<struct timeval>(nanoseconds(987654321012LL));
+ EXPECT_EQ(987, tv.tv_sec);
+ EXPECT_EQ(654321, tv.tv_usec);
+
+ tv = to<struct timeval>(nanoseconds(-987654321012LL));
+ EXPECT_EQ(-988, tv.tv_sec);
+ EXPECT_EQ(345679, tv.tv_usec);
+
+ tv = to<struct timeval>(microseconds(987654321012LL));
+ EXPECT_EQ(987654, tv.tv_sec);
+ EXPECT_EQ(321012, tv.tv_usec);
+
+ tv = to<struct timeval>(milliseconds(987654321012LL));
+ EXPECT_EQ(987654321, tv.tv_sec);
+ EXPECT_EQ(12000, tv.tv_usec);
+
+ tv = to<struct timeval>(seconds(987654321012LL));
+ EXPECT_EQ(987654321012, tv.tv_sec);
+ EXPECT_EQ(0, tv.tv_usec);
+
+ // Try converting fractional seconds
+ tv = to<struct timeval>(duration<double>{3.456789});
+ EXPECT_EQ(3, tv.tv_sec);
+ EXPECT_EQ(456789, tv.tv_usec);
+ tv = to<struct timeval>(duration<double>{-3.456789});
+ EXPECT_EQ(-4, tv.tv_sec);
+ EXPECT_EQ(543211, tv.tv_usec);
+
+ // Try converting fractional hours
+ tv = to<struct timeval>(duration<double, std::ratio<3600>>{3.456789});
+ EXPECT_EQ(12444, tv.tv_sec);
+ // The usec field is generally off-by-one due to
+ // floating point rounding error
+ EXPECT_NEAR(440400, tv.tv_usec, 1);
+ tv = to<struct timeval>(duration<double, std::ratio<3600>>{-3.456789});
+ EXPECT_EQ(-12445, tv.tv_sec);
+ EXPECT_NEAR(559600, tv.tv_usec, 1);
+
+ // Try converting fractional milliseconds
+ tv = to<struct timeval>(duration<double, std::milli>{9123.456789});
+ EXPECT_EQ(9, tv.tv_sec);
+ EXPECT_EQ(123456, tv.tv_usec);
+ tv = to<struct timeval>(duration<double, std::milli>{-9123.456789});
+ EXPECT_EQ(-10, tv.tv_sec);
+ EXPECT_NEAR(876544, tv.tv_usec, 1);
+
+ tv = to<struct timeval>(duration<uint32_t, std::ratio<3600>>{3});
+ EXPECT_EQ(10800, tv.tv_sec);
+ EXPECT_EQ(0, tv.tv_usec);
+
+ tv = to<struct timeval>(duration<uint32_t, std::nano>{3123});
+ EXPECT_EQ(0, tv.tv_sec);
+ EXPECT_EQ(3, tv.tv_usec);
+ tv = to<struct timeval>(duration<int32_t, std::nano>{-3123});
+ EXPECT_EQ(-1, tv.tv_sec);
+ EXPECT_EQ(999997, tv.tv_usec);
+
+ tv = to<struct timeval>(createTimePoint<steady_clock>(123us));
+ EXPECT_EQ(0, tv.tv_sec);
+ EXPECT_EQ(123, tv.tv_usec);
+
+ tv = to<struct timeval>(createTimePoint<system_clock>(123us));
+ EXPECT_EQ(0, tv.tv_sec);
+ EXPECT_EQ(123, tv.tv_usec);
+}