On Thu, 9 Oct 2025, Tomasz Kamiński wrote:

> This covers:
>  * weekday_indexed, weekday_last
>  * month_day, month_day_last,
>  * month_weekday, month_weekday_last
>  * year_month
> 
> libstdc++-v3/ChangeLog:
> 
>       * testsuite/std/time/month_day/io.cc: New formatting tests.
>       * testsuite/std/time/month_day_last/io.cc: Likewise.
>       * testsuite/std/time/month_weekday/io.cc: Likewise.
>       * testsuite/std/time/month_weekday_last/io.cc: Likewise.
>       * testsuite/std/time/weekday_indexed/io.cc: Likewise.
>       * testsuite/std/time/weekday_last/io.cc: Likewise.
>       * testsuite/std/time/year_month/io.cc: Likewise.

LGTM

> 
> Signed-off-by: Tomasz Kamiński <[email protected]>
> ---
> Lightly tested on x86_64-linux. OK for trunk?
> 
>  .../testsuite/std/time/month_day/io.cc        | 41 ++++++++++++++++++-
>  .../testsuite/std/time/month_day_last/io.cc   | 41 ++++++++++++++++++-
>  .../testsuite/std/time/month_weekday/io.cc    | 41 ++++++++++++++++++-
>  .../std/time/month_weekday_last/io.cc         | 41 ++++++++++++++++++-
>  .../testsuite/std/time/weekday_indexed/io.cc  | 41 ++++++++++++++++++-
>  .../testsuite/std/time/weekday_last/io.cc     | 41 ++++++++++++++++++-
>  .../testsuite/std/time/year_month/io.cc       | 41 ++++++++++++++++++-
>  7 files changed, 280 insertions(+), 7 deletions(-)
> 
> diff --git a/libstdc++-v3/testsuite/std/time/month_day/io.cc 
> b/libstdc++-v3/testsuite/std/time/month_day/io.cc
> index 30aa5881356..c3ae180b32b 100644
> --- a/libstdc++-v3/testsuite/std/time/month_day/io.cc
> +++ b/libstdc++-v3/testsuite/std/time/month_day/io.cc
> @@ -22,6 +22,45 @@ test_ostream()
>    VERIFY( ss.str() == "juil./27" );
>  }
>  
> +void
> +test_format()
> +{
> +  using namespace std::chrono;
> +  std::locale loc_fr(ISO_8859(15,fr_FR));
> +
> +  auto s = std::format("{:%b%%%B%t%m%n %d%%%e}", month(1)/day(3));
> +  VERIFY( s == "Jan%January\t01\n 03% 3" );
> +  s = std::format(loc_fr, "{:L%b%%%B%t%m%n %d%%%e}", month(1)/day(3));
> +  VERIFY( s == "janv.%janvier\t01\n 03% 3");
> +
> +  s = std::format("{0:%m/%d} {0}", month(10)/day(13));
> +  VERIFY( s == "10/13 Oct/13" );
> +  s = std::format("{0:%m/%d} {0}", month(13)/day(34));
> +  VERIFY( s == "13/34 13 is not a valid month/34 is not a valid day" );
> +
> +  std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
> +  std::string_view my_specs = "bBdehm";
> +  for (char c : specs)
> +  {
> +    char fmt[] = { '{', ':', '%', c, '}' };
> +    try
> +    {
> +      auto md = month(1)/day(10);
> +      (void) std::vformat(std::string_view(fmt, 5), 
> std::make_format_args(md));
> +      // The call above should throw for any conversion-spec not in my_specs:
> +      VERIFY(my_specs.find(c) != my_specs.npos);
> +    }
> +    catch (const std::format_error& e)
> +    {
> +      VERIFY(my_specs.find(c) == my_specs.npos);
> +      std::string_view s = e.what();
> +      // Libstdc++-specific message:
> +      VERIFY(s.find("format argument does not contain the information "
> +                 "required by the chrono-specs") != s.npos);
> +    }
> +  }
> +}
> +
>  void
>  test_parse()
>  {
> @@ -102,6 +141,6 @@ test_parse()
>  int main()
>  {
>    test_ostream();
> -  // TODO: test_format();
> +  test_format();
>    test_parse();
>  }
> diff --git a/libstdc++-v3/testsuite/std/time/month_day_last/io.cc 
> b/libstdc++-v3/testsuite/std/time/month_day_last/io.cc
> index c12cee848ed..3fb973bfb0d 100644
> --- a/libstdc++-v3/testsuite/std/time/month_day_last/io.cc
> +++ b/libstdc++-v3/testsuite/std/time/month_day_last/io.cc
> @@ -22,9 +22,48 @@ test_ostream()
>    VERIFY( ss.str() == "juil./last" );
>  }
>  
> +void
> +test_format()
> +{
> +  using namespace std::chrono;
> +  std::locale loc_fr(ISO_8859(15,fr_FR));
> +
> +  auto s = std::format("{:%b%%%B%t%m%n}", month(3)/last);
> +  VERIFY( s == "Mar%March\t03\n" );
> +  s = std::format(loc_fr, "{:L%b%%%B%t%m%n}", month(3)/last);
> +  VERIFY( s == "mars%mars\t03\n");
> +
> +  s = std::format("{0:%m/last} {0}", month(4)/last);
> +  VERIFY( s == "04/last Apr/last" );
> +  s = std::format("{0:%m/last} {0}", month(0)/last);
> +  VERIFY( s == "00/last 0 is not a valid month/last" );
> +
> +  std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
> +  std::string_view my_specs = "bBhm";
> +  for (char c : specs)
> +  {
> +    char fmt[] = { '{', ':', '%', c, '}' };
> +    try
> +    {
> +      auto mdl = month(1)/last;
> +      (void) std::vformat(std::string_view(fmt, 5), 
> std::make_format_args(mdl));
> +      // The call above should throw for any conversion-spec not in my_specs:
> +      VERIFY(my_specs.find(c) != my_specs.npos);
> +    }
> +    catch (const std::format_error& e)
> +    {
> +      VERIFY(my_specs.find(c) == my_specs.npos);
> +      std::string_view s = e.what();
> +      // Libstdc++-specific message:
> +      VERIFY(s.find("format argument does not contain the information "
> +                 "required by the chrono-specs") != s.npos);
> +    }
> +  }
> +}
> +
>  int main()
>  {
>    test_ostream();
> -  // TODO: test_format();
> +  test_format();
>    // TODO: test_parse();
>  }
> diff --git a/libstdc++-v3/testsuite/std/time/month_weekday/io.cc 
> b/libstdc++-v3/testsuite/std/time/month_weekday/io.cc
> index 82cac648905..1bb6f4d9337 100644
> --- a/libstdc++-v3/testsuite/std/time/month_weekday/io.cc
> +++ b/libstdc++-v3/testsuite/std/time/month_weekday/io.cc
> @@ -23,9 +23,48 @@ test_ostream()
>    VERIFY( ss.str() == "juil./jeu.[4]" );
>  }
>  
> +void
> +test_format()
> +{
> +  using namespace std::chrono;
> +  std::locale loc_fr(ISO_8859(15,fr_FR));
> +
> +  auto s = std::format("{:%b%%%B%t%m%n %a%%%A%t%u%n%w}", 
> month(5)/weekday(1)[2]);
> +  VERIFY( s == "May%May\t05\n Mon%Monday\t1\n1" );
> +  s = std::format(loc_fr, "{:L%b%%%B%t%m%n %a%%%A%t%u%n%w}", 
> month(5)/weekday(1)[2]);
> +  VERIFY( s == "mai%mai\t05\n lun.%lundi\t1\n1");
> +
> +  s = std::format("{0:%m/%u[]} {0}", month(9)/weekday(0)[2]);
> +  VERIFY( s == "09/7[] Sep/Sun[2]" );
> +  s = std::format("{0:%m/%u[]} {0}", month(111)/weekday(8)[0]);
> +  VERIFY( s == "111/8[] 111 is not a valid month/8 is not a valid weekday[0 
> is not a valid index]" );
> +
> +  std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
> +  std::string_view my_specs = "aAbBhmuw";
> +  for (char c : specs)
> +  {
> +    char fmt[] = { '{', ':', '%', c, '}' };
> +    try
> +    {
> +      auto mwi = month(1)/weekday(1)[1];
> +      (void) std::vformat(std::string_view(fmt, 5), 
> std::make_format_args(mwi));
> +      // The call above should throw for any conversion-spec not in my_specs:
> +      VERIFY(my_specs.find(c) != my_specs.npos);
> +    }
> +    catch (const std::format_error& e)
> +    {
> +      VERIFY(my_specs.find(c) == my_specs.npos);
> +      std::string_view s = e.what();
> +      // Libstdc++-specific message:
> +      VERIFY(s.find("format argument does not contain the information "
> +                 "required by the chrono-specs") != s.npos);
> +    }
> +  }
> +}
> +
>  int main()
>  {
>    test_ostream();
> -  // TODO: test_format();
> +  test_format();
>    // TODO: test_parse();
>  }
> diff --git a/libstdc++-v3/testsuite/std/time/month_weekday_last/io.cc 
> b/libstdc++-v3/testsuite/std/time/month_weekday_last/io.cc
> index 47968d0e06d..ecbfc27110a 100644
> --- a/libstdc++-v3/testsuite/std/time/month_weekday_last/io.cc
> +++ b/libstdc++-v3/testsuite/std/time/month_weekday_last/io.cc
> @@ -23,9 +23,48 @@ test_ostream()
>    VERIFY( ss.str() == "juil./jeu.[last]" );
>  }
>  
> +void
> +test_format()
> +{
> +  using namespace std::chrono;
> +  std::locale loc_fr(ISO_8859(15,fr_FR));
> +
> +  auto s = std::format("{:%b%%%B%t%m%n %a%%%A%t%u%n%w}", 
> month(6)/weekday(2)[last]);
> +  VERIFY( s == "Jun%June\t06\n Tue%Tuesday\t2\n2" );
> +  s = std::format(loc_fr, "{:L%b%%%B%t%m%n %a%%%A%t%u%n%w}", 
> month(6)/weekday(2)[last]);
> +  VERIFY( s == "juin%juin\t06\n mar.%mardi\t2\n2");
> +
> +  s = std::format("{0:%m/%w[last]} {0}", month(8)/weekday(7)[last]);
> +  VERIFY( s == "08/0[last] Aug/Sun[last]" );
> +  s = std::format("{0:%m/%w[last]} {0}", month(70)/weekday(9)[last]);
> +  VERIFY( s == "70/9[last] 70 is not a valid month/9 is not a valid 
> weekday[last]" );
> +
> +  std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
> +  std::string_view my_specs = "aAbBhmuw";
> +  for (char c : specs)
> +  {
> +    char fmt[] = { '{', ':', '%', c, '}' };
> +    try
> +    {
> +      auto mwl = month(1)/weekday(1)[last];
> +      (void) std::vformat(std::string_view(fmt, 5), 
> std::make_format_args(mwl));
> +      // The call above should throw for any conversion-spec not in my_specs:
> +      VERIFY(my_specs.find(c) != my_specs.npos);
> +    }
> +    catch (const std::format_error& e)
> +    {
> +      VERIFY(my_specs.find(c) == my_specs.npos);
> +      std::string_view s = e.what();
> +      // Libstdc++-specific message:
> +      VERIFY(s.find("format argument does not contain the information "
> +                 "required by the chrono-specs") != s.npos);
> +    }
> +  }
> +}
> +
>  int main()
>  {
>    test_ostream();
> -  // TODO: test_format();
> +  test_format();
>    // TODO: test_parse();
>  }
> diff --git a/libstdc++-v3/testsuite/std/time/weekday_indexed/io.cc 
> b/libstdc++-v3/testsuite/std/time/weekday_indexed/io.cc
> index 29255ad5c06..1ad9ffd8253 100644
> --- a/libstdc++-v3/testsuite/std/time/weekday_indexed/io.cc
> +++ b/libstdc++-v3/testsuite/std/time/weekday_indexed/io.cc
> @@ -22,9 +22,48 @@ test_ostream()
>    VERIFY( ss.str() == "sam.[1]" );
>  }
>  
> +void
> +test_format()
> +{
> +  using namespace std::chrono;
> +  std::locale loc_fr(ISO_8859(15,fr_FR));
> +
> +  auto s = std::format("{:%a%%%A%t%u%n%w}", weekday(7)[3]);
> +  VERIFY( s == "Sun%Sunday\t7\n0" );
> +  s = std::format(loc_fr, "{:L%a%%%A%t%u%n%w}", weekday(7)[3]);
> +  VERIFY( s == "dim.%dimanche\t7\n0");
> +
> +  s = std::format("{0:%w[]} {0}", weekday(4)[4]);
> +  VERIFY( s == "4[] Thu[4]" );
> +  s = std::format("{0:%w[]} {0}", weekday(10)[7]);
> +  VERIFY( s == "10[] 10 is not a valid weekday[7 is not a valid index]" );
> +
> +  std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
> +  std::string_view my_specs = "aAuw";
> +  for (char c : specs)
> +  {
> +    char fmt[] = { '{', ':', '%', c, '}' };
> +    try
> +    {
> +      auto wi = weekday(1)[1];
> +      (void) std::vformat(std::string_view(fmt, 5), 
> std::make_format_args(wi));
> +      // The call above should throw for any conversion-spec not in my_specs:
> +      VERIFY(my_specs.find(c) != my_specs.npos);
> +    }
> +    catch (const std::format_error& e)
> +    {
> +      VERIFY(my_specs.find(c) == my_specs.npos);
> +      std::string_view s = e.what();
> +      // Libstdc++-specific message:
> +      VERIFY(s.find("format argument does not contain the information "
> +                 "required by the chrono-specs") != s.npos);
> +    }
> +  }
> +}
> +
>  int main()
>  {
>    test_ostream();
> -  // TODO: test_format();
> +  test_format();
>    // TODO: test_parse();
>  }
> diff --git a/libstdc++-v3/testsuite/std/time/weekday_last/io.cc 
> b/libstdc++-v3/testsuite/std/time/weekday_last/io.cc
> index 6f76922195d..095d5615f48 100644
> --- a/libstdc++-v3/testsuite/std/time/weekday_last/io.cc
> +++ b/libstdc++-v3/testsuite/std/time/weekday_last/io.cc
> @@ -22,9 +22,48 @@ test_ostream()
>    VERIFY( ss.str() == "sam.[last]" );
>  }
>  
> +void
> +test_format()
> +{
> +  using namespace std::chrono;
> +  std::locale loc_fr(ISO_8859(15,fr_FR));
> +
> +  auto s = std::format("{:%a%%%A%t%u%n%w}", weekday(5)[last]);
> +  VERIFY( s == "Fri%Friday\t5\n5" );
> +  s = std::format(loc_fr, "{:L%a%%%A%t%u%n%w}", weekday(5)[last]);
> +  VERIFY( s == "ven.%vendredi\t5\n5");
> +
> +  s = std::format("{0:%w[last]} {0}", weekday(6)[last]);
> +  VERIFY( s == "6[last] Sat[last]" );
> +  s = std::format("{0:%w[last]} {0}", weekday(9)[last]);
> +  VERIFY( s == "9[last] 9 is not a valid weekday[last]" );
> +
> +  std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
> +  std::string_view my_specs = "aAuw";
> +  for (char c : specs)
> +  {
> +    char fmt[] = { '{', ':', '%', c, '}' };
> +    try
> +    {
> +      auto wl = weekday(1)[last];
> +      (void) std::vformat(std::string_view(fmt, 5), 
> std::make_format_args(wl));
> +      // The call above should throw for any conversion-spec not in my_specs:
> +      VERIFY(my_specs.find(c) != my_specs.npos);
> +    }
> +    catch (const std::format_error& e)
> +    {
> +      VERIFY(my_specs.find(c) == my_specs.npos);
> +      std::string_view s = e.what();
> +      // Libstdc++-specific message:
> +      VERIFY(s.find("format argument does not contain the information "
> +                 "required by the chrono-specs") != s.npos);
> +    }
> +  }
> +}
> +
>  int main()
>  {
>    test_ostream();
> -  // TODO: test_format();
> +  test_format();
>    // TODO: test_parse();
>  }
> diff --git a/libstdc++-v3/testsuite/std/time/year_month/io.cc 
> b/libstdc++-v3/testsuite/std/time/year_month/io.cc
> index 7bb3442e299..3392eb334bf 100644
> --- a/libstdc++-v3/testsuite/std/time/year_month/io.cc
> +++ b/libstdc++-v3/testsuite/std/time/year_month/io.cc
> @@ -22,6 +22,45 @@ test_ostream()
>    VERIFY( ss.str() == "2023/juil." );
>  }
>  
> +void
> +test_format()
> +{
> +  using namespace std::chrono;
> +  std::locale loc_fr(ISO_8859(15,fr_FR));
> +
> +  auto s = std::format("{:%C%%%y\t%Y %b%%%B%t%m%n}", year(2019)/month(4));
> +  VERIFY( s == "20%19\t2019 Apr%April\t04\n" );
> +  s = std::format(loc_fr, "{:L%C%%%y\t%Y %b%%%B%t%m%n}", 
> year(2019)/month(4));
> +  VERIFY( s == "20%19\t2019 avril%avril\t04\n");
> +
> +  s = std::format("{0:%Y/%m} {0}", year(2018)/month(2));
> +  VERIFY( s == "2018/02 2018/Feb" );
> +  s = std::format("{0:%Y/%m} {0}", year(-32768)/month(15));
> +  VERIFY( s == "-32768/15 -32768 is not a valid year/15 is not a valid 
> month" );
> +
> +  std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ";
> +  std::string_view my_specs = "CbBhmyY";
> +  for (char c : specs)
> +  {
> +    char fmt[] = { '{', ':', '%', c, '}' };
> +    try
> +    {
> +      auto ym = year(2013)/month(1);
> +      (void) std::vformat(std::string_view(fmt, 5), 
> std::make_format_args(ym));
> +      // The call above should throw for any conversion-spec not in my_specs:
> +      VERIFY(my_specs.find(c) != my_specs.npos);
> +    }
> +    catch (const std::format_error& e)
> +    {
> +      VERIFY(my_specs.find(c) == my_specs.npos);
> +      std::string_view s = e.what();
> +      // Libstdc++-specific message:
> +      VERIFY(s.find("format argument does not contain the information "
> +                 "required by the chrono-specs") != s.npos);
> +    }
> +  }
> +}
> +
>  void
>  test_parse()
>  {
> @@ -73,6 +112,6 @@ test_parse()
>  int main()
>  {
>    test_ostream();
> -  // TODO: test_format();
> +  test_format();
>    test_parse();
>  }
> -- 
> 2.51.0
> 
> 

Reply via email to