Skip to content

Commit 084aa07

Browse files
Abseil Teamdinord
authored andcommitted
Export of internal Abseil changes
-- 42dc250867db8816381a38596e00a3b27b7dbc37 by Gennadiy Rozental <[email protected]>: Import of CCTZ from GitHub. PiperOrigin-RevId: 401863616 GitOrigin-RevId: 42dc250867db8816381a38596e00a3b27b7dbc37 Change-Id: Ia1f100293e0c0845de76d986a170b5ca8d15f1a3
1 parent 4a995b1 commit 084aa07

File tree

3 files changed

+172
-25
lines changed

3 files changed

+172
-25
lines changed

absl/time/internal/cctz/include/cctz/time_zone.h

Lines changed: 93 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
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

4243
namespace detail {
4344
template <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&);
280270
bool 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
383456
ABSL_NAMESPACE_END

absl/time/internal/cctz/src/time_zone_format_test.cc

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
#include <chrono>
16+
#include <cstdint>
1617
#include <iomanip>
1718
#include <sstream>
1819
#include <string>
@@ -1504,7 +1505,7 @@ TEST(Parse, MaxRange) {
15041505
parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp));
15051506
EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::max());
15061507
EXPECT_FALSE(
1507-
parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp));
1508+
parse(RFC3339_sec, "292277026596-12-04T14:30:08-01:00", utc, &tp));
15081509

15091510
// tests the lower limit using +00:00 offset
15101511
EXPECT_TRUE(
@@ -1525,10 +1526,82 @@ TEST(Parse, MaxRange) {
15251526
parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01", utc, &tp));
15261527
EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01",
15271528
utc, &tp));
1529+
}
1530+
1531+
TEST(Parse, TimePointOverflow) {
1532+
const time_zone utc = utc_time_zone();
1533+
1534+
using D = chrono::duration<std::int64_t, std::nano>;
1535+
time_point<D> tp;
1536+
1537+
EXPECT_TRUE(
1538+
parse(RFC3339_full, "2262-04-11T23:47:16.8547758079+00:00", utc, &tp));
1539+
EXPECT_EQ(tp, time_point<D>::max());
1540+
EXPECT_EQ("2262-04-11T23:47:16.854775807+00:00",
1541+
format(RFC3339_full, tp, utc));
1542+
#if 0
1543+
// TODO(#199): Will fail until cctz::parse() properly detects overflow.
1544+
EXPECT_FALSE(
1545+
parse(RFC3339_full, "2262-04-11T23:47:16.8547758080+00:00", utc, &tp));
1546+
EXPECT_TRUE(
1547+
parse(RFC3339_full, "1677-09-21T00:12:43.1452241920+00:00", utc, &tp));
1548+
EXPECT_EQ(tp, time_point<D>::min());
1549+
EXPECT_EQ("1677-09-21T00:12:43.145224192+00:00",
1550+
format(RFC3339_full, tp, utc));
1551+
EXPECT_FALSE(
1552+
parse(RFC3339_full, "1677-09-21T00:12:43.1452241919+00:00", utc, &tp));
1553+
#endif
1554+
1555+
using DS = chrono::duration<std::int8_t, chrono::seconds::period>;
1556+
time_point<DS> stp;
1557+
1558+
EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T00:02:07.9+00:00", utc, &stp));
1559+
EXPECT_EQ(stp, time_point<DS>::max());
1560+
EXPECT_EQ("1970-01-01T00:02:07+00:00", format(RFC3339_full, stp, utc));
1561+
EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T00:02:08+00:00", utc, &stp));
1562+
1563+
EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T23:57:52+00:00", utc, &stp));
1564+
EXPECT_EQ(stp, time_point<DS>::min());
1565+
EXPECT_EQ("1969-12-31T23:57:52+00:00", format(RFC3339_full, stp, utc));
1566+
EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T23:57:51.9+00:00", utc, &stp));
15281567

1529-
// TODO: Add tests that parsing times with fractional seconds overflow
1530-
// appropriately. This can't be done until cctz::parse() properly detects
1531-
// overflow when combining the chrono seconds and femto.
1568+
using DM = chrono::duration<std::int8_t, chrono::minutes::period>;
1569+
time_point<DM> mtp;
1570+
1571+
EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T02:07:59+00:00", utc, &mtp));
1572+
EXPECT_EQ(mtp, time_point<DM>::max());
1573+
EXPECT_EQ("1970-01-01T02:07:00+00:00", format(RFC3339_full, mtp, utc));
1574+
EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T02:08:00+00:00", utc, &mtp));
1575+
1576+
EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T21:52:00+00:00", utc, &mtp));
1577+
EXPECT_EQ(mtp, time_point<DM>::min());
1578+
EXPECT_EQ("1969-12-31T21:52:00+00:00", format(RFC3339_full, mtp, utc));
1579+
EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T21:51:59+00:00", utc, &mtp));
1580+
}
1581+
1582+
TEST(Parse, TimePointOverflowFloor) {
1583+
const time_zone utc = utc_time_zone();
1584+
1585+
using D = chrono::duration<std::int64_t, std::micro>;
1586+
time_point<D> tp;
1587+
1588+
EXPECT_TRUE(
1589+
parse(RFC3339_full, "294247-01-10T04:00:54.7758079+00:00", utc, &tp));
1590+
EXPECT_EQ(tp, time_point<D>::max());
1591+
EXPECT_EQ("294247-01-10T04:00:54.775807+00:00",
1592+
format(RFC3339_full, tp, utc));
1593+
#if 0
1594+
// TODO(#199): Will fail until cctz::parse() properly detects overflow.
1595+
EXPECT_FALSE(
1596+
parse(RFC3339_full, "294247-01-10T04:00:54.7758080+00:00", utc, &tp));
1597+
EXPECT_TRUE(
1598+
parse(RFC3339_full, "-290308-12-21T19:59:05.2241920+00:00", utc, &tp));
1599+
EXPECT_EQ(tp, time_point<D>::min());
1600+
EXPECT_EQ("-290308-12-21T19:59:05.224192+00:00",
1601+
format(RFC3339_full, tp, utc));
1602+
EXPECT_FALSE(
1603+
parse(RFC3339_full, "-290308-12-21T19:59:05.2241919+00:00", utc, &tp));
1604+
#endif
15321605
}
15331606

15341607
//

absl/time/internal/cctz/src/time_zone_if.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ class TimeZoneIf {
5656

5757
// Convert between time_point<seconds> and a count of seconds since the
5858
// Unix epoch. We assume that the std::chrono::system_clock and the
59-
// Unix clock are second aligned, but not that they share an epoch.
59+
// Unix clock are second aligned, and that the results are representable.
60+
// (That is, that they share an epoch, which is required since C++20.)
6061
inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) {
6162
return (tp - std::chrono::time_point_cast<seconds>(
6263
std::chrono::system_clock::from_time_t(0)))

0 commit comments

Comments
 (0)