<chrono>
leap second support should allow for negative leap secondsSection: 30.11.8 [time.zone.leap] Status: C++20 Submitter: Asher Dunn Opened: 2019-12-16 Last modified: 2021-02-25
Priority: 3
View all other issues in [time.zone.leap].
View all issues with C++20 status.
Discussion:
class leap
(which is expected to be renamed by P1981R0 to
leap_second
) defined in 30.11.8 [time.zone.leap] should include support for both
positive leap seconds (23:59:60
added to UTC at a specified time) and negative leap seconds
(23:59:59
removed from UTC at a specified time). While only positive leap seconds have been
inserted to date, the definition of UTC allows for both.
[2020-01 Priority set to 3 after review on the reflector.]
Previous resolution [SUPERSEDED]:This wording is relative to N4842.
Modify 30.7.3.2 [time.clock.utc.members] as indicated:
template<class Duration> static sys_time<common_type_t<Duration, seconds>> to_sys(const utc_time<Duration>& u);-2- Returns: A
sys_time t
, such thatfrom_sys(t) == u
if such a mapping exists. Otherwiseu
represents atime_point
during a positive leap second insertion, the conversion counts that leap second as not inserted, and the last representable value ofsys_time
prior to the insertion of the leap second is returned.template<class Duration> static utc_time<common_type_t<Duration, seconds>> from_sys(const sys_time<Duration>& t);-3- Returns: A
[…]utc_time u
, such thatu.time_since_epoch() - t.time_since_epoch()
is equal to thenumbersum of leap seconds that were inserted betweent
and 1970-01-01. Ift
is exactly the date of leap second insertion, then the conversion counts that leap second as inserted.Modify 30.7.3.3 [time.clock.utc.nonmembers] as indicated:
template<class Duration> leap_second_info get_leap_second_info(const utc_time<Duration>& ut);-6- Returns: A
leap_second_info
whereis_leap_second
istrue
ifut
is during a positive leap second insertion, and otherwisefalse
.elapsed
is thenumbersum of leap seconds between 1970-01-01 andut
. Ifis_leap_second
istrue
, the leap second referred to byut
is included in the count.Modify 30.7.4.1 [time.clock.tai.overview] as indicated:
-1- The clock
tai_clock
measures seconds since 1958-01-01 00:00:00 and is offset 10s ahead of UTC at this date. That is, 1958-01-01 00:00:00 TAI is equivalent to 1957-12-31 23:59:50 UTC. Leap seconds are not inserted into TAI. Therefore every time a leap second is inserted into UTC, UTCfalls another second behindshifts another second with respect to TAI. For example by 2000-01-01 there had been 22 positive and 0 negative leap seconds inserted so 2000-01-01 00:00:00 UTC is equivalent to 2000-01-01 00:00:32 TAI (22s plus the initial 10s offset).Modify 30.7.5.1 [time.clock.gps.overview] as indicated:
-1- The clock
gps_clock
measures seconds since the first Sunday of January, 1980 00:00:00 UTC. Leap seconds are not inserted into GPS. Therefore every time a leap second is inserted into UTC, UTCfalls another second behindshifts another second with respect to GPS. Aside from the offset from1958y/January/1
to1980y/January/Sunday[1]
, GPS is behind TAI by 19s due to the 10s offset between 1958 and 1970 and the additional 9 leap seconds inserted between 1970 and 1980.Modify 30.11.8.1 [time.zone.leap.overview] as indicated:
namespace std::chrono { class leap { public: leap(const leap&) = default; leap& operator=(const leap&) = default; // unspecified additional constructors constexpr sys_seconds date() const noexcept; constexpr seconds value() const noexcept; }; }-1- Objects of type
-2- [Example:leap
representing the date and value of the leap second insertions are constructed and stored in the time zone database when initialized.for (auto& l : get_tzdb().leaps) if (l <= 2018y/March/17d) cout << l.date() << ": " << l.value() << '\n';Produces the output:
1972-07-01 00:00:00: 1s 1973-01-01 00:00:00: 1s 1974-01-01 00:00:00: 1s 1975-01-01 00:00:00: 1s 1976-01-01 00:00:00: 1s 1977-01-01 00:00:00: 1s 1978-01-01 00:00:00: 1s 1979-01-01 00:00:00: 1s 1980-01-01 00:00:00: 1s 1981-07-01 00:00:00: 1s 1982-07-01 00:00:00: 1s 1983-07-01 00:00:00: 1s 1985-07-01 00:00:00: 1s 1988-01-01 00:00:00: 1s 1990-01-01 00:00:00: 1s 1991-01-01 00:00:00: 1s 1992-07-01 00:00:00: 1s 1993-07-01 00:00:00: 1s 1994-07-01 00:00:00: 1s 1996-01-01 00:00:00: 1s 1997-07-01 00:00:00: 1s 1999-01-01 00:00:00: 1s 2006-01-01 00:00:00: 1s 2009-01-01 00:00:00: 1s 2012-07-01 00:00:00: 1s 2015-07-01 00:00:00: 1s 2017-01-01 00:00:00: 1s— end example]
Modify 30.11.8.2 [time.zone.leap.members] as indicated:
constexpr sys_seconds date() const noexcept;-1- Returns: The date and time at which the leap second was inserted.
constexpr seconds value() const noexcept;-?- Returns: The value of the leap second. Always
+1s
to indicate a positive leap second or-1s
to indicate a negative leap second. All leap seconds inserted up through 2017 were positive leap seconds.Modify 30.12 [time.format] as indicated:
template<class Duration, class charT> struct formatter<chrono::utc_time<Duration>, charT>;-7- Remarks: If
%Z
is used, it is replaced withSTATICALLY-WIDEN<charT>("UTC")
. If%z
(or a modified variant of%z
) is used, an offset of0min
is formatted. If the argument represents a time during a positive leap second insertion, and if a seconds field is formatted, the integral portion of that format isSTATICALLY-WIDEN<charT>("60")
.
[2020-02-14; Prague]
LWG Review. Some wording improvements have been made and lead to revised wording.
Proposed resolution:
This wording is relative to N4849.
Modify 30.7.3.2 [time.clock.utc.members] as indicated:
template<class Duration> static sys_time<common_type_t<Duration, seconds>> to_sys(const utc_time<Duration>& u);-2- Returns: A
sys_time t
, such thatfrom_sys(t) == u
if such a mapping exists. Otherwiseu
represents atime_point
during a positive leap second insertion, the conversion counts that leap second as not inserted, and the last representable value ofsys_time
prior to the insertion of the leap second is returned.template<class Duration> static utc_time<common_type_t<Duration, seconds>> from_sys(const sys_time<Duration>& t);-3- Returns: A
[…]utc_time u
, such thatu.time_since_epoch() - t.time_since_epoch()
is equal to thenumbersum of leap seconds that were inserted betweent
and 1970-01-01. Ift
is exactly the date of leap second insertion, then the conversion counts that leap second as inserted.
Modify 30.7.3.3 [time.clock.utc.nonmembers] as indicated:
template<class Duration> leap_second_info get_leap_second_info(const utc_time<Duration>& ut);-6- Returns: A
leap_second_info
,lsi
, wherelsi.is_leap_second
istrue
ifut
is during a positive leap second insertion, and otherwisefalse
.lsi.elapsed
is thenumbersum of leap seconds between 1970-01-01 andut
. Iflsi.is_leap_second
istrue
, the leap second referred to byut
is included in thecountsum.
Modify 30.7.4.1 [time.clock.tai.overview] as indicated:
-1- The clock
tai_clock
measures seconds since 1958-01-01 00:00:00 and is offset 10s ahead of UTC at this date. That is, 1958-01-01 00:00:00 TAI is equivalent to 1957-12-31 23:59:50 UTC. Leap seconds are not inserted into TAI. Therefore every time a leap second is inserted into UTC, UTCfalls another second behindshifts another second with respect to TAI. For example by 2000-01-01 there had been 22 positive and 0 negative leap seconds inserted so 2000-01-01 00:00:00 UTC is equivalent to 2000-01-01 00:00:32 TAI (22s plus the initial 10s offset).
Modify 30.7.5.1 [time.clock.gps.overview] as indicated:
-1- The clock
gps_clock
measures seconds since the first Sunday of January, 1980 00:00:00 UTC. Leap seconds are not inserted into GPS. Therefore every time a leap second is inserted into UTC, UTCfalls another second behindshifts another second with respect to GPS. Aside from the offset from1958y/January/1
to1980y/January/Sunday[1]
, GPS is behind TAI by 19s due to the 10s offset between 1958 and 1970 and the additional 9 leap seconds inserted between 1970 and 1980.
Modify 30.11.8.1 [time.zone.leap.overview] as indicated:
namespace std::chrono { class leap { public: leap(const leap&) = default; leap& operator=(const leap&) = default; // unspecified additional constructors constexpr sys_seconds date() const noexcept; constexpr seconds value() const noexcept; }; }-1- Objects of type
-2- [Example:leap
representing the date and value of the leap second insertions are constructed and stored in the time zone database when initialized.for (auto& l : get_tzdb().leaps) if (l <= 2018y/March/17d) cout << l.date() << ": " << l.value() << '\n';Produces the output:
1972-07-01 00:00:00: 1s 1973-01-01 00:00:00: 1s 1974-01-01 00:00:00: 1s 1975-01-01 00:00:00: 1s 1976-01-01 00:00:00: 1s 1977-01-01 00:00:00: 1s 1978-01-01 00:00:00: 1s 1979-01-01 00:00:00: 1s 1980-01-01 00:00:00: 1s 1981-07-01 00:00:00: 1s 1982-07-01 00:00:00: 1s 1983-07-01 00:00:00: 1s 1985-07-01 00:00:00: 1s 1988-01-01 00:00:00: 1s 1990-01-01 00:00:00: 1s 1991-01-01 00:00:00: 1s 1992-07-01 00:00:00: 1s 1993-07-01 00:00:00: 1s 1994-07-01 00:00:00: 1s 1996-01-01 00:00:00: 1s 1997-07-01 00:00:00: 1s 1999-01-01 00:00:00: 1s 2006-01-01 00:00:00: 1s 2009-01-01 00:00:00: 1s 2012-07-01 00:00:00: 1s 2015-07-01 00:00:00: 1s 2017-01-01 00:00:00: 1s— end example]
Modify 30.11.8.2 [time.zone.leap.members] as indicated:
constexpr sys_seconds date() const noexcept;-1- Returns: The date and time at which the leap second was inserted.
constexpr seconds value() const noexcept;-?- Returns:
+1s
to indicate a positive leap second or-1s
to indicate a negative leap second. [Note: All leap seconds inserted up through 2019 were positive leap seconds. — end note]
Modify 30.12 [time.format] as indicated:
template<class Duration, class charT> struct formatter<chrono::utc_time<Duration>, charT>;-7- Remarks: If
%Z
is used, it is replaced withSTATICALLY-WIDEN<charT>("UTC")
. If%z
(or a modified variant of%z
) is used, an offset of0min
is formatted. If the argument represents a time during a positive leap second insertion, and if a seconds field is formatted, the integral portion of that format isSTATICALLY-WIDEN<charT>("60")
.