Since 2022 the TZif format defined in the zic(8) man page has said that
links can refer to other links, rather than only referring to a zone.
This isn't supported by the C++20 spec, which assumes that the target()
for a chrono::time_zone_link always names a chrono::time_zone, not
another chrono::time_zone_link.
This hasn't been a problem until now, because there are no entries in
the tzdata file that chain links together. However, Debian Sid has
changed the target of the Asia/Chungking link from the Asia/Shanghai
zone to the Asia/Chongqing link, creating a link chain. The libstdc++
code is unable to handle this, so chrono::locate_zone("Asia/Chungking")
will fail with the tzdata.zi file from Debian Sid.
It seems likely that the C++ spec will need a change to allow link
chains, so that the original structure of the IANA database can be fully
represented by chrono::tzdb. The alternative would be for chrono::tzdb
to flatten all chains when loading the data, so that a link's target is
always a zone, but this means throwing away information present in the
tzdata.zi input file.
In anticipation of a change to the spec, this commit adds support for
chained links to libstdc++. When a name is found to be a link, we try to
find its target in the list of zones as before, but now if the target
isn't the name of a zone we don't fail. Instead we look for another link
with that name, and keep doing that until we reach the end of the chain
of links, and then look up the last target as a zone.
This new logic would get stuck in a loop if the tzdata.zi file is buggy
and defines a link chain that contains a cycle, e.g. two links that
refer to each other. To deal with that unlikely case, we use the
tortoise and hare algorithm to detect cycles in link chains, and throw
an exception if we detect a cycle. Cycles in links should never happen,
and it is expected that link chains will be short (if they occur at all)
and so the code is optimized for short chains without cycles. Longer
chains (four or more links) and cycles will do more work, but won't fail
to resolve a chain or get stuck in a loop.
The new test file checks various forms of broken links and cycles.
Also add a new check in the testsuite that every element in the
get_tzdb().zones and get_tzdb().links sequences can be successfully
found using locate_zone.
libstdc++-v3/ChangeLog:
PR libstdc++/114770
* src/c++20/tzdb.cc (do_locate_zone): Support links that have
another link as their target.
* testsuite/std/time/tzdb/1.cc: Check that all zones and links
can be found by locate_zone.
* testsuite/std/time/tzdb/links.cc: New test.
The std/time/year_month_day/io.cc test assumes that %x in the fr_FR
locale is %d/%m/%Y but on FreeBSD it is %d.%m.%Y instead. Make the test
PASS for either format.
Similarly, 27_io/manipulators/extended/get_time/char/2.cc expects that
%a in the de_DE locale is "Di" but on FreeBSD it's "Di." with a trailing
period. Adjust the input string to be "1971 Di." instead of "Di 1971"
and that way if %a doesn't expect the trailing '.' it simply won't
extract it from the stream.
This fixes:
FAIL: std/time/year_month_day/io.cc -std=gnu++20 execution test
FAIL: 27_io/manipulators/extended/get_time/char/2.cc -std=gnu++17 execution test
libstdc++-v3/ChangeLog:
* testsuite/27_io/manipulators/extended/get_time/char/2.cc:
Adjust input string so that it matches %a with or without a
trailing period.
* testsuite/std/time/year_month_day/io.cc: Adjust expected
format for %x in the fr_FR locale.
When parsing a std::chrono::sys_days (or a sys_time with an even longer
period) we should not require a time-of-day to be present in the input,
because we can't represent that in the result type anyway.
Rather than trying to decide which specializations should require a
time-of-date and which should not, follow the direction of Howard
Hinnant's date library, which allows extracting a sys_time of any period
from input that only contains a date, defaulting the time-of-day part to
00:00:00. This seems consistent with the intent of the standard, which
says it's an error "If the parse fails to decode a valid date" (i.e., it
doesn't care about decoding a valid time, only a date).
libstdc++-v3/ChangeLog:
PR libstdc++/114240
* include/bits/chrono_io.h (_Parser::operator()): Assume
hours(0) for a time_point, so that a time is not required
to be present.
* testsuite/std/time/parse/114240.cc: New test.
Implementing all chrono::from_stream overloads in terms of
chrono::sys_time meant that a leap second time like 23:59:60.001 cannot
be parsed, because that cannot be represented in a sys_time.
The fix to support parsing leap seconds as utc_time is to convert the
parsed date to utc_time<days> and then add the parsed time to that,
which allows the result to land in a leap second, rather than doing all
the arithmetic with sys_time which doesn't have leap seconds.
For local_time we also allow %S to parse a 60s value, because doing
otherwise might disallow some valid uses. We can't know all use cases
users have for treating times as local_time.
For all other clocks, we can reject times that have 60 or 60.nnn as the
seconds part, because that cannot occur in a valid UNIX, GPS, or TAI
time. Since our chrono::file_clock uses sys_time, it can't occur for
that clock either.
In order to support this a new _M_is_leap_second member is needed in the
_Parser type. This can be added at the end, where most targets currently
have padding bytes. Similar to what I did recently for formatter _Spec
structs, we can also reserve additional padding bits for future
expansion.
This also fixes bugs in the from_stream overloads for utc_time,
tai_time, gps_time, and file_time, which were not using time_point_cast
to explicitly convert to the result type. That's needed because the
result type might have lower precision than the value returned from
from_sys or from_utc, which has a precision no lower than seconds.
libstdc++-v3/ChangeLog:
PR libstdc++/114279
* include/bits/chrono_io.h (_Parser::_M_is_leap_second): New
data member.
(_Parser::_M_reserved): Reserve padding bits for future use.
(_Parser::operator()): Set _M_is_leap_second if %S reads 60s.
(from_stream): Only allow _M_is_leap_second for utc_time and
local_time. Adjust arithmetic for utc_time so that leap seconds
are preserved. Use time_point_cast to convert to a possibly
lower-precision result type.
* testsuite/std/time/parse.cc: Move to ...
* testsuite/std/time/parse/parse.cc: ... here.
* testsuite/std/time/parse/114279.cc: New test.
The list in tzdb.cc isn't the only hardcoded list of leap seconds in the
library, there's the one defined inline in <chrono> (to avoid loading
the tzdb for the common case) and another in a testcase. This updates
them to note that there are no new leap seconds in 2024 either, until at
least 2024-12-28.
libstdc++-v3/ChangeLog:
* include/std/chrono (__get_leap_second_info): Update expiry
time for hardcoded list of leap seconds.
* testsuite/std/time/tzdb/leap_seconds.cc: Update comment.
The test_format() function contained an incorrect assertion but wasn't
actually being called from main.
libstdc++-v3/ChangeLog:
* testsuite/std/time/clock/gps/io.cc: Fix expected result in
assertion and call test_format() from main.
The test_format() function contained an incorrect assertion but wasn't
actually being called from main.
libstdc++-v3/ChangeLog:
* testsuite/std/time/clock/file/io.cc: Fix expected result in
assertion and call test_format() from main.
Currently trying to use std::format with certain specializations of
std::chrono::time_point is ill-formed, due to one member function of the
__formatter_chrono type which tries to write a time_point to an ostream.
For sys_time<floating-point> or sys_time with a period greater than days
there is no operator<< that can be used.
That operator<< is only needed when using an empty chrono-specs in the
format string, like "{}", but the ill-formed expression gives an error
even if not actually used. This means it's not possible to format some
other specializations of chrono::time_point, even when using a non-empty
chrono-specs.
This fixes it by avoiding using 'os << t' for all chrono::time_point
specializations, and instead using std::format("{:L%F %T}", t). So that
we continue to reject std::format("{}", sys_time{1.0s}) a check for
empty chrono-specs is added to the formatter<sys_time<D>, C>
specialization.
While testing this I noticed that the output for %S with a
floating-point duration was incorrect, as the subseconds part was being
appended to the seconds without a decimal point, and without the correct
number of leading zeros.
libstdc++-v3/ChangeLog:
PR libstdc++/113500
* include/bits/chrono_io.h (__formatter_chrono::_M_S): Fix
printing of subseconds with floating-point rep.
(__formatter_chrono::_M_format_to_ostream): Do not write
time_point specializations directly to the ostream.
(formatter<chrono::sys_time<D>, C>::parse): Do not allow an
empty chrono-spec if the type fails to meet the constraints for
writing to an ostream with operator<<.
* testsuite/std/time/clock/file/io.cc: Check formatting
non-integral times with empty chrono-specs.
* testsuite/std/time/clock/gps/io.cc: Likewise.
* testsuite/std/time/clock/utc/io.cc: Likewise.
* testsuite/std/time/hh_mm_ss/io.cc: Likewise.
THe std::chrono::file_clock conversions were not using common_type and
so failed to compile when converting anything that should have increased
precision after arithmetic with a std::chrono::seconds value.
libstdc++-v3/ChangeLog:
* include/bits/chrono.h (__file_clock::from_sys)
(__file_clock::to_sys, __file_clock::_S_from_sys)
(__file_clock::_S_to_sys): Use common_type for return type.
* testsuite/std/time/clock/file/members.cc: Check round trip
conversion for time with lower precision that seconds.
This change makes std::make_format_args refuse to create dangling
references to temporaries. This makes the std::vformat API safer. This
was approved in Kona 2023 as a DR for C++20 so the change is implemented
unconditionally.
libstdc++-v3/ChangeLog:
* include/bits/chrono_io.h (__formatter_chrono): Always use
lvalue arguments to make_format_args.
* include/std/format (make_format_args): Change parameter pack
from forwarding references to lvalue references. Remove use of
remove_reference_t which is now unnecessary.
(format_to, formatted_size): Remove incorrect forwarding of
arguments.
* include/std/ostream (print): Remove forwarding of arguments.
* include/std/print (print): Likewise.
* testsuite/20_util/duration/io.cc: Use lvalues as arguments to
make_format_args.
* testsuite/std/format/arguments/args.cc: Likewise.
* testsuite/std/format/arguments/lwg3810.cc: Likewise.
* testsuite/std/format/functions/format.cc: Likewise.
* testsuite/std/format/functions/vformat_to.cc: Likewise.
* testsuite/std/format/string.cc: Likewise.
* testsuite/std/time/day/io.cc: Likewise.
* testsuite/std/time/month/io.cc: Likewise.
* testsuite/std/time/weekday/io.cc: Likewise.
* testsuite/std/time/year/io.cc: Likewise.
* testsuite/std/time/year_month_day/io.cc: Likewise.
* testsuite/std/format/arguments/args_neg.cc: New test.
The following invoke signed integer overflow (UB) [1]:
month + months{MAX} // where MAX is the maximum value of months::rep
month + months{MIN} // where MIN is the maximum value of months::rep
month - months{MIN} // where MIN is the minimum value of months::rep
weekday + days {MAX} // where MAX is the maximum value of days::rep
weekday - days {MIN} // where MIN is the minimum value of days::rep
For the additions to MAX, the crux of the problem is that, in libstdc++,
months::rep and days::rep are int64_t. Other implementations use int32_t, cast
operands to int64_t and perform arithmetic operations without risk of
overflowing.
For month + months{MIN}, the implementation follows the Standard's "returns
clause" and evaluates:
modulo(static_cast<long long>(unsigned{__x}) + (__y.count() - 1), 12);
Overflow occurs when MIN - 1 is evaluated. Casting to a larger type could help
but, unfortunately again, this is not possible for libstdc++.
For the subtraction of MIN, the problem is that -MIN is not representable.
It's fair to say that the intention is for these additions/subtractions to
be performed in modulus (12 or 7) arithmetic so that no overflow is expected.
To fix these UB, this patch implements:
template <unsigned __d, typename _T>
unsigned __add_modulo(unsigned __x, _T __y);
template <unsigned __d, typename _T>
unsigned __sub_modulo(unsigned __x, _T __y);
which respectively, returns the remainder of Euclidean division of, __x + __y
and __x - __y by __d without overflowing. These functions replace
constexpr unsigned __modulo(long long __n, unsigned __d);
which also calculates the reminder of __n, where __n is the result of the
addition or subtraction. Hence, these operations might invoke UB before __modulo
is called and thus, __modulo can't do anything to remediate the issue.
In addition to solve the UB issues, __add_modulo and __sub_modulo allow better
codegen (shorter and branchless) on x86-64 and ARM [2].
[1] https://godbolt.org/z/a9YfWdn57
[2] https://godbolt.org/z/Gh36cr7E4
libstdc++-v3/ChangeLog:
* include/std/chrono: Fix + and - for months and weekdays.
* testsuite/std/time/month/1.cc: Add constexpr tests against overflow.
* testsuite/std/time/month/2.cc: New test for extreme values.
* testsuite/std/time/weekday/1.cc: Add constexpr tests against overflow.
* testsuite/std/time/weekday/2.cc: New test for extreme values.
During discussion of LWG 4022 I noticed that we do not correctly
implement floored division for the century. We were just truncating
towards zero, rather than applying the floor function. For negative
values that rounds the wrong way.
libstdc++-v3/ChangeLog:
* include/bits/chrono_io.h (__formatter_chrono::_M_C_y_Y): Fix
rounding for negative centuries.
* testsuite/std/time/year/io.cc: Check %C for negative years.
The following has undefined behaviour (signed overflow) [1]:
weekday max{sys_days{days{numeric_limits<days::rep>::max()}}};
The issue is in this line when __n is very large and __n + 4 overflows:
return weekday(__n >= -4 ? (__n + 4) % 7 : (__n + 5) % 7 + 6);
In addition to fixing this bug, the new implementation makes the compiler emit
shorter and branchless code for x86-64 and ARM [2].
[1] https://godbolt.org/z/1s5bv7KfT
[2] https://godbolt.org/z/zKsabzrhs
libstdc++-v3/ChangeLog:
* include/std/chrono (weekday::_S_from_days): Fix UB.
* testsuite/std/time/weekday/1.cc: Add test for overflow.
I meant to add these changes as part of r14-4959-g7d06b29f814580 but
missed these files out.
libstdc++-v3/ChangeLog:
* testsuite/std/time/clock/file/io.cc: Double timeout using
dg-timeout-factor.
* testsuite/std/time/clock/gps/io.cc: Likewise.
* testsuite/std/time/clock/local/io.cc: Likewise.
* testsuite/std/time/clock/system/io.cc: Likewise.
* testsuite/std/time/clock/tai/io.cc: Likewise.
* testsuite/std/time/clock/utc/io.cc: Likewise.
These tests do not run by default, because the c++20 effective target
selector isn't matched.
libstdc++-v3/ChangeLog:
* testsuite/23_containers/unordered_map/operations/1.cc: Add
dg-options for C++20 mode.
* testsuite/23_containers/unordered_multimap/operations/1.cc:
Likewise.
* testsuite/23_containers/unordered_multiset/operations/1.cc:
Likewise.
* testsuite/23_containers/unordered_set/operations/1.cc:
Likewise.
* testsuite/std/time/parse.cc: Move dg-options before dg-do.
We fail to diagnose an error and extract an incorrect time for cases
like "25:59" >> parse("%H:%M", mins). The bad "25" hour value gets
ignored (on the basis that we might not care about it if trying to
extract something like a weekday or a month name), but then when we get
to the end of the function we think we have a valid time from "59" and
so the result is 00:59.
The problem is that the '__bad_h' value is used for "no hour value read
yet" as well as "bad hour value read". If we just set __h = __bad_h and
continue, we can't tell later that we read an invalid hour.
The fix is to set failbit early when we're trying to extract a
time-of-day (e.g. duration or time_point) and we encounter an invalid
hour, minute, or second value. We can still delay other error checking
to the end.
libstdc++-v3/ChangeLog:
* include/bits/chrono_io.h (_Parser::operator()): Set failbit
early if invalid values are read when _M_need & _TimeOfDay is
non-zero.
* testsuite/std/time/parse.cc: Check that "25:59" cannot be
parsed for "%H:%M".
libstdc++-v3/ChangeLog:
PR libstdc++/111162
* include/bits/chrono_io.h (_Parser::Operator()): Check %C
values are in range of year::min() to year::max().
* testsuite/std/time/parse.cc: Check out of range centuries.
This adds the missing C++20 features to <chrono>.
I've implemented my proposed resolutions to LWG issues 3960, 3961, and
3962. There are some unimplemented flags such as %OI which I think are
not implementable in general. It might be possible to use na_llanginfo
with ALT_DIGITS, but that isn't available on all targets. I intend to
file another LWG issue about that.
libstdc++-v3/ChangeLog:
PR libstdc++/104167
* include/bits/chrono_io.h (operator|=, operator|): Add noexcept
to _ChronoParts operators.
(from_stream, parse): Define new functions.
(__detail::_Parse, __detail::_Parser): New class templates.
* include/std/chrono (__cpp_lib_chrono): Define to 201907L for
C++20.
* include/std/version (__cpp_lib_chrono): Likewise.
* testsuite/20_util/duration/arithmetic/constexpr_c++17.cc:
Adjust expected value of feature test macro.
* testsuite/20_util/duration/io.cc: Test parsing.
* testsuite/std/time/clock/file/io.cc: Likewise.
* testsuite/std/time/clock/gps/io.cc: Likewise.
* testsuite/std/time/clock/system/io.cc: Likewise.
* testsuite/std/time/clock/tai/io.cc: Likewise.
* testsuite/std/time/clock/utc/io.cc: Likewise.
* testsuite/std/time/day/io.cc: Likewise.
* testsuite/std/time/month/io.cc: Likewise.
* testsuite/std/time/month_day/io.cc: Likewise.
* testsuite/std/time/weekday/io.cc: Likewise.
* testsuite/std/time/year/io.cc: Likewise.
* testsuite/std/time/year_month/io.cc: Likewise.
* testsuite/std/time/year_month_day/io.cc: Likewise.
* testsuite/std/time/syn_c++20.cc: Check value of macro and for
the existence of parse and from_stream in namespace chrono.
* testsuite/std/time/clock/local/io.cc: New test.
* testsuite/std/time/parse.cc: New test.
When formatting with an empty chrono spec ("{}") two minus signs were
being added to hh_mm_ss values. This is because the __is_neg flag was
checked to add one explicitly, and then the ostream operator added
another one.
We should only check the __is_neg flag for durations, because those are
the only types which are modified to be non-negative before calling
_M_format. We don't change hh_mm_ss values to be negative, because that
would require performing arithmetic on the hh_mm_ss members to sum them,
and then again to construct a new hh_mm_ss object with the positive
value. Instead, we can just be careful about using the __is_neg flag
correctly.
To fix the bug, _M_format_to_ostream no longer checks the __is_neg flag
for non-durations, and _M_format doesn't set it for hh_mm_ss until after
the call to _M_format_to_ostream. We can also avoid setting it for types
that it doesn't apply to, by making the __print_sign lambda only inspect
it for duration and hh_mm_ss types.
libstdc++-v3/ChangeLog:
* include/bits/chrono_io.h (__formatter_chrono::_M_format):
Do not set __is_neg for hh_mm_ss before calling
_M_format_to_ostream. Change __print_sign lambda to only check
__is_neg for durations and hh_mm_ss types.
(__formatter_chrono::_M_format_to_ostream): Only check __is_neg
for duration types.
* testsuite/std/time/hh_mm_ss/io.cc: Check negative values.
The r14-2640-gf4bce119f617dc commit only removed fractional seconds for
time points, but it needs to be done for durations and hh_mm_ss types
too.
libstdc++-v3/ChangeLog:
PR libstdc++/110719
* include/bits/chrono_io.h (__formatter_chrono::_S_floor_seconds):
Handle duration and hh_mm_ss.
* testsuite/20_util/duration/io.cc: Check locale-specific
formats.
* testsuite/std/time/hh_mm_ss/io.cc: Likewise.
This fixes some TODOs in the C++20 <chrono> format support, where the
locale-specific output was incorrect or unimplemented. The approach
taken here is to either use the formatting locale's std::time_put facet
to do the formatting, or to remove subsecond precision from time points
so that locale-specific formats don't print fractional seconds. This
ensures that we are consistent with what the std::time_put facet would
print (which never includes fractional seconds) even if we actually
reimplement the formatting by hand instead of using the facet.
This also fixes a misplaced statement that allowed modifiers for %Z
which should have been on %z instead. There was also some ill-formed
code in an untested branch for formatting time zone names to wide
characters. A new test for zoned_time I/O has been added to exercise
that code properly.
libstdc++-v3/ChangeLog:
PR libstdc++/110719
* include/bits/chrono_io.h (__formatter_chrono::_M_parse): Fix
allowed modifiers for %z and %Z. Fix -Wparentheses and
-Wnarrowing warnings.
(__formatter_chrono::_M_format): Call new functions for %d, %e,
%H, %I, %m and %M.
(__formatter_chrono::_M_c): Use _S_floor_seconds to remove
subsecond precision.
(__formatter_chrono::_M_C_y_Y): Use _M_locale_fmt to handle
modifiers.
(__formatter_chrono::_M_e): Replace with _M_d_e and use
_M_locale_fmt.
(__formatter_chrono::_M_I): Replace with _M_H_I and use
_M_locale_fmt.
(__formatter_chrono::_M_m): New function.
(__formatter_chrono::_M_M): New function.
(__formatter_chrono::_M_r): Use _M_locale_fmt.
(__formatter_chrono::_M_S): Likewise.
(__formatter_chrono::_M_u_w): Likewise.
(__formatter_chrono::_M_U_V_W): Likewise.
(__formatter_chrono::_M_X): Use _S_floor_seconds.
(__formatter_chrono::_M_Z): Fix untested branch for wchar_t.
(__formatter_chrono::_S_altnum): Remove function.
(__formatter_chrono::_S_dd_zero_fill): Remove function.
(__formatter_chrono::_S_floor_seconds): New function.
(__formatter_chrono::_M_locale_fmt): New function.
* testsuite/std/time/clock/system/io.cc: Adjust expected output
for locale-specific formats and check modified formats.
* testsuite/std/time/clock/utc/io.cc: Likewise.
* testsuite/std/time/zoned_time/io.cc: New test.
The logic for handling modified chrono specs like %Ey was just
restarting the loop after each modifier, and not checking whether we'd
already seen a modifier.
libstdc++-v3/ChangeLog:
PR libstdc++/110708
* include/bits/chrono_io.h (__formatter_chrono::_M_parse): Only
allow a single modifier.
* testsuite/std/time/format.cc: Check multiple modifiers.
I borked the logic in r13-4526-g5329e1a8e1480d so that the selected
partial specialization of hh_mm_ss::__subseconds might not be able to
represent the correct number of subseconds. This can result in a
truncated value being stored for the subseconds, e.g., 4755859375 gets
truncated to 460892079 because the correct value doesn't fit in
uint_least32_t.
Instead of checking whether the maximum value of the incoming duration
type can be represented, we would need to check whether that maximum value
can be represented after being converted to the correct precision type:
template<typename _Tp>
static constexpr bool __fits
= duration_cast<precision>(_Duration::max()).count()
<= duration_values<_Tp>::max();
However, this can fail to compile, due to integer overflow in the
constexpr multiplications. Instead, we could limit the check to the case
where the incoming duration has the same period as the precision, where
no conversion is needed and so no overflow can happen. But that seems of
very limited value, as it would only benefit specializations like
hh_mm_ss<duration<int, std::pico>>, which can only represent a
time-of-day between -00:00:00.0215 and +00:00:00.0215 measured in
picoseconds!
Additionally, the hh_mm_ss::__subseconds partial specializations do not
have disjoint constraints, so that some hh_mm_ss specializations result
in ambiguities tying to match a __subseconds partial specialization.
The most practical fix is to just stop using the __fits variable
template in the constraints of the partial specializations. This fixes
the truncated values by not selecting an inappropriate partial
specialization, and fixes the ambiguous match by ensuring the
constraints are disjoint.
Fixing this changes the layout of some specializations, so is an ABI
change. It only affects specializations that have a small (less than
64-bit) representation type and either a very small period (e.g. like
the picosecond specialization above) or a non-power-of-ten period like
ratio<1, 1024>. For example both hh_mm_ss<duration<int, std::pico>> and
hh_mm_ss<duration<int, ratio<1, 1024>> are affected (increasing from 16
bytes to 24 on x86_64), but hh_mm_ss<duration<int, ratio<1, 1000>> and
hh_mm_ss<duration<long, ratio<1, 1024>> are not affected.
libstdc++-v3/ChangeLog:
PR libstdc++/109772
* include/std/chrono (hh_mm_ss::__fits): Remove variable
template.
(hh_mm_ss::__subseconds): Remove __fits from constraints.
* testsuite/std/time/hh_mm_ss/109772.cc: New test.
* testsuite/std/time/hh_mm_ss/1.cc: Adjust expected size for
hh_mm_ss<duration<int, std::pico>>.
Import the new 2023a tzdata.zi file and update the expiry dates of the
hardcoded lists of leapseconds to 2023-12-28.
With the new data, Africa/Egypt no longer has a single unbroken sys_info
from 2014-09-25 to chrono::year::max(). Only check up to 2014-09-01 so
that the test isn't sensitive to differences between 2022g and 2023a
data.
libstdc++-v3/ChangeLog:
PR libstdc++/109288
* include/std/chrono (__detail::__get_leap_second_info): Update
expiry date of hardcoded leapseconds list.
* src/c++20/tzdb.cc (tzdb_list::_Node::_S_read_leap_seconds()):
Likewise.
* src/c++20/tzdata.zi: Import new file from 2023a release.
* testsuite/std/time/time_zone/get_info_local.cc: Only check
transitions for Egypt up to 2014.
This is consistent with the behaviour of glibc, which assumes UTC when
/etc/localtime and TZ do not identify a valid time zone. The fallback
tzdb used when no valid tzdata exists always contains the UTC zone, so
this change means we have a valid tzdb and valid current zone even in
the degenerate case.
With this default we no longer need the AIX-specific kluge to try and
identify TZ values specifying a 0-offset zone. We can just use the UTC
default for those, as it has the same effect.
It's still possible for chrono::current_zone() to fail, because the user
could have provided a custom tzdata.zi file which doesn't contain the
UTC time zone, so the "UTC" default would fail to find a valid zone, and
throw an exception. That's just user error, they should not provide bad
data and expect reasonable behaviour.
libstdc++-v3/ChangeLog:
* src/c++20/tzdb.cc (chrono::tzdb::current_zone()) Use "UTC" if
current time zone cannot be determined.
* testsuite/std/time/tzdb/1.cc: Remove conditions based on
HAVE_TZDB macro and test all members unconditionally.
For targets where the ABI mandates structure layout that has
no padding, like cris-elf, this test started failing when
introduced as an add-on to the existing 1.cc, thereby
effectively causing a regression in testsuite results.
Adding an empty structure to S0, corresponds better to the
layout of hh_mm_ss<seconds>.
PR testsuite/108632
* testsuite/std/time/hh_mm_ss/1.cc (size): Add empty
struct at end of S0.
Use the global override_used to tell whether the target supports the
override functionality that the test_reload and test_erase functions
rely on.
libstdc++-v3/ChangeLog:
* testsuite/std/time/tzdb_list/1.cc: Remove dg-xfail-run-if
and fail gracefully if defining the weak symbol doesn't work.
For a port with signed char and unsigned wchar_t initializing a wchar_t
array with a char is a narrowing conversion. The code is wrong for
assuming that (int)'a' == (int)L'a' anyway, so fix it properly by using
ctype<wchar_t>::widen(char).
libstdc++-v3/ChangeLog:
* testsuite/std/time/clock/utc/io.cc: Use ctype to widen char.
This allows most of the tzdb functionality to be disabled by
configuring with --with-libstdcxx-zoneinfo=no. This might be desirable
for small targets that don't need the time zone support.
libstdc++-v3/ChangeLog:
* src/c++20/tzdb.cc (TZDB_DISABLED): Disable all code for
loading tzdb.
* testsuite/std/time/tzdb/leap_seconds.cc: Require tzdb
effective target.
* testsuite/std/time/tzdb_list/1.cc: Likewise.
libstdc++-v3/ChangeLog:
* include/bits/chrono_io.h (operator<<): Fix syntax errors.
* testsuite/std/time/month_day/io.cc: New test.
* testsuite/std/time/month_day_last/io.cc: New test.
* testsuite/std/time/month_weekday/io.cc: New test.
* testsuite/std/time/month_weekday_last/io.cc: New test.
* testsuite/std/time/weekday_indexed/io.cc: New test.
* testsuite/std/time/weekday_last/io.cc: New test.
* testsuite/std/time/year_month/io.cc: New test.
* testsuite/std/time/year_month_day_last/io.cc: New test.
* testsuite/std/time/year_month_weekday/io.cc: New test.
* testsuite/std/time/year_month_weekday_last/io.cc: New test.
libstdc++-v3/ChangeLog:
PR libstdc++/108265
* include/std/chrono (hh_mm_ss): Do not use chrono::abs if
duration rep is unsigned.
* testsuite/std/time/hh_mm_ss/1.cc: Check unsigned rep.
in leap_seconds.cc, we are testing to see if the function that
overrides the default zoneinfo directory has been called. That
is implemented with a static boolean that needs to be initialized
to false.
Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
libstdc++-v3/ChangeLog:
* testsuite/std/time/tzdb/leap_seconds.cc: Initialize the
override_used test var to false.
This adds the operator<< overloads and std::formatter specializations
required by C++20 so that <chrono> types can be written to ostreams and
printed with std::format.
libstdc++-v3/ChangeLog:
* include/Makefile.am: Add new header.
* include/Makefile.in: Regenerate.
* include/std/chrono (operator<<): Move to new header.
(nonexistent_local_time::_M_make_what_str): Define correctly.
(ambiguous_local_time::_M_make_what_str): Likewise.
* include/bits/chrono_io.h: New file.
* src/c++20/tzdb.cc (operator<<(ostream&, const Rule&)): Use
new ostream output for month and weekday types.
* testsuite/20_util/duration/io.cc: Test std::format support.
* testsuite/std/time/exceptions.cc: Check what() strings.
* testsuite/std/time/syn_c++20.cc: Uncomment local_time_format.
* testsuite/std/time/time_zone/get_info_local.cc: Enable check
for formatted output of local_info objects.
* testsuite/std/time/clock/file/io.cc: New test.
* testsuite/std/time/clock/gps/io.cc: New test.
* testsuite/std/time/clock/system/io.cc: New test.
* testsuite/std/time/clock/tai/io.cc: New test.
* testsuite/std/time/clock/utc/io.cc: New test.
* testsuite/std/time/day/io.cc: New test.
* testsuite/std/time/format.cc: New test.
* testsuite/std/time/hh_mm_ss/io.cc: New test.
* testsuite/std/time/month/io.cc: New test.
* testsuite/std/time/weekday/io.cc: New test.
* testsuite/std/time/year/io.cc: New test.
* testsuite/std/time/year_month_day/io.cc: New test.
This is the largest missing piece of C++20 support. Only the cxx11 ABI
is supported, due to the use of std::string in the API for time zones.
For the old gcc4 ABI, utc_clock and leap seconds are supported, but only
using a hardcoded list of leap seconds, no up-to-date tzdb::leap_seconds
information is available, and no time zones or zoned_time conversions.
The implementation currently depends on a tzdata.zi file being provided
by the OS or the user. The expected location is /usr/share/zoneinfo but
that can be changed using --with-libstdcxx-zoneinfo-dir=PATH. On targets
that support it there is also a weak symbol that users can override in
their own program (which also helps with testing):
extern "C++" const char* __gnu_cxx::zoneinfo_dir_override();
If no file is found, a fallback tzdb object will be created which only
contains the "Etc/UTC" and "Etc/GMT" time zones.
A leapseconds file is also expected in the same directory, but if that
isn't present then a hardcoded list of leapseconds is used, which is
correct at least as far as 2023-06-28 (and it currently looks like no
leap second will be inserted for a few years).
The tzdata.zi and leapseconds files from https://www.iana.org/time-zones
are in the public domain, so shipping copies of them with GCC would be
an option. However, the tzdata.zi file will rapidly become outdated, so
users should really provide it themselves (or convince their OS vendor
to do so). It would also be possible to implement an alternative parser
for the compiled tzdata files (one per time zone) under
/usr/share/zoneinfo. Those files are present on more operating systems,
but do not contain all the information present in tzdata.zi.
Specifically, the "links" are not present, so that e.g. "UTC" and
"Universal" are distinct time zones, rather than both being links to the
canonical "Etc/UTC" zone. For some platforms those files are hard links
to the same file, but there's no indication which zone is the canonical
name and which is a link. Other platforms just store them in different
inodes anyway. I do not plan to add such an alternative parser for the
compiled files. That would need to be contributed by maintainers or
users of targets that require it, if making tzdata.zi available is not
an option. The library ABI would not need to change for a new tzdb
implementation, because everything in tzdb_list, tzdb and time_zone is
implemented as a pimpl (except for the shared_ptr links between nodes,
described below). That means the new exported symbols added by this
commit should be stable even if the implementation is completely
rewritten.
The information from tzdata.zi is parsed and stored in data structures
that closely model the info in the file. This is a space-efficient
representation that uses less memory that storing every transition for
every time zone. It also avoids spending time expanding that
information into time zone transitions that might never be needed by the
program. When a conversion to/from a local time to UTC is requested the
information will be processed to determine the time zone transitions
close to the time being converted.
There is a bug in some time zone transitions. When generating a sys_info
object immediately after one that was previously generated, we need to
find the previous rule that was in effect and note its offset and
letters. This is so that the start time and abbreviation of the new
sys_info will be correct. This only affects time zones that use a format
like "C%sT" where the LETTERS replacing %s are non-empty for standard
time, e.g. "Asia/Shanghai" which uses "CST" for standard time and "CDT"
for daylight time.
The tzdb_list structure maintains a linked list of tzdb nodes using
shared_ptr links. This allows the iterators into the list to share
ownership with the list itself. This offers a non-portable solution to a
lifetime issue in the API. Because tzdb objects can be erased from the
list using tzdb_list::erase_after, separate modules/libraries in a large
program cannot guarantee that any const tzdb& or const time_zone*
remains valid indefinitely. Holding onto a tzdb_list::const_iterator
will extend the tzdb object's lifetime, even if it's erased from the
list. An alternative design would be for the list iterator to hold a
weak_ptr. This would allow users to test whether the tzdb still exists
when the iterator is dereferenced, which is better than just having a
dangling raw pointer. That doesn't actually extend the tzdb's lifetime
though, and every use of it would need to be preceded by checking the
weak_ptr. Using shared_ptr adds a little bit of overhead but allows
users to solve the lifetime issue if they rely on the libstdc++-specific
iterator property.
libstdc++-v3/ChangeLog:
* acinclude.m4 (GLIBCXX_ZONEINFO_DIR): New macro.
* config.h.in: Regenerate.
* config/abi/pre/gnu.ver: Export new symbols.
* configure: Regenerate.
* configure.ac (GLIBCXX_ZONEINFO_DIR): Use new macro.
* include/std/chrono (utc_clock::from_sys): Correct handling
of leap seconds.
(nonexistent_local_time::_M_make_what_str): Define.
(ambiguous_local_time::_M_make_what_str): Define.
(__throw_bad_local_time): Define new function.
(time_zone, tzdb_list, tzdb): Implement all members.
(remote_version, zoned_time, get_leap_second_info): Define.
* include/std/version: Add comment for __cpp_lib_chrono.
* src/c++20/Makefile.am: Add new file.
* src/c++20/Makefile.in: Regenerate.
* src/c++20/tzdb.cc: New file.
* testsuite/lib/libstdc++.exp: Define effective target tzdb.
* testsuite/std/time/clock/file/members.cc: Check file_time
alias and file_clock::now() member.
* testsuite/std/time/clock/gps/1.cc: Likewise for gps_clock.
* testsuite/std/time/clock/tai/1.cc: Likewise for tai_clock.
* testsuite/std/time/syn_c++20.cc: Uncomment everything except
parse.
* testsuite/std/time/clock/utc/leap_second_info.cc: New test.
* testsuite/std/time/exceptions.cc: New test.
* testsuite/std/time/time_zone/get_info_local.cc: New test.
* testsuite/std/time/time_zone/get_info_sys.cc: New test.
* testsuite/std/time/time_zone/requirements.cc: New test.
* testsuite/std/time/tzdb/1.cc: New test.
* testsuite/std/time/tzdb/leap_seconds.cc: New test.
* testsuite/std/time/tzdb_list/1.cc: New test.
* testsuite/std/time/tzdb_list/requirements.cc: New test.
* testsuite/std/time/zoned_time/1.cc: New test.
* testsuite/std/time/zoned_time/custom.cc: New test.
* testsuite/std/time/zoned_time/deduction.cc: New test.
* testsuite/std/time/zoned_time/req_neg.cc: New test.
* testsuite/std/time/zoned_time/requirements.cc: New test.
* testsuite/std/time/zoned_traits.cc: New test.
This uses a single byte for the minutes and seconds members, and places
the bool member next to those single bytes. This means we do not need 40
bytes to store a time that can fit in a single 8-byte integer.
When there is no subsecond precision we can do away with the _M_ss
member altogether. If the subsecond precision is coarse enough, we can
use a smaller representation for _M_ss, e.g. hh_mm_ss<milliseconds> only
needs uint_least32_t for _M_ss, and hh_mm_ss<duration<long, ratio<1,10>>
and hh_mm_ss<duration<int8_t, nano>> only need a single byte. In the
latter case the type can only ever represent up to 255ns anyway, so we
don't need a larger representation type (in such cases, we could even
remove the _M_h, _M_m and _M_s members, but it's a very unlikely
scenario that isn't worth optimizing for).
Except for specializations with a floating-point rep or using higher
precision than nanoseconds, hh_mm_ss should now fit in 16 bytes, or even
12 bytes for x86-32 where alignof(long long) == 4.
libstdc++-v3/ChangeLog:
* include/std/chrono (chrono::hh_mm_ss): Do not use 64-bit
representations for all four duration members. Reorder members.
(hh_mm_ss::hh_mm_ss()): Define as defaulted.
(hh_mm_ss::hh_mm_ss(Duration)): Delegate to a new private
constructor, instead of calling chrono::abs repeatedly.
* testsuite/std/time/hh_mm_ss/1.cc: Check floating-point
representations. Check default constructor. Check sizes.
This test of leap second handling is taken from the C++20 standard.
libstdc++-v3/ChangeLog:
* testsuite/std/time/clock/utc/1.cc: Check handling across leap
second insertion.
Also add the basic types for timezones, without the non-inline
definitions needed to actually use them.
The get_leap_second_info function currently uses a hardcoded list of
leap seconds, correct as of the end of 2022. That needs to be replaced
with a dynamically generated list read from the system tzdata. That will
be done in a later patch.
libstdc++-v3/ChangeLog:
* include/std/chrono (utc_clock, tai_clock, gps_clock): Define.
(clock_time_conversion, clock_cast): Define.
(sys_info, local_info): Define structs for timezone information.
(nonexistent_local_time, ambiguous_local_time): Define
exceptions for invalid times.
(time_zone, time_zone_link, leap_second, zoned_traits, tzdb)
(tzdb_list): Define classes representing time zones.
(get_leap_second_info): Define new function returning leap
second offset for a given time point.
* testsuite/std/time/clock/gps/1.cc: New test.
* testsuite/std/time/clock/tai/1.cc: New test.
* testsuite/std/time/clock/utc/1.cc: New test.
This patch reimplements std::chrono::year::is_leap(). Leap year check is
ubiquitously implemented (including here) as:
y % 4 == 0 && (y % 100 != 0 || y % 400 == 0).
The rationale being that testing divisibility by 4 first implies an earlier
return for 75% of the cases, therefore, avoiding the needless calculations of
y % 100 and y % 400. Although this fact is true, it does not take into account
the cost of branching. This patch, instead, tests divisibility by 100 first:
(y % 100 != 0 || y % 400 == 0) && y % 4 == 0.
It is certainly counterintuitive that this could be more efficient since among
the three divisibility tests (4, 100 and 400) the one by 100 is the only one
that can never provide a definitive answer and a second divisibility test (by 4
or 400) is always required. However, measurements [1] in x86_64 suggest this is
3x more efficient! A possible explanation is that checking divisibility by 100
first implies a split in the execution path with probabilities of (1%, 99%)
rather than (25%, 75%) when divisibility by 4 is checked first. This decreases
the entropy of the branching distribution which seems to help prediction.
Given that y belongs to [-32767, 32767] [time.cal.year.members], a more
efficient algorithm [2] to check divisibility by 100 is used (instead of
y % 100 != 0). Measurements suggest that this optimization improves performance
by 20%.
The patch adds a test that exhaustively compares the result of this
implementation with the ubiquitous one for all y in [-32767, 32767]. Although
its completeness, the test completes in a matter of seconds.
References:
[1] https://stackoverflow.com/a/60646967/1137388
[2] https://accu.org/journals/overload/28/155/overload155.pdf#page=16
libstdc++-v3/ChangeLog:
* include/std/chrono (year::is_leap): New implementation.
* testsuite/std/time/year/2.cc: New test.
This patch reimplements std::chrono::year_month_day::_M_days_since_epoch()
which calculates the number of elapsed days since 1970/01/01. The new
implementation is based on Proposition 6.2 of Neri and Schneider, "Euclidean
Affine Functions and Applications to Calendar Algorithms" available at
https://arxiv.org/abs/2102.06959.
The aforementioned paper benchmarks the implementation against several
counterparts, including libc++'s (which is identical to the current
implementation). The results, shown in Figure 3, indicate the new algorithm is
1.7 times faster than the current one.
The patch adds a test which loops through all dates in [-32767/01/01,
32767/12/31], and for each of them, gets the number of days and compares the
result against its expected value. The latter is calculated using a much
simpler and easy to understand algorithm but which is also much slower.
The dates used in the test covers the full range of possible values
[time.cal.year.members]. Despite its completeness the test runs in matter of
seconds.
libstdc++-v3/ChangeLog:
* include/std/chrono (year_month_day::_M_days_since_epoch):
New implementation.
* testsuite/std/time/year_month_day/4.cc: New test.
This patch reimplements std::chrono::year_month_day::_S_from_days() which
retrieves a date from the number of elapsed days since 1970/01/01. The new
implementation is based on Proposition 6.3 of Neri and Schneider, "Euclidean
Affine Functions and Applications to Calendar Algorithms" available at
https://arxiv.org/abs/2102.06959.
The aforementioned paper benchmarks the implementation against several
counterparts, including libc++'s (which is identical to the current
implementation). The results, shown in Figure 4, indicate the new algorithm is
2.2 times faster than the current one.
The patch adds a test which loops through all integers in [-12687428, 11248737],
and for each of them, gets the corresponding date and compares the result
against its expected value. The latter is calculated using a much simpler and
easy to understand algorithm but which is also much slower.
The interval used in the test covers the full range of values for which a
roundtrip must work [time.cal.ymd.members]. Despite its completeness the test
runs in a matter of seconds.
libstdc++-v3/ChangeLog:
* include/std/chrono (year_month_day::_S_from_days): New
implementation.
* testsuite/std/time/year_month_day/3.cc: New test.
The conversion function year_month_weekday::operator sys_days computes
the offset in days from the first weekday of the month with:
days{(index()-1)*7}
^~~~~~~~~~~~~ type 'unsigned'
We want the above to yield -7d when index() is 0u, but our 'days' alias
is based on long instead of int, so the conversion from unsigned to the
underlying type of 'days' instead yields a large positive value.
This patch fixes this by casting the result of index() to int so that
the initializer is sign-extended in the conversion to long.
The added testcase also verifies we do the right thing when index() == 5.
libstdc++-v3/ChangeLog:
PR libstdc++/96713
* include/std/chrono (year_month_weekday::operator sys_days):
Cast the result of index() to int so that the initializer for
days{} is sign-extended when it's converted to the underlying
type.
* testsuite/std/time/year_month_weekday/3.cc: New test.
This fixes the months-based addition for year_month when the
year_month's month component is 0.
libstdc++-v3/ChangeLog:
* include/std/chrono (year_month::operator+): Properly handle a
month value of 0 by casting the month value to int before
subtracting 1 from it so that the difference is sign-extended in
the subsequent addition.
* testsuite/std/time/year_month/1.cc: Test adding months to a
year_month whose month component is below or above the
normalized range of [1,12].