2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Conversions between std::chrono types and POSIX time types.
20 * These conversions will fail with a ConversionError if an overflow would
21 * occur performing the conversion. (e.g., if the input value cannot fit in
22 * the destination type). However they allow loss of precision (e.g.,
23 * converting nanoseconds to a struct timeval which only has microsecond
24 * granularity, or a struct timespec to std::chrono::minutes).
30 #include <type_traits>
32 #include <folly/Conv.h>
33 #include <folly/Expected.h>
39 struct is_duration : std::false_type {};
40 template <typename Rep, typename Period>
41 struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type {};
43 struct is_time_point : std::false_type {};
44 template <typename Clock, typename Duration>
45 struct is_time_point<std::chrono::time_point<Clock, Duration>>
48 struct is_std_chrono_type {
49 static constexpr bool value =
50 is_duration<T>::value || is_time_point<T>::value;
53 struct is_posix_time_type {
54 static constexpr bool value = std::is_same<T, struct timespec>::value ||
55 std::is_same<T, struct timeval>::value;
57 template <typename Tgt, typename Src>
58 struct is_chrono_conversion {
59 static constexpr bool value =
60 ((is_std_chrono_type<Tgt>::value && is_posix_time_type<Src>::value) ||
61 (is_posix_time_type<Tgt>::value && is_std_chrono_type<Src>::value));
65 * This converts a number in some input type to time_t while ensuring that it
66 * fits in the range of numbers representable by time_t.
68 * This is similar to the normal folly::tryTo() behavior when converting
69 * arthmetic types to an integer type, except that it does not complain about
70 * floating point conversions losing precision.
72 template <typename Src>
73 Expected<time_t, ConversionCode> chronoRangeCheck(Src value) {
74 if (value > std::numeric_limits<time_t>::max()) {
75 return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);
77 if (std::is_signed<Src>::value) {
78 if (value < std::numeric_limits<time_t>::lowest()) {
79 return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
83 return static_cast<time_t>(value);
87 * Convert a std::chrono::duration with second granularity to a pair of
88 * (seconds, subseconds)
90 * The SubsecondRatio template parameter specifies what type of subseconds to
91 * return. This must have a numerator of 1.
93 template <typename SubsecondRatio, typename Rep>
94 static Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(
95 const std::chrono::duration<Rep, std::ratio<1, 1>>& duration) {
97 SubsecondRatio::num == 1, "subsecond numerator should always be 1");
99 auto sec = chronoRangeCheck(duration.count());
100 if (sec.hasError()) {
101 return makeUnexpected(sec.error());
104 time_t secValue = sec.value();
106 if (std::is_floating_point<Rep>::value) {
107 auto fraction = (duration.count() - secValue);
108 subsec = static_cast<long>(fraction * SubsecondRatio::den);
109 if (duration.count() < 0 && fraction < 0) {
110 if (secValue == std::numeric_limits<time_t>::lowest()) {
111 return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
114 subsec += SubsecondRatio::den;
117 return std::pair<time_t, long>{secValue, subsec};
121 * Convert a std::chrono::duration with subsecond granularity to a pair of
122 * (seconds, subseconds)
124 template <typename SubsecondRatio, typename Rep, std::intmax_t Denominator>
125 static Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(
126 const std::chrono::duration<Rep, std::ratio<1, Denominator>>& duration) {
127 static_assert(Denominator != 1, "special case expecting den != 1");
129 SubsecondRatio::num == 1, "subsecond numerator should always be 1");
131 auto sec = chronoRangeCheck(duration.count() / Denominator);
132 if (sec.hasError()) {
133 return makeUnexpected(sec.error());
135 auto secTimeT = sec.value();
137 auto remainder = duration.count() - (secTimeT * Denominator);
138 auto subsec = (remainder * SubsecondRatio::den) / Denominator;
139 if (UNLIKELY(duration.count() < 0) && remainder != 0) {
140 if (secTimeT == std::numeric_limits<time_t>::lowest()) {
141 return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
144 subsec += SubsecondRatio::den;
147 return std::pair<time_t, long>{secTimeT, subsec};
151 * Convert a std::chrono::duration with coarser-than-second granularity to a
152 * pair of (seconds, subseconds)
154 template <typename SubsecondRatio, typename Rep, std::intmax_t Numerator>
155 static Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(
156 const std::chrono::duration<Rep, std::ratio<Numerator, 1>>& duration) {
157 static_assert(Numerator != 1, "special case expecting num!=1");
159 SubsecondRatio::num == 1, "subsecond numerator should always be 1");
161 constexpr auto maxValue = std::numeric_limits<time_t>::max() / Numerator;
162 constexpr auto minValue = std::numeric_limits<time_t>::lowest() / Numerator;
163 if (duration.count() > maxValue) {
164 return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);
166 if (duration.count() < minValue) {
167 return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
170 // Note that we can't use chronoRangeCheck() here since we have to check
171 // if (duration.count() * Numerator) would overflow (which we do above).
172 auto secOriginalRep = (duration.count() * Numerator);
173 auto sec = static_cast<time_t>(secOriginalRep);
176 if (std::is_floating_point<Rep>::value) {
177 auto fraction = secOriginalRep - sec;
178 subsec = static_cast<long>(fraction * SubsecondRatio::den);
179 if (duration.count() < 0 && fraction < 0) {
180 if (sec == std::numeric_limits<time_t>::lowest()) {
181 return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
184 subsec += SubsecondRatio::den;
187 return std::pair<time_t, long>{sec, subsec};
191 * Helper classes for picking an intermediate duration type to use
192 * when doing conversions to/from durations where neither the numerator nor
195 template <typename T, bool IsFloatingPoint, bool IsSigned>
196 struct IntermediateTimeRep {};
197 template <typename T, bool IsSigned>
198 struct IntermediateTimeRep<T, true, IsSigned> {
201 template <typename T>
202 struct IntermediateTimeRep<T, false, true> {
203 using type = intmax_t;
205 template <typename T>
206 struct IntermediateTimeRep<T, false, false> {
207 using type = uintmax_t;
209 // For IntermediateDuration we always use 1 as the numerator, and the original
210 // Period denominator. This ensures that we do not lose precision when
211 // performing the conversion.
212 template <typename Rep, typename Period>
213 using IntermediateDuration = std::chrono::duration<
214 typename IntermediateTimeRep<
216 std::is_floating_point<Rep>::value,
217 std::is_signed<Rep>::value>::type,
218 std::ratio<1, Period::den>>;
221 * Convert a std::chrono::duration to a pair of (seconds, subseconds)
223 * This overload is only used for unusual durations where neither the numerator
224 * nor denominator are 1.
226 template <typename SubsecondRatio, typename Rep, typename Period>
227 Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(
228 const std::chrono::duration<Rep, Period>& duration) {
229 static_assert(Period::num != 1, "should use special-case code when num==1");
230 static_assert(Period::den != 1, "should use special-case code when den==1");
232 SubsecondRatio::num == 1, "subsecond numerator should always be 1");
234 // Perform this conversion by first converting to a duration where the
235 // numerator is 1, then convert to the output type.
236 using IntermediateType = IntermediateDuration<Rep, Period>;
237 using IntermediateRep = typename IntermediateType::rep;
239 // Check to see if we would have overflow converting to the intermediate
241 constexpr auto maxInput =
242 std::numeric_limits<IntermediateRep>::max() / Period::num;
243 if (duration.count() > maxInput) {
244 return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);
246 constexpr auto minInput =
247 std::numeric_limits<IntermediateRep>::min() / Period::num;
248 if (duration.count() < minInput) {
249 return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
252 IntermediateType{static_cast<IntermediateRep>(duration.count()) *
253 static_cast<IntermediateRep>(Period::num)};
255 return durationToPosixTime<SubsecondRatio>(intermediate);
259 * Check for overflow when converting to a duration type that is second
260 * granularity or finer (e.g., nanoseconds, milliseconds, seconds)
262 * This assumes the input is normalized, with subseconds >= 0 and subseconds
263 * less than 1 second.
265 template <bool IsFloatingPoint>
266 struct CheckOverflowToDuration {
269 typename SubsecondRatio,
272 static ConversionCode check(Seconds seconds, Subseconds subseconds) {
274 Tgt::period::num == 1,
275 "this implementation should only be used for subsecond granularity "
278 !std::is_floating_point<typename Tgt::rep>::value, "incorrect usage");
280 SubsecondRatio::num == 1, "subsecond numerator should always be 1");
282 if (LIKELY(seconds >= 0)) {
283 constexpr auto maxCount = std::numeric_limits<typename Tgt::rep>::max();
284 constexpr auto maxSeconds = maxCount / Tgt::period::den;
286 auto unsignedSeconds =
287 static_cast<typename std::make_unsigned<Seconds>::type>(seconds);
288 if (LIKELY(unsignedSeconds < maxSeconds)) {
289 return ConversionCode::SUCCESS;
292 if (UNLIKELY(unsignedSeconds == maxSeconds)) {
293 constexpr auto maxRemainder =
294 maxCount - (maxSeconds * Tgt::period::den);
295 constexpr auto maxSubseconds =
296 (maxRemainder * SubsecondRatio::den) / Tgt::period::den;
297 if (subseconds <= 0) {
298 return ConversionCode::SUCCESS;
300 if (static_cast<typename std::make_unsigned<Subseconds>::type>(
301 subseconds) <= maxSubseconds) {
302 return ConversionCode::SUCCESS;
305 return ConversionCode::POSITIVE_OVERFLOW;
306 } else if (std::is_unsigned<typename Tgt::rep>::value) {
307 return ConversionCode::NEGATIVE_OVERFLOW;
309 constexpr auto minCount =
310 static_cast<typename std::make_signed<typename Tgt::rep>::type>(
311 std::numeric_limits<typename Tgt::rep>::lowest());
312 constexpr auto minSeconds = (minCount / Tgt::period::den);
313 if (LIKELY(seconds >= minSeconds)) {
314 return ConversionCode::SUCCESS;
317 if (UNLIKELY(seconds == minSeconds - 1)) {
318 constexpr auto maxRemainder =
319 minCount - (minSeconds * Tgt::period::den) + Tgt::period::den;
320 constexpr auto maxSubseconds =
321 (maxRemainder * SubsecondRatio::den) / Tgt::period::den;
322 if (subseconds <= 0) {
323 return ConversionCode::NEGATIVE_OVERFLOW;
325 if (subseconds >= maxSubseconds) {
326 return ConversionCode::SUCCESS;
329 return ConversionCode::NEGATIVE_OVERFLOW;
335 struct CheckOverflowToDuration<true> {
338 typename SubsecondRatio,
341 static ConversionCode check(
342 Seconds /* seconds */,
343 Subseconds /* subseconds */) {
345 std::is_floating_point<typename Tgt::rep>::value, "incorrect usage");
347 SubsecondRatio::num == 1, "subsecond numerator should always be 1");
349 // We expect floating point types to have much a wider representable range
350 // than integer types, so we don't bother actually checking the input
351 // integer value here.
353 std::numeric_limits<typename Tgt::rep>::max() >=
354 std::numeric_limits<Seconds>::max(),
355 "unusually limited floating point type");
357 std::numeric_limits<typename Tgt::rep>::lowest() <=
358 std::numeric_limits<Seconds>::lowest(),
359 "unusually limited floating point type");
361 return ConversionCode::SUCCESS;
366 * Convert a timeval or a timespec to a std::chrono::duration with second
369 * The SubsecondRatio template parameter specifies what type of subseconds to
370 * return. This must have a numerator of 1.
372 * The input must be in normalized form: the subseconds field must be greater
373 * than or equal to 0, and less than SubsecondRatio::den (i.e., less than 1
377 typename SubsecondRatio,
381 auto posixTimeToDuration(
383 Subseconds subseconds,
384 std::chrono::duration<Rep, std::ratio<1, 1>> dummy)
385 -> Expected<decltype(dummy), ConversionCode> {
386 using Tgt = decltype(dummy);
387 static_assert(Tgt::period::num == 1, "special case expecting num==1");
388 static_assert(Tgt::period::den == 1, "special case expecting den==1");
390 SubsecondRatio::num == 1, "subsecond numerator should always be 1");
392 auto outputSeconds = tryTo<typename Tgt::rep>(seconds);
393 if (outputSeconds.hasError()) {
394 return makeUnexpected(outputSeconds.error());
397 if (std::is_floating_point<typename Tgt::rep>::value) {
398 return Tgt{typename Tgt::rep(seconds) +
399 (typename Tgt::rep(subseconds) / SubsecondRatio::den)};
402 // If the value is negative, we have to round up a non-zero subseconds value
403 if (UNLIKELY(outputSeconds.value() < 0) && subseconds > 0) {
405 outputSeconds.value() ==
406 std::numeric_limits<typename Tgt::rep>::lowest())) {
407 return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
409 return Tgt{outputSeconds.value() + 1};
412 return Tgt{outputSeconds.value()};
416 * Convert a timeval or a timespec to a std::chrono::duration with subsecond
420 typename SubsecondRatio,
424 std::intmax_t Denominator>
425 auto posixTimeToDuration(
427 Subseconds subseconds,
428 std::chrono::duration<Rep, std::ratio<1, Denominator>> dummy)
429 -> Expected<decltype(dummy), ConversionCode> {
430 using Tgt = decltype(dummy);
431 static_assert(Tgt::period::num == 1, "special case expecting num==1");
432 static_assert(Tgt::period::den != 1, "special case expecting den!=1");
434 SubsecondRatio::num == 1, "subsecond numerator should always be 1");
436 auto errorCode = detail::CheckOverflowToDuration<
437 std::is_floating_point<typename Tgt::rep>::value>::
438 template check<Tgt, SubsecondRatio>(seconds, subseconds);
439 if (errorCode != ConversionCode::SUCCESS) {
440 return makeUnexpected(errorCode);
443 if (LIKELY(seconds >= 0)) {
444 return std::chrono::duration_cast<Tgt>(
445 std::chrono::duration<typename Tgt::rep>{seconds}) +
446 std::chrono::duration_cast<Tgt>(
447 std::chrono::duration<typename Tgt::rep, SubsecondRatio>{
450 // For negative numbers we have to round subseconds up towards zero, even
451 // though it is a positive value, since the overall value is negative.
452 return std::chrono::duration_cast<Tgt>(
453 std::chrono::duration<typename Tgt::rep>{seconds + 1}) -
454 std::chrono::duration_cast<Tgt>(
455 std::chrono::duration<typename Tgt::rep, SubsecondRatio>{
456 SubsecondRatio::den - subseconds});
461 * Convert a timeval or a timespec to a std::chrono::duration with
462 * granularity coarser than 1 second.
465 typename SubsecondRatio,
469 std::intmax_t Numerator>
470 auto posixTimeToDuration(
472 Subseconds subseconds,
473 std::chrono::duration<Rep, std::ratio<Numerator, 1>> dummy)
474 -> Expected<decltype(dummy), ConversionCode> {
475 using Tgt = decltype(dummy);
476 static_assert(Tgt::period::num != 1, "special case expecting num!=1");
477 static_assert(Tgt::period::den == 1, "special case expecting den==1");
479 SubsecondRatio::num == 1, "subsecond numerator should always be 1");
481 if (UNLIKELY(seconds < 0) && subseconds > 0) {
482 // Increment seconds by one to handle truncation of negative numbers
484 if (UNLIKELY(seconds == std::numeric_limits<Seconds>::lowest())) {
485 return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
490 if (std::is_floating_point<typename Tgt::rep>::value) {
491 // Convert to the floating point type before performing the division
492 return Tgt{static_cast<typename Tgt::rep>(seconds) / Tgt::period::num};
494 // Perform the division as an integer, and check that the result fits in
495 // the output integer type
496 auto outputValue = (seconds / Tgt::period::num);
497 auto expectedOuput = tryTo<typename Tgt::rep>(outputValue);
498 if (expectedOuput.hasError()) {
499 return makeUnexpected(expectedOuput.error());
502 return Tgt{expectedOuput.value()};
507 * Convert a timeval or timespec to a std::chrono::duration
509 * This overload is only used for unusual durations where neither the numerator
510 * nor denominator are 1.
513 typename SubsecondRatio,
517 std::intmax_t Denominator,
518 std::intmax_t Numerator>
519 auto posixTimeToDuration(
521 Subseconds subseconds,
522 std::chrono::duration<Rep, std::ratio<Numerator, Denominator>> dummy)
523 -> Expected<decltype(dummy), ConversionCode> {
524 using Tgt = decltype(dummy);
526 Tgt::period::num != 1, "should use special-case code when num==1");
528 Tgt::period::den != 1, "should use special-case code when den==1");
530 SubsecondRatio::num == 1, "subsecond numerator should always be 1");
532 // Cast through an intermediate type with subsecond granularity.
533 // Note that this could fail due to overflow during the initial conversion
534 // even if the result is representable in the output POSIX-style types.
536 // Note that for integer type conversions going through this intermediate
537 // type can result in slight imprecision due to truncating the intermediate
538 // calculation to an integer.
539 using IntermediateType =
540 IntermediateDuration<typename Tgt::rep, typename Tgt::period>;
541 auto intermediate = posixTimeToDuration<SubsecondRatio>(
542 seconds, subseconds, IntermediateType{});
543 if (intermediate.hasError()) {
544 return makeUnexpected(intermediate.error());
546 // Now convert back to the target duration. Use tryTo() to confirm that the
547 // result fits in the target representation type.
548 return tryTo<typename Tgt::rep>(
549 intermediate.value().count() / Tgt::period::num)
550 .then([](typename Tgt::rep tgt) { return Tgt{tgt}; });
555 typename SubsecondRatio,
558 Expected<Tgt, ConversionCode> tryPosixTimeToDuration(
560 Subseconds subseconds) {
562 SubsecondRatio::num == 1, "subsecond numerator should always be 1");
564 // Normalize the input if required
565 if (UNLIKELY(subseconds < 0)) {
566 const auto overflowSeconds = (subseconds / SubsecondRatio::den);
567 const auto remainder = (subseconds % SubsecondRatio::den);
568 if (std::numeric_limits<Seconds>::lowest() + 1 - overflowSeconds >
570 return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
572 seconds = seconds - 1 + overflowSeconds;
573 subseconds = remainder + SubsecondRatio::den;
574 } else if (UNLIKELY(subseconds >= SubsecondRatio::den)) {
575 const auto overflowSeconds = (subseconds / SubsecondRatio::den);
576 const auto remainder = (subseconds % SubsecondRatio::den);
577 if (std::numeric_limits<Seconds>::max() - overflowSeconds < seconds) {
578 return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);
580 seconds += overflowSeconds;
581 subseconds = remainder;
584 return posixTimeToDuration<SubsecondRatio>(seconds, subseconds, Tgt{});
587 } // namespace detail
590 * struct timespec to std::chrono::duration
592 template <typename Tgt>
593 typename std::enable_if<
594 detail::is_duration<Tgt>::value,
595 Expected<Tgt, ConversionCode>>::type
596 tryTo(const struct timespec& ts) {
597 return detail::tryPosixTimeToDuration<Tgt, std::nano>(ts.tv_sec, ts.tv_nsec);
601 * struct timeval to std::chrono::duration
603 template <typename Tgt>
604 typename std::enable_if<
605 detail::is_duration<Tgt>::value,
606 Expected<Tgt, ConversionCode>>::type
607 tryTo(const struct timeval& tv) {
608 return detail::tryPosixTimeToDuration<Tgt, std::micro>(tv.tv_sec, tv.tv_usec);
612 * timespec or timeval to std::chrono::time_point
614 template <typename Tgt, typename Src>
615 typename std::enable_if<
616 detail::is_time_point<Tgt>::value && detail::is_posix_time_type<Src>::value,
617 Expected<Tgt, ConversionCode>>::type
618 tryTo(const Src& value) {
619 return tryTo<typename Tgt::duration>(value).then(
620 [](typename Tgt::duration result) { return Tgt(result); });
624 * std::chrono::duration to struct timespec
626 template <typename Tgt, typename Rep, typename Period>
627 typename std::enable_if<
628 std::is_same<Tgt, struct timespec>::value,
629 Expected<Tgt, ConversionCode>>::type
630 tryTo(const std::chrono::duration<Rep, Period>& duration) {
631 auto result = detail::durationToPosixTime<std::nano>(duration);
632 if (result.hasError()) {
633 return makeUnexpected(result.error());
637 ts.tv_sec = result.value().first;
638 ts.tv_nsec = result.value().second;
643 * std::chrono::duration to struct timeval
645 template <typename Tgt, typename Rep, typename Period>
646 typename std::enable_if<
647 std::is_same<Tgt, struct timeval>::value,
648 Expected<Tgt, ConversionCode>>::type
649 tryTo(const std::chrono::duration<Rep, Period>& duration) {
650 auto result = detail::durationToPosixTime<std::micro>(duration);
651 if (result.hasError()) {
652 return makeUnexpected(result.error());
656 tv.tv_sec = result.value().first;
657 tv.tv_usec = result.value().second;
662 * std::chrono::time_point to timespec or timeval
664 template <typename Tgt, typename Clock, typename Duration>
665 typename std::enable_if<
666 detail::is_posix_time_type<Tgt>::value,
667 Expected<Tgt, ConversionCode>>::type
668 tryTo(const std::chrono::time_point<Clock, Duration>& timePoint) {
669 return tryTo<Tgt>(timePoint.time_since_epoch());
673 * For all chrono conversions, to() wraps tryTo()
675 template <typename Tgt, typename Src>
676 typename std::enable_if<detail::is_chrono_conversion<Tgt, Src>::value, Tgt>::
678 to(const Src& value) {
679 return tryTo<Tgt>(value).thenOrThrow(
680 [](Tgt res) { return res; },
681 [&](ConversionCode e) { return makeConversionError(e, StringPiece{}); });