Skip to content

Commit

Permalink
[libc++][TZDB] Adds local_info formatter. (llvm#86256)
Browse files Browse the repository at this point in the history
This adds the local_info type and its formatting options.
The usage of the local_info object will be done in separate patches.

Implements parts of:
- P0355 Extending to Calendars and Time Zones
- P1361 Integration of chrono with text formatting
  • Loading branch information
mordante committed Apr 18, 2024
1 parent af0b69f commit 8a21d59
Show file tree
Hide file tree
Showing 15 changed files with 467 additions and 2 deletions.
2 changes: 1 addition & 1 deletion libcxx/docs/Status/FormatPaper.csv
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_weekday_last``",,Mark de Wever,|Complete|,16.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::hh_mm_ss<duration<Rep, Period>>``",,Mark de Wever,|Complete|,17.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_info``",,Mark de Wever,|Complete|,19.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_info``",A ``<chrono>`` implementation,Mark de Wever,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_info``",,Mark de Wever,|Complete|,19.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",A ``<chrono>`` implementation,Mark de Wever,,

"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``"
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ set(files
__chrono/high_resolution_clock.h
__chrono/leap_second.h
__chrono/literals.h
__chrono/local_info.h
__chrono/month.h
__chrono/month_weekday.h
__chrono/monthday.h
Expand Down
3 changes: 3 additions & 0 deletions libcxx/include/__chrono/convert_to_tm.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <__chrono/duration.h>
#include <__chrono/file_clock.h>
#include <__chrono/hh_mm_ss.h>
#include <__chrono/local_info.h>
#include <__chrono/month.h>
#include <__chrono/month_weekday.h>
#include <__chrono/monthday.h>
Expand Down Expand Up @@ -175,6 +176,8 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
} else if constexpr (same_as<_ChronoT, chrono::sys_info>) {
// Has no time information.
} else if constexpr (same_as<_ChronoT, chrono::local_info>) {
// Has no time information.
# endif
} else
static_assert(sizeof(_ChronoT) == 0, "Add the missing type specialization");
Expand Down
20 changes: 20 additions & 0 deletions libcxx/include/__chrono/formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <__chrono/duration.h>
#include <__chrono/file_clock.h>
#include <__chrono/hh_mm_ss.h>
#include <__chrono/local_info.h>
#include <__chrono/month.h>
#include <__chrono/month_weekday.h>
#include <__chrono/monthday.h>
Expand Down Expand Up @@ -419,6 +420,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
else if constexpr (same_as<_Tp, chrono::sys_info>)
return true;
else if constexpr (same_as<_Tp, chrono::local_info>)
return true;
# endif
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
Expand Down Expand Up @@ -463,6 +466,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
else if constexpr (same_as<_Tp, chrono::sys_info>)
return true;
else if constexpr (same_as<_Tp, chrono::local_info>)
return true;
# endif
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
Expand Down Expand Up @@ -507,6 +512,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
else if constexpr (same_as<_Tp, chrono::sys_info>)
return true;
else if constexpr (same_as<_Tp, chrono::local_info>)
return true;
# endif
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
Expand Down Expand Up @@ -551,6 +558,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
else if constexpr (same_as<_Tp, chrono::sys_info>)
return true;
else if constexpr (same_as<_Tp, chrono::local_info>)
return true;
# endif
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
Expand Down Expand Up @@ -893,6 +902,17 @@ struct formatter<chrono::sys_info, _CharT> : public __formatter_chrono<_CharT> {
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time_zone);
}
};

template <__fmt_char_type _CharT>
struct formatter<chrono::local_info, _CharT> : public __formatter_chrono<_CharT> {
public:
using _Base = __formatter_chrono<_CharT>;

template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags{});
}
};
# endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)

#endif // if _LIBCPP_STD_VER >= 20
Expand Down
50 changes: 50 additions & 0 deletions libcxx/include/__chrono/local_info.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html

#ifndef _LIBCPP___CHRONO_LOCAL_INFO_H
#define _LIBCPP___CHRONO_LOCAL_INFO_H

