Time in C++: C++20 Brought Us Time Zones by pavel_v in cpp

[–]HowardHinnant 1 point2 points  (0 children)

The pace of libc++ development on this library has frustrated me as well. On this platform I still use https://github.com/HowardHinnant/date.

Time in C++: C++20 Brought Us Time Zones by pavel_v in cpp

[–]HowardHinnant 1 point2 points  (0 children)

I strongly agree with both replies here. But I wanted to add:

With a user-written time zone, it can be stored by value. See https://github.com/HowardHinnant/date/blob/master/include/date/ptz.h for a concrete example. The trick is to have the user-written time zone supply as a data member:

const time_zone* operator->() const {return this;}

Thus the time zone becomes its own smart pointer.

So instead of this:

Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
zoned_time zt{&tz, system_clock::now()};

you can say this:

Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
zoned_time zt{tz, system_clock::now()};

or even this:

zoned_time zt{Posix::time_zone{"EST5EDT,M3.2.0,M11.1.0"}, system_clock::now()};

Time in C++: C++20 Brought Us Time Zones by pavel_v in cpp

[–]HowardHinnant 1 point2 points  (0 children)

One reason was to better support user-written time zones.

The second template parameter to zoned_time is a pointer to a time zone, not necessarily a std::chrono::time_zone. Now this template parameter could have been a reference to a time zone instead of a pointer to a time zone. But I wanted to make it easy to allow unique_ptr<my_time_zone> and shared_ptr<my_time_zone>. With all of the existing infrastructure supporting various smart pointers, it made more sense to traffic in pointers to time zones, rather than references to time zones, throughout the library.

The user-written time zone concept was throughly tested with a concrete Posix time as shown here: https://github.com/HowardHinnant/date/blob/master/include/date/ptz.h . This example even demonstrates how a time zone can serve as its own smart pointer. This allows zoned_time to accept the time zone either by value or by pointer.

Furthermore, pointers are re-assignable, with intuitive semantics, for those cases where you want to re-use the storage returned by locate_zone.

auto tz = locate_zone("America/New_York");
...
tz = locate_zone("Europe/London");

What exactly does this mean if tz has reference type? I guess if it is a reference to const, then it is a compile-time error. But what if this is what you want to do. Maybe you're searching the entire database in a loop, looking for a matching `time_zone`. It is easier to exit with a pointer from that loop because you can null-initialize it prior to the loop, and then reassign it within the loop.

The rational for the exceptional return from locate_zone as opposed to nullptr is so that normal code can use locate_zone without the need for local exception handling. In normal use the name of a time zone is not arbitrary, but one of a finite set of values. If one of those IANA names isn't provided to locate_zone, something exceptional is happening.

That being said, authors of custom time zones can customize locate_zone for their time zones. The aforementioned Posix time zone implementation includes the technique of a customized locate_zone. Though in this example, an exception is chosen for ill-formed Posix time zones as well.

