Commit 084aa074 authored by Abseil Team's avatar Abseil Team Committed by dinord
Browse files

Export of internal Abseil changes

--
42dc250867db8816381a38596e00a3b27b7dbc37 by Gennadiy Rozental <rogeeff@google.com>:

Import of CCTZ from GitHub.

PiperOrigin-RevId: 401863616
GitOrigin-RevId: 42dc250867db8816381a38596e00a3b27b7dbc37
Change-Id: Ia1f100293e0c0845de76d986a170b5ca8d15f1a3
parent 4a995b1e
......@@ -22,6 +22,7 @@
#include <chrono>
#include <cstdint>
#include <limits>
#include <string>
#include <utility>
......@@ -41,20 +42,9 @@ using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead.
namespace detail {
template <typename D>
inline std::pair<time_point<seconds>, D> split_seconds(
const time_point<D>& tp) {
auto sec = std::chrono::time_point_cast<seconds>(tp);
auto sub = tp - sec;
if (sub.count() < 0) {
sec -= seconds(1);
sub += seconds(1);
}
return {sec, std::chrono::duration_cast<D>(sub)};
}
inline std::pair<time_point<seconds>, seconds> split_seconds(
const time_point<seconds>& tp) {
return {tp, seconds::zero()};
}
std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp);
std::pair<time_point<seconds>, seconds> split_seconds(
const time_point<seconds>& tp);
} // namespace detail
// 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>&,
const femtoseconds&, const time_zone&);
bool parse(const std::string&, const std::string&, const time_zone&,
time_point<seconds>*, femtoseconds*, std::string* err = nullptr);
template <typename Rep, std::intmax_t Denom>
bool join_seconds(
const time_point<seconds>& sec, const femtoseconds& fs,
time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp);
template <typename Rep, std::intmax_t Num>
bool join_seconds(
const time_point<seconds>& sec, const femtoseconds& fs,
time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp);
template <typename Rep>
bool join_seconds(
const time_point<seconds>& sec, const femtoseconds& fs,
time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp);
bool join_seconds(const time_point<seconds>& sec, const femtoseconds&,
time_point<seconds>* tpp);
} // namespace detail
// 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,
const time_zone& tz, time_point<D>* tpp) {
time_point<seconds> sec;
detail::femtoseconds fs;
const bool b = detail::parse(fmt, input, tz, &sec, &fs);
if (b) {
// TODO: Return false if unrepresentable as a time_point<D>.
*tpp = std::chrono::time_point_cast<D>(sec);
*tpp += std::chrono::duration_cast<D>(fs);
return detail::parse(fmt, input, tz, &sec, &fs) &&
detail::join_seconds(sec, fs, tpp);
}
namespace detail {
// Split a time_point<D> into a time_point<seconds> and a D subseconds.
// Undefined behavior if time_point<seconds> is not of sufficient range.
// Note that this means it is UB to call cctz::time_zone::lookup(tp) or
// cctz::format(fmt, tp, tz) with a time_point that is outside the range
// of a 64-bit std::time_t.
template <typename D>
std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp) {
auto sec = std::chrono::time_point_cast<seconds>(tp);
auto sub = tp - sec;
if (sub.count() < 0) {
sec -= seconds(1);
sub += seconds(1);
}
return b;
return {sec, std::chrono::duration_cast<D>(sub)};
}
inline std::pair<time_point<seconds>, seconds> split_seconds(
const time_point<seconds>& tp) {
return {tp, seconds::zero()};
}
// Join a time_point<seconds> and femto subseconds into a time_point<D>.
// Floors to the resolution of time_point<D>. Returns false if time_point<D>
// is not of sufficient range.
template <typename Rep, std::intmax_t Denom>
bool join_seconds(
const time_point<seconds>& sec, const femtoseconds& fs,
time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp) {
using D = std::chrono::duration<Rep, std::ratio<1, Denom>>;
// TODO(#199): Return false if result unrepresentable as a time_point<D>.
*tpp = std::chrono::time_point_cast<D>(sec);
*tpp += std::chrono::duration_cast<D>(fs);
return true;
}
template <typename Rep, std::intmax_t Num>
bool join_seconds(
const time_point<seconds>& sec, const femtoseconds&,
time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp) {
using D = std::chrono::duration<Rep, std::ratio<Num, 1>>;
auto count = sec.time_since_epoch().count();
if (count >= 0 || count % Num == 0) {
count /= Num;
} else {
count /= Num;
count -= 1;
}
if (count > (std::numeric_limits<Rep>::max)()) return false;
if (count < (std::numeric_limits<Rep>::min)()) return false;
*tpp = time_point<D>() + D{static_cast<Rep>(count)};
return true;
}
template <typename Rep>
bool join_seconds(
const time_point<seconds>& sec, const femtoseconds&,
time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp) {
using D = std::chrono::duration<Rep, std::ratio<1, 1>>;
auto count = sec.time_since_epoch().count();
if (count > (std::numeric_limits<Rep>::max)()) return false;
if (count < (std::numeric_limits<Rep>::min)()) return false;
*tpp = time_point<D>() + D{static_cast<Rep>(count)};
return true;
}
inline bool join_seconds(const time_point<seconds>& sec, const femtoseconds&,
time_point<seconds>* tpp) {
*tpp = sec;
return true;
}
} // namespace detail
} // namespace cctz
} // namespace time_internal
ABSL_NAMESPACE_END
......
......@@ -13,6 +13,7 @@
// limitations under the License.
#include <chrono>
#include <cstdint>
#include <iomanip>
#include <sstream>
#include <string>
......@@ -1504,7 +1505,7 @@ TEST(Parse, MaxRange) {
parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp));
EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::max());
EXPECT_FALSE(
parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp));
parse(RFC3339_sec, "292277026596-12-04T14:30:08-01:00", utc, &tp));
// tests the lower limit using +00:00 offset
EXPECT_TRUE(
......@@ -1525,10 +1526,82 @@ TEST(Parse, MaxRange) {
parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01", utc, &tp));
EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01",
utc, &tp));
}
TEST(Parse, TimePointOverflow) {
const time_zone utc = utc_time_zone();
using D = chrono::duration<std::int64_t, std::nano>;
time_point<D> tp;
EXPECT_TRUE(
parse(RFC3339_full, "2262-04-11T23:47:16.8547758079+00:00", utc, &tp));
EXPECT_EQ(tp, time_point<D>::max());
EXPECT_EQ("2262-04-11T23:47:16.854775807+00:00",
format(RFC3339_full, tp, utc));
#if 0
// TODO(#199): Will fail until cctz::parse() properly detects overflow.
EXPECT_FALSE(
parse(RFC3339_full, "2262-04-11T23:47:16.8547758080+00:00", utc, &tp));
EXPECT_TRUE(
parse(RFC3339_full, "1677-09-21T00:12:43.1452241920+00:00", utc, &tp));
EXPECT_EQ(tp, time_point<D>::min());
EXPECT_EQ("1677-09-21T00:12:43.145224192+00:00",
format(RFC3339_full, tp, utc));
EXPECT_FALSE(
parse(RFC3339_full, "1677-09-21T00:12:43.1452241919+00:00", utc, &tp));
#endif
using DS = chrono::duration<std::int8_t, chrono::seconds::period>;
time_point<DS> stp;
EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T00:02:07.9+00:00", utc, &stp));
EXPECT_EQ(stp, time_point<DS>::max());
EXPECT_EQ("1970-01-01T00:02:07+00:00", format(RFC3339_full, stp, utc));
EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T00:02:08+00:00", utc, &stp));
EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T23:57:52+00:00", utc, &stp));
EXPECT_EQ(stp, time_point<DS>::min());
EXPECT_EQ("1969-12-31T23:57:52+00:00", format(RFC3339_full, stp, utc));
EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T23:57:51.9+00:00", utc, &stp));
// TODO: Add tests that parsing times with fractional seconds overflow
// appropriately. This can't be done until cctz::parse() properly detects
// overflow when combining the chrono seconds and femto.
using DM = chrono::duration<std::int8_t, chrono::minutes::period>;
time_point<DM> mtp;
EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T02:07:59+00:00", utc, &mtp));
EXPECT_EQ(mtp, time_point<DM>::max());
EXPECT_EQ("1970-01-01T02:07:00+00:00", format(RFC3339_full, mtp, utc));
EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T02:08:00+00:00", utc, &mtp));
EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T21:52:00+00:00", utc, &mtp));
EXPECT_EQ(mtp, time_point<DM>::min());
EXPECT_EQ("1969-12-31T21:52:00+00:00", format(RFC3339_full, mtp, utc));
EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T21:51:59+00:00", utc, &mtp));
}
TEST(Parse, TimePointOverflowFloor) {
const time_zone utc = utc_time_zone();
using D = chrono::duration<std::int64_t, std::micro>;
time_point<D> tp;
EXPECT_TRUE(
parse(RFC3339_full, "294247-01-10T04:00:54.7758079+00:00", utc, &tp));
EXPECT_EQ(tp, time_point<D>::max());
EXPECT_EQ("294247-01-10T04:00:54.775807+00:00",
format(RFC3339_full, tp, utc));
#if 0
// TODO(#199): Will fail until cctz::parse() properly detects overflow.
EXPECT_FALSE(
parse(RFC3339_full, "294247-01-10T04:00:54.7758080+00:00", utc, &tp));
EXPECT_TRUE(
parse(RFC3339_full, "-290308-12-21T19:59:05.2241920+00:00", utc, &tp));
EXPECT_EQ(tp, time_point<D>::min());
EXPECT_EQ("-290308-12-21T19:59:05.224192+00:00",
format(RFC3339_full, tp, utc));
EXPECT_FALSE(
parse(RFC3339_full, "-290308-12-21T19:59:05.2241919+00:00", utc, &tp));
#endif
}
//
......
......@@ -56,7 +56,8 @@ class TimeZoneIf {
// Convert between time_point<seconds> and a count of seconds since the
// Unix epoch. We assume that the std::chrono::system_clock and the
// Unix clock are second aligned, but not that they share an epoch.
// Unix clock are second aligned, and that the results are representable.
// (That is, that they share an epoch, which is required since C++20.)
inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) {
return (tp - std::chrono::time_point_cast<seconds>(
std::chrono::system_clock::from_time_t(0)))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment