2222
2323#include < chrono>
2424#include < cstdint>
25+ #include < limits>
2526#include < string>
2627#include < utility>
2728
@@ -41,20 +42,9 @@ using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead.
4142
4243namespace detail {
4344template <typename D>
44- inline std::pair<time_point<seconds>, D> split_seconds (
45- const time_point<D>& tp) {
46- auto sec = std::chrono::time_point_cast<seconds>(tp);
47- auto sub = tp - sec;
48- if (sub.count () < 0 ) {
49- sec -= seconds (1 );
50- sub += seconds (1 );
51- }
52- return {sec, std::chrono::duration_cast<D>(sub)};
53- }
54- inline std::pair<time_point<seconds>, seconds> split_seconds (
55- const time_point<seconds>& tp) {
56- return {tp, seconds::zero ()};
57- }
45+ std::pair<time_point<seconds>, D> split_seconds (const time_point<D>& tp);
46+ std::pair<time_point<seconds>, seconds> split_seconds (
47+ const time_point<seconds>& tp);
5848} // namespace detail
5949
6050// cctz::time_zone is an opaque, small, value-type class representing a
@@ -279,6 +269,20 @@ std::string format(const std::string&, const time_point<seconds>&,
279269 const femtoseconds&, const time_zone&);
280270bool parse (const std::string&, const std::string&, const time_zone&,
281271 time_point<seconds>*, femtoseconds*, std::string* err = nullptr );
272+ template <typename Rep, std::intmax_t Denom>
273+ bool join_seconds (
274+ const time_point<seconds>& sec, const femtoseconds& fs,
275+ time_point<std::chrono::duration<Rep, std::ratio<1 , Denom>>>* tpp);
276+ template <typename Rep, std::intmax_t Num>
277+ bool join_seconds (
278+ const time_point<seconds>& sec, const femtoseconds& fs,
279+ time_point<std::chrono::duration<Rep, std::ratio<Num, 1 >>>* tpp);
280+ template <typename Rep>
281+ bool join_seconds (
282+ const time_point<seconds>& sec, const femtoseconds& fs,
283+ time_point<std::chrono::duration<Rep, std::ratio<1 , 1 >>>* tpp);
284+ bool join_seconds (const time_point<seconds>& sec, const femtoseconds&,
285+ time_point<seconds>* tpp);
282286} // namespace detail
283287
284288// Formats the given time_point in the given cctz::time_zone according to
@@ -369,15 +373,84 @@ inline bool parse(const std::string& fmt, const std::string& input,
369373 const time_zone& tz, time_point<D>* tpp) {
370374 time_point<seconds> sec;
371375 detail::femtoseconds fs;
372- const bool b = detail::parse (fmt, input, tz, &sec, &fs);
373- if (b) {
374- // TODO: Return false if unrepresentable as a time_point<D>.
375- *tpp = std::chrono::time_point_cast<D>(sec);
376- *tpp += std::chrono::duration_cast<D>(fs);
376+ return detail::parse (fmt, input, tz, &sec, &fs) &&
377+ detail::join_seconds (sec, fs, tpp);
378+ }
379+
380+ namespace detail {
381+
382+ // Split a time_point<D> into a time_point<seconds> and a D subseconds.
383+ // Undefined behavior if time_point<seconds> is not of sufficient range.
384+ // Note that this means it is UB to call cctz::time_zone::lookup(tp) or
385+ // cctz::format(fmt, tp, tz) with a time_point that is outside the range
386+ // of a 64-bit std::time_t.
387+ template <typename D>
388+ std::pair<time_point<seconds>, D> split_seconds (const time_point<D>& tp) {
389+ auto sec = std::chrono::time_point_cast<seconds>(tp);
390+ auto sub = tp - sec;
391+ if (sub.count () < 0 ) {
392+ sec -= seconds (1 );
393+ sub += seconds (1 );
377394 }
378- return b;
395+ return {sec, std::chrono::duration_cast<D>(sub)};
396+ }
397+
398+ inline std::pair<time_point<seconds>, seconds> split_seconds (
399+ const time_point<seconds>& tp) {
400+ return {tp, seconds::zero ()};
379401}
380402
403+ // Join a time_point<seconds> and femto subseconds into a time_point<D>.
404+ // Floors to the resolution of time_point<D>. Returns false if time_point<D>
405+ // is not of sufficient range.
406+ template <typename Rep, std::intmax_t Denom>
407+ bool join_seconds (
408+ const time_point<seconds>& sec, const femtoseconds& fs,
409+ time_point<std::chrono::duration<Rep, std::ratio<1 , Denom>>>* tpp) {
410+ using D = std::chrono::duration<Rep, std::ratio<1 , Denom>>;
411+ // TODO(#199): Return false if result unrepresentable as a time_point<D>.
412+ *tpp = std::chrono::time_point_cast<D>(sec);
413+ *tpp += std::chrono::duration_cast<D>(fs);
414+ return true ;
415+ }
416+
417+ template <typename Rep, std::intmax_t Num>
418+ bool join_seconds (
419+ const time_point<seconds>& sec, const femtoseconds&,
420+ time_point<std::chrono::duration<Rep, std::ratio<Num, 1 >>>* tpp) {
421+ using D = std::chrono::duration<Rep, std::ratio<Num, 1 >>;
422+ auto count = sec.time_since_epoch ().count ();
423+ if (count >= 0 || count % Num == 0 ) {
424+ count /= Num;
425+ } else {
426+ count /= Num;
427+ count -= 1 ;
428+ }
429+ if (count > (std::numeric_limits<Rep>::max)()) return false ;
430+ if (count < (std::numeric_limits<Rep>::min)()) return false ;
431+ *tpp = time_point<D>() + D{static_cast <Rep>(count)};
432+ return true ;
433+ }
434+
435+ template <typename Rep>
436+ bool join_seconds (
437+ const time_point<seconds>& sec, const femtoseconds&,
438+ time_point<std::chrono::duration<Rep, std::ratio<1 , 1 >>>* tpp) {
439+ using D = std::chrono::duration<Rep, std::ratio<1 , 1 >>;
440+ auto count = sec.time_since_epoch ().count ();
441+ if (count > (std::numeric_limits<Rep>::max)()) return false ;
442+ if (count < (std::numeric_limits<Rep>::min)()) return false ;
443+ *tpp = time_point<D>() + D{static_cast <Rep>(count)};
444+ return true ;
445+ }
446+
447+ inline bool join_seconds (const time_point<seconds>& sec, const femtoseconds&,
448+ time_point<seconds>* tpp) {
449+ *tpp = sec;
450+ return true ;
451+ }
452+
453+ } // namespace detail
381454} // namespace cctz
382455} // namespace time_internal
383456ABSL_NAMESPACE_END
0 commit comments