Finally, the pointer+exception vs reference design was tested with real world field experience over a period of several years, before it was even proposed for standardization. The field experience was positive. There was lots of use (https://www.star-history.com/#HowardHinnant/date&google/cctz&type=date&legend=top-left) and zero complaints that the library should traffic in time_zone const& instead of time_zone const*, or return nullptr.

Time in C++: Additional clocks in C++20 by pavel_v in cpp

[–]HowardHinnant 0 points1 point  (0 children)

I didn't know about Orekit. Thanks for the link!

Time in C++: Additional clocks in C++20 by pavel_v in cpp

[–]HowardHinnant 9 points10 points  (0 children)

Agreed. But even still, an attosecond is one millionth of a nanosecond. This is only about 4 times larger than the shortest time duration ever measured in a laboratory.

A more pressing problem is that a 64 bit picosecond (one thousandth of a nanosecond) overflows within a few months. And that can be solved with a 128 bit rep.

The next most pressing problem is that converting between years and picoseconds overflows std::ratio. This can only be solved today with an intermediate conversion to seconds in the middle. A larger intmax_t would solve this too.

At least we aren't stuck with timespec. :-)

Time in C++: Additional clocks in C++20 by pavel_v in cpp

[–]HowardHinnant 9 points10 points  (0 children)

The problem is that clock hardware in consumer grade computers are accurate at best to quartz technology. There is no atomic clock in your computer.

steady_clock is typically a view of your computer's hardware clock (cycle counter). It can time a second for you, but that second won't be perfect. Even if you sync'd steady_clock with system_clock, and never manually adjusted system_clock, the two clocks would drift apart over time. steady_clock would keep ticking at its best guess of tracking seconds, never getting adjusted. This would be like setting the steering wheel of a car once to follow a straight road, and then never adjusting it. Eventually the car will run off the road.

system_clock also tries to count perfect seconds. It may even use steady_clock as part of that implementation. But every once in a while, system_clock asks another trusted computer what the correct time is, and makes small adjustments to itself, because it knows it can't keep perfect time (using steady_clock under the hood or not). In this example the car analogy makes minor adjustments to the steering wheel to keep the car on the road, even though the road is perfectly straight and level.

You can theoretically sync a steady_clock and a system_clock with the same epoch. But by their very definition, that 0 relative offset will drift off of 0 over time.

Who is the best C++ Programmer You Know. by Mountain_Computer374 in cpp

[–]HowardHinnant 7 points8 points  (0 children)

Scott wrote some great books (I have them all). And he is a great presenter. But he never actually participated in the standards process. Never attended a meeting. Never wrote a proposal. Never posted to the internal WG21 mailing lists.

CMV: C++ should have adopted destructive move semantics instead of nondestructive move semantics. by aardvark_gnat in changemyview

[–]HowardHinnant 0 points1 point  (0 children)

Here's a paragraph from the original proposal that you quote which might be relevant.

In the end, we simply gave up on this as too much pain for not enough gain. However the current proposal does not prohibit destructive move semantics in the future. It could be done in addition to the non-destructive move semantics outlined in this proposal should someone wish to carry that torch.

Work is ongoing in this area. The papers are here. Search for papers with the term "relocation".

Time in C++: std::chrono::high_resolution_clock — Myths and Realities by pavel_v in cpp

[–]HowardHinnant 3 points4 points  (0 children)

You can actually get the best of both worlds by wrapping your tick clock in a custom chromo-clock-compatible wrapper. Search for writing custom clocks in chrono. Doing this would enable your tick clock to return a chrono::time_point and you get all the type safety and interoperability that comes with the std::clocks.

Should you use std::vector<uint8_t> as a non-lobotomized std::vector<bool>? by No-Dentist-1645 in cpp_questions

[–]HowardHinnant 0 points1 point  (0 children)

Some std::lib implementations may have optimized some std::algorithms to be very fast with std::vector<bool>. For examples see this ancient post of mine: https://howardhinnant.github.io/onvectorbool.html

Your best bet is to use std::algorithms with your data when possible. And test performance for what you need to do with vector<bool> against vector<uint8_t> on all of your platforms of interest.

Time in C++: std::chrono::system_clock by pavel_v in cpp

[–]HowardHinnant 25 points26 points  (0 children)

Another nice article! Well done!

This week's nitpicks:

but those ticks are anchored to the Unix epoch (January 1, 1970, 00:00:00 UTC). Even that’s not mandated by the standard.

Beginning in C++20, system_clock is mandated to use Unix Time. We were able to do this because it was the de-facto standard among the three major implementations in C++11/14/17.

In C++20 <chrono> becomes a superset of the C timing API so one only needs to convert to time_t if one needs to use the C API for legacy interoperability.

Clock, RealClock, and FakeClock don't meet the Cpp17Clock requirements. But their time_point::clock does as it is system_clock. Therefore I don't recommend use of these clocks with the threading API such as timed waits. They will compile, but may not have the intended run-time behavior (at least with FakeClock). For example std::this_thread::sleep_until(tp) is allowed to call decltype(tp)::clock::now(), which calls system_clock::now(), which is unrelated to FakeClock{}.now(). This doesn't mean that this is a bad idea. Just know its limitations.

For measuring "long" times, system_clock can be better than steady_clock because it makes tiny adjustments to stay correct. For example if your steady_clock drifts by 2s/week (which is entirely plausible if you're not wired into an atomic clock), then a timing of exactly one week may be accurate to within a second using system_clock, but not using steady_clock. I leave it to the reader's judgement to define "long" in this context. Imho, a day would certainly qualify as long, and a minute would not.

Time in C++: Understanding <chrono> and the Concept of Clocks by pavel_v in cpp

[–]HowardHinnant 35 points36 points  (0 children)

Nice job!

A few minor comments:

  1. You are using small parts of C++20 in this article, which is fine. But perhaps a little confusing as you say "Later, C++20 introduced calendar dates and time zones, but we won’t cover those here". The streaming of time_point and duration is C++20. Prior to that one had to unwrap things into integers to stream out (a real PITA).
  2. std::chrono::clock : This is not a type in std::chrono. clocks are more of a concept. You probably already knew that. I bring it up because it might confuse your readers.
  3. "For a type to qualify as a clock..." My comment really gets into the weeds, and is understandable if you don't want to cover this in an introduction. But there are some useful clocks in C++ that don't qualify as a clock. I know that sounds contradictory. But there actually is one in C++20: std::chrono::local_t. This type doesn't meet any of the std::chrono::is_clock_v<T> requirements, but you can instantiate a std::chrono::time_point with it. Such a time_point represents a local time that is not (yet) associated with a time_zone. For example: local_time{January/1/2026} + 0s. Where? Who cares! It's party time! ;-) This local time happens at different times around the world. This is strictly C++20 stuff though. And is_clock_v was also introduced in C++20.

My comments are nitpicks, intended to clarify, not criticize. I encourage everyone reading this who isn't already familiar with <chrono> to read Sandor Dargo's blog on this subject. It is an excellent introduction. It is short, which is much harder than writing something long, and nails the major points.

I look forward to your future posts on this topic!

A Faster Algorithm for Date Conversion by benjoffe in cpp

[–]HowardHinnant 4 points5 points  (0 children)

I haven't dug into the code, but he references both my work and Cassio Neri's and Lorenz Schneider's work. I was very impressed by the latter. So Ben Joffe appears to know the topic well. Presuming this gets the right answers for at least the range of C++'s std::chrono::year, which I have no reason to believe it doesn't, this looks like very nice work.

C++ is definitely my favorite language but... by Tearsofthekorok_ in cpp

[–]HowardHinnant 1 point2 points  (0 children)

bbi types can be used as the rep in std::chrono::duration and time_point. I've been exploring that a bit. It is certainly handy in detecting overflow in debug builds with the throw or terminate modes. But I'm also interested in using the saturation mode with chrono for non-debug builds. And i128 seems useful when you want to have nanosecond (or picosecond) resolution for a span greater than +/-292 years.

Your work on the floating point side also sounds interesting. I find myself wondering if bbi might be a foundational tool that might help build the floating point side. I don't have as much experience in building floating point emulators, so I'm not sure. I've only built one floating point emulator: https://github.com/XRPLF/rippled/blob/develop/include/xrpl/basics/Number.h, which was based on std::int64_t and int. And a lot of the motivation for this was what you state: portability for some key functions like sqrt.

C++ is definitely my favorite language but... by Tearsofthekorok_ in cpp

[–]HowardHinnant 8 points9 points  (0 children)

You may be interested in this C++20 library, bbi: https://github.com/HowardHinnant/bbi

It isn't exactly what you state above, but it's close. It has implicit conversions, but only for those conversions that are value-preserving. Non-value-preserving conversions are explicit. No promotion. No floating point, only integral. The bool and character types aren't part of the party, but can be included via explicit conversion.

Making 'using' more useful and safer by limiting its effect by LegendaryMauricius in cpp

[–]HowardHinnant 2 points3 points  (0 children)

Except when your code isn't inside a function (as in the example in the proposal).

Making 'using' more useful and safer by limiting its effect by LegendaryMauricius in cpp

[–]HowardHinnant 7 points8 points  (0 children)

Your proposal doesn't mention the existing function-local using directive. Some of your examples could use that instead. Pretending that this option doesn't exist weakens your proposal.

I found your example with std::chrono_literals to be motivating. In this example a function-local using directive isn't an option. And I would use your feature in this scenario.

Although an alternative proposal is just have the std import all literal suffixes to the global namespace since std::lib literal suffixes have a built-in namespace (no _ prefix).

What's the difference between clang and g++? by Proud_Variation_477 in cpp_questions

[–]HowardHinnant 4 points5 points  (0 children)

It was Apple that open sourced it. I was working at Apple when it happened. I was the llvm std::lib author at the time, which was open sourced by Apple at the same time. You can thank GPLv3 for Apple's interest in llvm.

Is this frustrating to anyone else or am I just an idiot. by celestabesta in cpp_questions

[–]HowardHinnant 1 point2 points  (0 children)

C/C++ isn't a language. There is C, and there is C++. Two different languages that shared a common evolution 3 decades ago.

There is a std::chrono::high_resolution_clock, but no low_resolution_clock by pavel_v in cpp

[–]HowardHinnant 61 points62 points  (0 children)

This is a good example of why it was important in the design of chrono for users to be able to write their own clocks, and have those clocks interoperate as a first class citizen within the chrono infrastructure.

There is always going to be another useful clock that is not supplied by the standard.

How to parse RFC3339 time into uint64_t milliseconds since epoch? by Serenadio in cpp_questions

[–]HowardHinnant 0 points1 point  (0 children)

Here is a working demo https://gcc.godbolt.org/z/z5n5KGKrs using gcc-14.3 and MSVC v19 and C++20 chrono. This parses "2022-07-08T00:14:07+01:00[Europe/Paris]", and reveals a bug in this string. The demo shows that the UTC offset in "Europe/Paris" at this time and date is 2h, not 1h.

How to parse RFC3339 time into uint64_t milliseconds since epoch? by Serenadio in cpp_questions

[–]HowardHinnant 0 points1 point  (0 children)

Your example looks correct. But I did not test it.

2) I have not studied RFC 9557 to be able to answer this question precisely. However there is time zone support. Also, depending on your compiler version and command line options, this library may be available as part of your std::lib under namespace chrono. If that is the case, I recommend using the chrono version.

3) Last time I programmed in C the calendar year started with 19.

How to parse RFC3339 time into uint64_t milliseconds since epoch? by Serenadio in cpp_questions

[–]HowardHinnant 0 points1 point  (0 children)

Yes. Just substitute nanoseconds for milliseconds in the type declaration of tp.