From fe231dc6927f64da91b362984c035071a8d89486 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Oct 2026 12:07:32 +0100 Subject: [PATCH] docs: write std::chrono durations as unit/value objects; reword a title with escaped quotes (#5911) Signed-off-by: Niels Lohmann --- .../docs/examples/type_recipes_chrono.cpp | 49 +++++++++++++++++-- .../docs/examples/type_recipes_chrono.output | 5 +- docs/mkdocs/docs/features/type_recipes.md | 8 +-- docs/mkdocs/docs/home/exceptions.md | 2 +- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/docs/mkdocs/docs/examples/type_recipes_chrono.cpp b/docs/mkdocs/docs/examples/type_recipes_chrono.cpp index 2db928ad9..a3bd357ff 100644 --- a/docs/mkdocs/docs/examples/type_recipes_chrono.cpp +++ b/docs/mkdocs/docs/examples/type_recipes_chrono.cpp @@ -1,9 +1,38 @@ #include #include +#include +#include #include using json = nlohmann::json; +// the name of a period; extend the list for the periods you use +template const char* unit_name(); +template <> const char* unit_name() +{ + return "ns"; +} +template <> const char* unit_name() +{ + return "us"; +} +template <> const char* unit_name() +{ + return "ms"; +} +template <> const char* unit_name>() +{ + return "s"; +} +template <> const char* unit_name>() +{ + return "min"; +} +template <> const char* unit_name>() +{ + return "h"; +} + namespace nlohmann { template @@ -11,12 +40,16 @@ struct adl_serializer> { static void to_json(json& j, const std::chrono::duration& d) { - j = d.count(); + j = {{"unit", unit_name()}, {"value", d.count()}}; } static void from_json(const json& j, std::chrono::duration& d) { - d = std::chrono::duration(j.get()); + if (j.at("unit").get() != unit_name()) + { + throw std::invalid_argument("unexpected unit " + j.at("unit").dump()); + } + d = std::chrono::duration(j.at("value").get()); } }; @@ -41,11 +74,21 @@ int main() std::cout << std::boolalpha; - // the unit is not part of the JSON value + // the unit is part of the JSON value const json timeout = milliseconds(1500); std::cout << "duration: " << timeout << '\n'; std::cout << "as milliseconds: " << timeout.get().count() << '\n'; + // reading with a different unit is rejected + try + { + timeout.get(); + } + catch (const std::invalid_argument& e) + { + std::cout << "error: " << e.what() << '\n'; + } + // a system_clock time point is the time since the Unix epoch const time_point t(milliseconds(1700000000123)); const json stamp = t; diff --git a/docs/mkdocs/docs/examples/type_recipes_chrono.output b/docs/mkdocs/docs/examples/type_recipes_chrono.output index 59bbd9340..6b40a2bc2 100644 --- a/docs/mkdocs/docs/examples/type_recipes_chrono.output +++ b/docs/mkdocs/docs/examples/type_recipes_chrono.output @@ -1,4 +1,5 @@ -duration: 1500 +duration: {"unit":"ms","value":1500} as milliseconds: 1500 -time point: 1700000000123 +error: unexpected unit "ms" +time point: {"unit":"ms","value":1700000000123} round trip: true diff --git a/docs/mkdocs/docs/features/type_recipes.md b/docs/mkdocs/docs/features/type_recipes.md index 3bd1ec7da..314425dc6 100644 --- a/docs/mkdocs/docs/features/type_recipes.md +++ b/docs/mkdocs/docs/features/type_recipes.md @@ -92,8 +92,10 @@ to choose the alternative. ## `std::chrono` -A duration is written as its count and read back with the same representation and period. The **unit is not part of -the JSON value**, so document it, for example in the member name (`timeout_ms`): +A duration is written as an object with the keys `unit` and `value`, for example `{"unit": "ms", "value": 1500}`, so +the JSON value is self-describing. The serializer names the unit of the period (`ns`, `us`, `ms`, `s`, `min`, `h`; +extend the list for other periods), and the deserializer rejects a value whose unit differs from the requested +duration type instead of silently misreading it: ??? example "Example: serialize durations and system_clock time points" @@ -107,7 +109,7 @@ the JSON value**, so document it, for example in the member name (`timeout_ms`): --8<-- "examples/type_recipes_chrono.output" ``` -A `system_clock::time_point` with millisecond precision is written as the number of milliseconds since the Unix epoch. +A `system_clock::time_point` with millisecond precision is written as the duration since the Unix epoch, that is, as an object with `unit` and `value`. The time point serializer is limited to `system_clock`, because the epoch of other clocks (for example `steady_clock`) has no meaning outside the running program. diff --git a/docs/mkdocs/docs/home/exceptions.md b/docs/mkdocs/docs/home/exceptions.md index 759b32b4e..4b8f6edd9 100644 --- a/docs/mkdocs/docs/home/exceptions.md +++ b/docs/mkdocs/docs/home/exceptions.md @@ -195,7 +195,7 @@ This error indicates a syntax error while deserializing a JSON text. The error m - Make sure the input is correctly read. Try to write the input to standard output to check if, for instance, the input file was successfully opened. - Paste the input to a JSON validator like or a tool like [jq](https://jqlang.github.io/jq/). -??? info "Parsing a file fails at line 1, column 1 (or \"unexpected end of input\")" +??? info "Parsing a file fails at line 1, column 1 (unexpected end of input)" The messages `attempting to parse an empty input` (since version 3.11.3) and `unexpected end of input` (also the message of earlier versions for an empty input) at the very start of the input mean