implement to() conversions for std::chrono to timespec/timeval
[folly.git] / folly / chrono / Conv.h
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /**
18  * Conversions between std::chrono types and POSIX time types.
19  *
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).
25  */
26
27 #pragma once
28
29 #include <chrono>
30 #include <type_traits>
31
32 #include <folly/Conv.h>
33 #include <folly/Expected.h>
34
35 namespace folly {
36 namespace detail {
37
38 template <typename T>
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 {};
42 template <typename T>
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>>
46     : std::true_type {};
47 template <typename T>
48 struct is_std_chrono_type {
49   static constexpr bool value =
50       is_duration<T>::value || is_time_point<T>::value;
51 };
52 template <typename T>
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;
56 };
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));
62 };
63
64 /**
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.
67  *
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.
71  */
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);
76   }
77   if (std::is_signed<Src>::value) {
78     if (value < std::numeric_limits<time_t>::lowest()) {
79       return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
80     }
81   }
82
83   return static_cast<time_t>(value);
84 }
85
86 /**
87  * Convert a std::chrono::duration with second granularity to a pair of
88  * (seconds, subseconds)
89  *
90  * The SubsecondRatio template parameter specifies what type of subseconds to
91  * return.  This must have a numerator of 1.
92  */
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) {
96   static_assert(
97       SubsecondRatio::num == 1, "subsecond numerator should always be 1");
98
99   auto sec = chronoRangeCheck(duration.count());
100   if (sec.hasError()) {
101     return makeUnexpected(sec.error());
102   }
103
104   time_t secValue = sec.value();
105   long subsec = 0L;
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);
112       }
113       secValue -= 1;
114       subsec += SubsecondRatio::den;
115     }
116   }
117   return std::pair<time_t, long>{secValue, subsec};
118 }
119
120 /**
121  * Convert a std::chrono::duration with subsecond granularity to a pair of
122  * (seconds, subseconds)
123  */
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");
128   static_assert(
129       SubsecondRatio::num == 1, "subsecond numerator should always be 1");
130
131   auto sec = chronoRangeCheck(duration.count() / Denominator);
132   if (sec.hasError()) {
133     return makeUnexpected(sec.error());
134   }
135   auto secTimeT = sec.value();
136
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);
142     }
143     secTimeT -= 1;
144     subsec += SubsecondRatio::den;
145   }
146
147   return std::pair<time_t, long>{secTimeT, subsec};
148 }
149
150 /**
151  * Convert a std::chrono::duration with coarser-than-second granularity to a
152  * pair of (seconds, subseconds)
153  */
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");
158   static_assert(
159       SubsecondRatio::num == 1, "subsecond numerator should always be 1");
160
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);
165   }
166   if (duration.count() < minValue) {
167     return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
168   }
169
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);
174
175   long subsec = 0L;
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);
182       }
183       sec -= 1;
184       subsec += SubsecondRatio::den;
185     }
186   }
187   return std::pair<time_t, long>{sec, subsec};
188 }
189
190 /**
191  * Convert a std::chrono::duration to a pair of (seconds, subseconds)
192  *
193  * This overload is only used for unusual durations where neither the numerator
194  * nor denominator are 1.
195  */
196 template <typename SubsecondRatio, typename Rep, typename Period>
197 Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(
198     const std::chrono::duration<Rep, Period>& duration) {
199   static_assert(Period::num != 1, "should use special-case code when num==1");
200   static_assert(Period::den != 1, "should use special-case code when den==1");
201   static_assert(
202       SubsecondRatio::num == 1, "subsecond numerator should always be 1");
203
204   // TODO: We need to implement an overflow-checking tryTo() function for
205   // duration-to-duration casts for the code above to work.
206   //
207   // For now this is unimplemented, and we just have a static_assert that
208   // will always fail if someone tries to instantiate this.  Unusual duration
209   // types should be extremely rare, and I'm not aware of any code at the
210   // moment that actually needs this.
211   static_assert(
212       Period::num == 1,
213       "conversion from unusual duration types is not implemented yet");
214   (void)duration;
215   return makeUnexpected(ConversionCode::SUCCESS);
216 }
217
218 /**
219  * Check for overflow when converting to a duration type that is second
220  * granularity or finer (e.g., nanoseconds, milliseconds, seconds)
221  *
222  * This assumes the input is normalized, with subseconds >= 0 and subseconds
223  * less than 1 second.
224  */
225 template <bool IsFloatingPoint>
226 struct CheckOverflowToDuration {
227   template <
228       typename Tgt,
229       typename SubsecondRatio,
230       typename Seconds,
231       typename Subseconds>
232   static ConversionCode check(Seconds seconds, Subseconds subseconds) {
233     static_assert(
234         Tgt::period::num == 1,
235         "this implementation should only be used for subsecond granularity "
236         "duration types");
237     static_assert(
238         !std::is_floating_point<typename Tgt::rep>::value, "incorrect usage");
239     static_assert(
240         SubsecondRatio::num == 1, "subsecond numerator should always be 1");
241
242     if (LIKELY(seconds >= 0)) {
243       constexpr auto maxCount = std::numeric_limits<typename Tgt::rep>::max();
244       constexpr auto maxSeconds = maxCount / Tgt::period::den;
245
246       auto unsignedSeconds =
247           static_cast<typename std::make_unsigned<Seconds>::type>(seconds);
248       if (LIKELY(unsignedSeconds < maxSeconds)) {
249         return ConversionCode::SUCCESS;
250       }
251
252       if (UNLIKELY(unsignedSeconds == maxSeconds)) {
253         constexpr auto maxRemainder =
254             maxCount - (maxSeconds * Tgt::period::den);
255         constexpr auto maxSubseconds =
256             (maxRemainder * SubsecondRatio::den) / Tgt::period::den;
257         if (subseconds <= 0) {
258           return ConversionCode::SUCCESS;
259         }
260         if (static_cast<typename std::make_unsigned<Subseconds>::type>(
261                 subseconds) <= maxSubseconds) {
262           return ConversionCode::SUCCESS;
263         }
264       }
265       return ConversionCode::POSITIVE_OVERFLOW;
266     } else if (std::is_unsigned<typename Tgt::rep>::value) {
267       return ConversionCode::NEGATIVE_OVERFLOW;
268     } else {
269       constexpr auto minCount =
270           static_cast<typename std::make_signed<typename Tgt::rep>::type>(
271               std::numeric_limits<typename Tgt::rep>::lowest());
272       constexpr auto minSeconds = (minCount / Tgt::period::den);
273       if (LIKELY(seconds >= minSeconds)) {
274         return ConversionCode::SUCCESS;
275       }
276
277       if (UNLIKELY(seconds == minSeconds - 1)) {
278         constexpr auto maxRemainder =
279             minCount - (minSeconds * Tgt::period::den) + Tgt::period::den;
280         constexpr auto maxSubseconds =
281             (maxRemainder * SubsecondRatio::den) / Tgt::period::den;
282         if (subseconds <= 0) {
283           return ConversionCode::NEGATIVE_OVERFLOW;
284         }
285         if (subseconds >= maxSubseconds) {
286           return ConversionCode::SUCCESS;
287         }
288       }
289       return ConversionCode::NEGATIVE_OVERFLOW;
290     }
291   }
292 };
293
294 template <>
295 struct CheckOverflowToDuration<true> {
296   template <
297       typename Tgt,
298       typename SubsecondRatio,
299       typename Seconds,
300       typename Subseconds>
301   static ConversionCode check(
302       Seconds /* seconds */,
303       Subseconds /* subseconds */) {
304     static_assert(
305         std::is_floating_point<typename Tgt::rep>::value, "incorrect usage");
306     static_assert(
307         SubsecondRatio::num == 1, "subsecond numerator should always be 1");
308
309     // We expect floating point types to have much a wider representable range
310     // than integer types, so we don't bother actually checking the input
311     // integer value here.
312     static_assert(
313         std::numeric_limits<typename Tgt::rep>::max() >=
314             std::numeric_limits<Seconds>::max(),
315         "unusually limited floating point type");
316     static_assert(
317         std::numeric_limits<typename Tgt::rep>::lowest() <=
318             std::numeric_limits<Seconds>::lowest(),
319         "unusually limited floating point type");
320
321     return ConversionCode::SUCCESS;
322   }
323 };
324
325 /**
326  * Helper class to convert a POSIX-style pair of (seconds, subseconds)
327  * to a std::chrono::duration type.
328  *
329  * The SubsecondRatio template parameter specifies what type of subseconds to
330  * return.  This must have a numerator of 1.
331  *
332  * The input must be in normalized form: the subseconds field must be greater
333  * than or equal to 0, and less than SubsecondRatio::den (i.e., less than 1
334  * second).
335  *
336  * This default implementation is only used for unusual std::chrono::duration
337  * types where neither the numerator nor denominator are 1.
338  */
339 template <typename Tgt>
340 struct PosixTimeToDuration {
341   template <typename SubsecondRatio, typename Seconds, typename Subseconds>
342   static Expected<Tgt, ConversionCode> cast(
343       Seconds seconds,
344       Subseconds subseconds);
345 };
346
347 /**
348  * Convert a timeval or a timespec to a std::chrono::duration with second
349  * granularity.
350  */
351 template <typename Rep>
352 struct PosixTimeToDuration<std::chrono::duration<Rep, std::ratio<1, 1>>> {
353   using Tgt = std::chrono::duration<Rep, std::ratio<1, 1>>;
354
355   template <typename SubsecondRatio, typename Seconds, typename Subseconds>
356   static Expected<Tgt, ConversionCode> cast(
357       Seconds seconds,
358       Subseconds subseconds) {
359     static_assert(Tgt::period::num == 1, "special case expecting num==1");
360     static_assert(Tgt::period::den == 1, "special case expecting den==1");
361     static_assert(
362         SubsecondRatio::num == 1, "subsecond numerator should always be 1");
363
364     auto outputSeconds = tryTo<typename Tgt::rep>(seconds);
365     if (outputSeconds.hasError()) {
366       return makeUnexpected(outputSeconds.error());
367     }
368
369     if (std::is_floating_point<typename Tgt::rep>::value) {
370       return Tgt{typename Tgt::rep(seconds) +
371                  (typename Tgt::rep(subseconds) / SubsecondRatio::den)};
372     }
373
374     // If the value is negative, we have to round up a non-zero subseconds value
375     if (UNLIKELY(outputSeconds.value() < 0) && subseconds > 0) {
376       if (UNLIKELY(
377               outputSeconds.value() ==
378               std::numeric_limits<typename Tgt::rep>::lowest())) {
379         return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
380       }
381       return Tgt{outputSeconds.value() + 1};
382     }
383
384     return Tgt{outputSeconds.value()};
385   }
386 };
387
388 /**
389  * Convert a timeval or a timespec to a std::chrono::duration with subsecond
390  * granularity
391  */
392 template <typename Rep, std::intmax_t Denominator>
393 struct PosixTimeToDuration<
394     std::chrono::duration<Rep, std::ratio<1, Denominator>>> {
395   using Tgt = std::chrono::duration<Rep, std::ratio<1, Denominator>>;
396
397   template <typename SubsecondRatio, typename Seconds, typename Subseconds>
398   static Expected<Tgt, ConversionCode> cast(
399       Seconds seconds,
400       Subseconds subseconds) {
401     static_assert(Tgt::period::num == 1, "special case expecting num==1");
402     static_assert(Tgt::period::den != 1, "special case expecting den!=1");
403     static_assert(
404         SubsecondRatio::num == 1, "subsecond numerator should always be 1");
405
406     auto errorCode = detail::CheckOverflowToDuration<
407         std::is_floating_point<typename Tgt::rep>::value>::
408         template check<Tgt, SubsecondRatio>(seconds, subseconds);
409     if (errorCode != ConversionCode::SUCCESS) {
410       return makeUnexpected(errorCode);
411     }
412
413     if (LIKELY(seconds >= 0)) {
414       return std::chrono::duration_cast<Tgt>(
415                  std::chrono::duration<typename Tgt::rep>{seconds}) +
416           std::chrono::duration_cast<Tgt>(
417                  std::chrono::duration<typename Tgt::rep, SubsecondRatio>{
418                      subseconds});
419     } else {
420       // For negative numbers we have to round subseconds up towards zero, even
421       // though it is a positive value, since the overall value is negative.
422       return std::chrono::duration_cast<Tgt>(
423                  std::chrono::duration<typename Tgt::rep>{seconds + 1}) -
424           std::chrono::duration_cast<Tgt>(
425                  std::chrono::duration<typename Tgt::rep, SubsecondRatio>{
426                      SubsecondRatio::den - subseconds});
427     }
428   }
429 };
430
431 /**
432  * Convert a timeval or a timespec to a std::chrono::duration with
433  * granularity coarser than 1 second.
434  */
435 template <typename Rep, std::intmax_t Numerator>
436 struct PosixTimeToDuration<
437     std::chrono::duration<Rep, std::ratio<Numerator, 1>>> {
438   using Tgt = std::chrono::duration<Rep, std::ratio<Numerator, 1>>;
439
440   template <typename SubsecondRatio, typename Seconds, typename Subseconds>
441   static Expected<Tgt, ConversionCode> cast(
442       Seconds seconds,
443       Subseconds subseconds) {
444     static_assert(Tgt::period::num != 1, "special case expecting num!=1");
445     static_assert(Tgt::period::den == 1, "special case expecting den==1");
446     static_assert(
447         SubsecondRatio::num == 1, "subsecond numerator should always be 1");
448
449     if (UNLIKELY(seconds < 0) && subseconds > 0) {
450       // Increment seconds by one to handle truncation of negative numbers
451       // properly.
452       if (UNLIKELY(seconds == std::numeric_limits<Seconds>::lowest())) {
453         return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
454       }
455       seconds += 1;
456     }
457
458     if (std::is_floating_point<typename Tgt::rep>::value) {
459       // Convert to the floating point type before performing the division
460       return Tgt{static_cast<typename Tgt::rep>(seconds) / Tgt::period::num};
461     } else {
462       // Perform the division as an integer, and check that the result fits in
463       // the output integer type
464       auto outputValue = (seconds / Tgt::period::num);
465       auto expectedOuput = tryTo<typename Tgt::rep>(outputValue);
466       if (expectedOuput.hasError()) {
467         return makeUnexpected(expectedOuput.error());
468       }
469
470       return Tgt{expectedOuput.value()};
471     }
472   }
473 };
474
475 /**
476  * PosixTimeToDuration::cast() implementation for the default case
477  * with unusual durations where neither the numerator nor denominator are 1.
478  */
479 template <typename Tgt>
480 template <typename SubsecondRatio, typename Seconds, typename Subseconds>
481 Expected<Tgt, ConversionCode> PosixTimeToDuration<Tgt>::cast(
482     Seconds seconds,
483     Subseconds subseconds) {
484   static_assert(
485       Tgt::period::num != 1, "should use special-case code when num==1");
486   static_assert(
487       Tgt::period::den != 1, "should use special-case code when den==1");
488   static_assert(
489       SubsecondRatio::num == 1, "subsecond numerator should always be 1");
490
491   // TODO: We need to implement an overflow-checking tryTo() function for
492   // duration-to-duration casts for the code above to work.
493   //
494   // For now this is unimplemented, and we just have a static_assert that
495   // will always fail if someone tries to instantiate this.  Unusual duration
496   // types should be extremely rare, and I'm not aware of any code at the
497   // moment that actually needs this.
498   static_assert(
499       Tgt::period::num == 1,
500       "conversion to unusual duration types is not implemented yet");
501   (void)seconds;
502   (void)subseconds;
503   return makeUnexpected(ConversionCode::SUCCESS);
504 }
505
506 template <
507     typename Tgt,
508     typename SubsecondRatio,
509     typename Seconds,
510     typename Subseconds>
511 Expected<Tgt, ConversionCode> tryPosixTimeToDuration(
512     Seconds seconds,
513     Subseconds subseconds) {
514   static_assert(
515       SubsecondRatio::num == 1, "subsecond numerator should always be 1");
516
517   // Normalize the input if required
518   if (UNLIKELY(subseconds < 0)) {
519     const auto overflowSeconds = (subseconds / SubsecondRatio::den);
520     const auto remainder = (subseconds % SubsecondRatio::den);
521     if (std::numeric_limits<Seconds>::lowest() + 1 - overflowSeconds >
522         seconds) {
523       return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
524     }
525     seconds = seconds - 1 + overflowSeconds;
526     subseconds = remainder + SubsecondRatio::den;
527   } else if (UNLIKELY(subseconds >= SubsecondRatio::den)) {
528     const auto overflowSeconds = (subseconds / SubsecondRatio::den);
529     const auto remainder = (subseconds % SubsecondRatio::den);
530     if (std::numeric_limits<Seconds>::max() - overflowSeconds < seconds) {
531       return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);
532     }
533     seconds += overflowSeconds;
534     subseconds = remainder;
535   }
536
537   using Converter = PosixTimeToDuration<Tgt>;
538   return Converter::template cast<SubsecondRatio>(seconds, subseconds);
539 }
540
541 } // namespace detail
542
543 /**
544  * struct timespec to std::chrono::duration
545  */
546 template <typename Tgt>
547 typename std::enable_if<
548     detail::is_duration<Tgt>::value,
549     Expected<Tgt, ConversionCode>>::type
550 tryTo(const struct timespec& ts) {
551   return detail::tryPosixTimeToDuration<Tgt, std::nano>(ts.tv_sec, ts.tv_nsec);
552 }
553
554 /**
555  * struct timeval to std::chrono::duration
556  */
557 template <typename Tgt>
558 typename std::enable_if<
559     detail::is_duration<Tgt>::value,
560     Expected<Tgt, ConversionCode>>::type
561 tryTo(const struct timeval& tv) {
562   return detail::tryPosixTimeToDuration<Tgt, std::micro>(tv.tv_sec, tv.tv_usec);
563 }
564
565 /**
566  * timespec or timeval to std::chrono::time_point
567  */
568 template <typename Tgt, typename Src>
569 typename std::enable_if<
570     detail::is_time_point<Tgt>::value && detail::is_posix_time_type<Src>::value,
571     Expected<Tgt, ConversionCode>>::type
572 tryTo(const Src& value) {
573   return tryTo<typename Tgt::duration>(value).then(
574       [](typename Tgt::duration result) { return Tgt(result); });
575 }
576
577 /**
578  * std::chrono::duration to struct timespec
579  */
580 template <typename Tgt, typename Rep, typename Period>
581 typename std::enable_if<
582     std::is_same<Tgt, struct timespec>::value,
583     Expected<Tgt, ConversionCode>>::type
584 tryTo(const std::chrono::duration<Rep, Period>& duration) {
585   auto result = detail::durationToPosixTime<std::nano>(duration);
586   if (result.hasError()) {
587     return makeUnexpected(result.error());
588   }
589
590   struct timespec ts;
591   ts.tv_sec = result.value().first;
592   ts.tv_nsec = result.value().second;
593   return ts;
594 }
595
596 /**
597  * std::chrono::duration to struct timeval
598  */
599 template <typename Tgt, typename Rep, typename Period>
600 typename std::enable_if<
601     std::is_same<Tgt, struct timeval>::value,
602     Expected<Tgt, ConversionCode>>::type
603 tryTo(const std::chrono::duration<Rep, Period>& duration) {
604   auto result = detail::durationToPosixTime<std::micro>(duration);
605   if (result.hasError()) {
606     return makeUnexpected(result.error());
607   }
608
609   struct timeval tv;
610   tv.tv_sec = result.value().first;
611   tv.tv_usec = result.value().second;
612   return tv;
613 }
614
615 /**
616  * std::chrono::time_point to timespec or timeval
617  */
618 template <typename Tgt, typename Clock, typename Duration>
619 typename std::enable_if<
620     detail::is_posix_time_type<Tgt>::value,
621     Expected<Tgt, ConversionCode>>::type
622 tryTo(const std::chrono::time_point<Clock, Duration>& timePoint) {
623   return tryTo<Tgt>(timePoint.time_since_epoch());
624 }
625
626 /**
627  * For all chrono conversions, to() wraps tryTo()
628  */
629 template <typename Tgt, typename Src>
630 typename std::enable_if<detail::is_chrono_conversion<Tgt, Src>::value, Tgt>::
631     type
632     to(const Src& value) {
633   return tryTo<Tgt>(value).thenOrThrow(
634       [](Tgt res) { return res; },
635       [&](ConversionCode e) { return makeConversionError(e, StringPiece{}); });
636 }
637
638 } // namespace folly