#include <version>
// Enable the contents of the header only when libc++ was built with experimental features enabled.
#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)

# include <__chrono/sys_info.h>
# include <__config>

# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
# endif

_LIBCPP_BEGIN_NAMESPACE_STD

# if _LIBCPP_STD_VER >= 20

namespace chrono {

struct local_info {
static constexpr int unique = 0;
static constexpr int nonexistent = 1;
static constexpr int ambiguous = 2;

int result;
sys_info first;
sys_info second;
};

} // namespace chrono

# endif // _LIBCPP_STD_VER >= 20

_LIBCPP_END_NAMESPACE_STD

#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)

#endif // _LIBCPP___CHRONO_LOCAL_INFO_H
22 changes: 22 additions & 0 deletions libcxx/include/__chrono/ostream.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <__chrono/duration.h>
#include <__chrono/file_clock.h>
#include <__chrono/hh_mm_ss.h>
#include <__chrono/local_info.h>
#include <__chrono/month.h>
#include <__chrono/month_weekday.h>
#include <__chrono/monthday.h>
Expand Down Expand Up @@ -280,6 +281,27 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __info) {
__abbrev);
}

template <class _CharT, class _Traits>
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __os, const local_info& __info) {
auto __result = [&]() -> basic_string<_CharT> {
switch (__info.result) {
case local_info::unique:
return _LIBCPP_STATICALLY_WIDEN(_CharT, "unique");
case local_info::nonexistent:
return _LIBCPP_STATICALLY_WIDEN(_CharT, "non-existent");
case local_info::ambiguous:
return _LIBCPP_STATICALLY_WIDEN(_CharT, "ambiguous");

default:
return std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "unspecified result ({})"), __info.result);
};
};

return __os << std::format(
_LIBCPP_STATICALLY_WIDEN(_CharT, "{}: {{{}, {}}}"), __result(), __info.first, __info.second);
}

# endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)

} // namespace chrono
Expand Down
16 changes: 16 additions & 0 deletions libcxx/include/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,20 @@ template<class charT, class traits>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os, const sys_info& si);
struct local_info { // C++20
static constexpr int unique = 0;
static constexpr int nonexistent = 1;
static constexpr int ambiguous = 2;
int result;
sys_info first;
sys_info second;
};
template<class charT, class traits> // C++20
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os, const local_info& li);
// 25.10.5, class time_zone // C++20
enum class choose {earliest, latest};
class time_zone {
Expand Down Expand Up @@ -834,6 +848,7 @@ namespace std {
template<class Rep, class Period, class charT>
struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, charT>; // C++20
template<class charT> struct formatter<chrono::sys_info, charT>; // C++20
template<class charT> struct formatter<chrono::local_info, charT>; // C++20
} // namespace std
namespace chrono {
Expand Down Expand Up @@ -899,6 +914,7 @@ constexpr chrono::year operator ""y(unsigned lo
# include <__chrono/day.h>
# include <__chrono/hh_mm_ss.h>
# include <__chrono/literals.h>
# include <__chrono/local_info.h>
# include <__chrono/month.h>
# include <__chrono/month_weekday.h>
# include <__chrono/monthday.h>
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/libcxx.imp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@
{ include: [ "<__chrono/high_resolution_clock.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/leap_second.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/literals.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/local_info.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/month.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/month_weekday.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/monthday.h>", "private", "<chrono>", "public" ] },
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,7 @@ module std_private_chrono_high_resolution_clock [system] {
}
module std_private_chrono_leap_second [system] { header "__chrono/leap_second.h" }
module std_private_chrono_literals [system] { header "__chrono/literals.h" }
module std_private_chrono_local_info [system] { header "__chrono/local_info.h" }
module std_private_chrono_month [system] { header "__chrono/month.h" }
module std_private_chrono_month_weekday [system] { header "__chrono/month_weekday.h" }
module std_private_chrono_monthday [system] { header "__chrono/monthday.h" }
Expand Down
1 change: 1 addition & 0 deletions libcxx/modules/std/chrono.inc
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ export namespace std {
# endif // if 0

// [time.zone.info], information classes
using std::chrono::local_info;
using std::chrono::sys_info;

# if 0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: no-localization

// TODO FMT This test should not require std::to_chars(floating-point)
// XFAIL: availability-fp_to_chars-missing

// XFAIL: libcpp-has-no-incomplete-tzdb

// <chrono>

// template<class charT, class traits>
// basic_ostream<charT, traits>&
// operator<<(basic_ostream<charT, traits>& os, const local_info& r);

// [time.zone.info.local]
// 7 Effects: Streams out the local_info object r in an unspecified format.
// 8 Returns: os.
//
// Tests the output produced by this function.

#include <cassert>
#include <chrono>
#include <memory>
#include <sstream>

#include "assert_macros.h"
#include "test_macros.h"
#include "make_string.h"
#include "concat_macros.h"

#define SV(S) MAKE_STRING_VIEW(CharT, S)

template <class CharT>
static void test(std::basic_string_view<CharT> expected, std::chrono::local_info&& info) {
std::basic_stringstream<CharT> sstr;
sstr << info;
std::basic_string<CharT> output = sstr.str();

TEST_REQUIRE(expected == output,
TEST_WRITE_CONCATENATED("\nExpected output ", expected, "\nActual output ", output, '\n'));
}

template <class CharT>
static void test() {
using namespace std::literals::chrono_literals;
namespace tz = std::chrono;
// result values matching the "known" results
test(SV("unique: "
"{[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\", "
"[1970-01-01 00:00:00, 1970-01-01 00:00:00) 00:00:00 0min \"\"}"),
tz::local_info{tz::local_info::unique,
tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"},
tz::sys_info{}});

test(SV("non-existent: "
"{[1970-01-01 00:00:00, 2038-12-31 00:00:00) 12:23:45 -67min \"NEG\", "
"[1970-01-01 00:00:00, 2038-12-31 00:00:00) -12:23:45 67min \"POS\"}"),
tz::local_info{
tz::local_info::nonexistent,
tz::sys_info{static_cast<tz::sys_days>(tz::year_month_day{1970y, tz::January, 1d}),
static_cast<tz::sys_days>(tz::year_month_day{2038y, tz::December, 31d}),
12h + 23min + 45s,
-67min,
"NEG"},
tz::sys_info{static_cast<tz::sys_days>(tz::year_month_day{1970y, tz::January, 1d}),
static_cast<tz::sys_days>(tz::year_month_day{2038y, tz::December, 31d}),
-(12h + 23min + 45s),
67min,
"POS"}});

test(SV("ambiguous: "
"{[1970-01-01 00:00:00, 2038-12-31 00:00:00) 12:23:45 -67min \"NEG\", "
"[1970-01-01 00:00:00, 2038-12-31 00:00:00) -12:23:45 67min \"POS\"}"),
tz::local_info{
tz::local_info::ambiguous,
tz::sys_info{static_cast<tz::sys_days>(tz::year_month_day{1970y, tz::January, 1d}),
static_cast<tz::sys_days>(tz::year_month_day{2038y, tz::December, 31d}),
12h + 23min + 45s,
-67min,
"NEG"},
tz::sys_info{static_cast<tz::sys_days>(tz::year_month_day{1970y, tz::January, 1d}),
static_cast<tz::sys_days>(tz::year_month_day{2038y, tz::December, 31d}),
-(12h + 23min + 45s),
67min,
"POS"}});

// result values not matching the "known" results
test(
SV("unspecified result (-1): "
"{[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\", "
"[1970-01-01 00:00:00, 1970-01-01 00:00:00) 00:00:00 0min \"\"}"),
tz::local_info{-1, tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"}, tz::sys_info{}});
test(SV("unspecified result (3): "
"{[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\", "
"[1970-01-01 00:00:00, 1970-01-01 00:00:00) 00:00:00 0min \"\"}"),
tz::local_info{3, tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"}, tz::sys_info{}});
}

int main(int, const char**) {
test<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif

return 0;
}
Loading

0 comments on commit 8a21d59

Please sign in to comment.