blob: b4127af511b583266b2032f899b0e81c039ccd9a [file] [log] [blame]
//===----------------------------------------------------------------------===//
// 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
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
// XFAIL: availability-fp_to_chars-missing
// REQUIRES: locale.fr_FR.UTF-8
// REQUIRES: locale.ja_JP.UTF-8
// <chrono>
// template<class Rep, class Period, class charT>
// struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, charT>;
#include <chrono>
#include <format>
#include <cassert>
#include <concepts>
#include <locale>
#include <iostream>
#include <ratio>
#include <type_traits>
#include "formatter_tests.h"
#include "make_string.h"
#include "platform_support.h" // locale name macros
#include "string_literal.h"
#include "test_macros.h"
template <class CharT>
static void test_no_chrono_specs() {
using namespace std::literals::chrono_literals;
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output
check(SV("00:00:00.000"), SV("{}"), std::chrono::hh_mm_ss{0ms});
check(SV("*00:00:00.000*"), SV("{:*^14}"), std::chrono::hh_mm_ss{0ms});
check(SV("*00:00:00.000"), SV("{:*>13}"), std::chrono::hh_mm_ss{0ms});
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_valid_values() {
using namespace std::literals::chrono_literals;
constexpr std::basic_string_view<CharT> fmt = SV(
"{:"
"%%H='%H'%t"
"%%OH='%OH'%t"
"%%I='%I'%t"
"%%OI='%OI'%t"
"%%M='%M'%t"
"%%OM='%OM'%t"
"%%S='%S'%t"
"%%OS='%OS'%t"
"%%p='%p'%t"
"%%R='%R'%t"
"%%T='%T'%t"
"%%r='%r'%t"
"%%X='%X'%t"
"%%EX='%EX'%t"
"%n}");
constexpr std::basic_string_view<CharT> lfmt = SV(
"{:L"
"%%H='%H'%t"
"%%OH='%OH'%t"
"%%I='%I'%t"
"%%OI='%OI'%t"
"%%M='%M'%t"
"%%OM='%OM'%t"
"%%S='%S'%t"
"%%OS='%OS'%t"
"%%p='%p'%t"
"%%R='%R'%t"
"%%T='%T'%t"
"%%r='%r'%t"
"%%X='%X'%t"
"%%EX='%EX'%t"
"%n}");
const std::locale loc(LOCALE_ja_JP_UTF_8);
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output using C-locale
check(SV("%H='00'\t"
"%OH='00'\t"
"%I='12'\t"
"%OI='12'\t"
"%M='00'\t"
"%OM='00'\t"
"%S='00'\t"
"%OS='00'\t"
"%p='AM'\t"
"%R='00:00'\t"
"%T='00:00:00'\t"
"%r='12:00:00 AM'\t"
"%X='00:00:00'\t"
"%EX='00:00:00'\t"
"\n"),
fmt,
std::chrono::hh_mm_ss(0s));
check(SV("%H='23'\t"
"%OH='23'\t"
"%I='11'\t"
"%OI='11'\t"
"%M='31'\t"
"%OM='31'\t"
"%S='30.123'\t"
"%OS='30.123'\t"
"%p='PM'\t"
"%R='23:31'\t"
"%T='23:31:30.123'\t"
"%r='11:31:30 PM'\t"
"%X='23:31:30'\t"
"%EX='23:31:30'\t"
"\n"),
fmt,
std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms));
check(SV("-%H='03'\t"
"%OH='03'\t"
"%I='03'\t"
"%OI='03'\t"
"%M='02'\t"
"%OM='02'\t"
"%S='01.123456789012'\t"
"%OS='01.123456789012'\t"
"%p='AM'\t"
"%R='03:02'\t"
"%T='03:02:01.123456789012'\t"
"%r='03:02:01 AM'\t"
"%X='03:02:01'\t"
"%EX='03:02:01'\t"
"\n"),
fmt,
std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<std::int64_t, std::pico>(123456789012))));
// The number of fractional seconds is 0 according to the Standard
// TODO FMT Determine what to do.
check(SV("%H='01'\t"
"%OH='01'\t"
"%I='01'\t"
"%OI='01'\t"
"%M='01'\t"
"%OM='01'\t"
"%S='01'\t"
"%OS='01'\t"
"%p='AM'\t"
"%R='01:01'\t"
"%T='01:01:01'\t"
"%r='01:01:01 AM'\t"
"%X='01:01:01'\t"
"%EX='01:01:01'\t"
"\n"),
fmt,
std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456)));
// Use the global locale (fr_FR)
check(SV("%H='00'\t"
"%OH='00'\t"
"%I='12'\t"
"%OI='12'\t"
"%M='00'\t"
"%OM='00'\t"
"%S='00'\t"
"%OS='00'\t"
#if defined(_AIX)
"%p='AM'\t"
#else
"%p=''\t"
#endif
"%R='00:00'\t"
"%T='00:00:00'\t"
#ifdef _WIN32
"%r='00:00:00'\t"
#elif defined(_AIX)
"%r='12:00:00 AM'\t"
#elif defined(__APPLE__) || defined(__FreeBSD__)
"%r=''\t"
#else
"%r='12:00:00 '\t"
#endif
"%X='00:00:00'\t"
"%EX='00:00:00'\t"
"\n"),
lfmt,
std::chrono::hh_mm_ss(0s));
check(SV("%H='23'\t"
"%OH='23'\t"
"%I='11'\t"
"%OI='11'\t"
"%M='31'\t"
"%OM='31'\t"
"%S='30,123'\t"
"%OS='30,123'\t"
#if defined(_AIX)
"%p='PM'\t"
#else
"%p=''\t"
#endif
"%R='23:31'\t"
"%T='23:31:30,123'\t"
#ifdef _WIN32
"%r='23:31:30'\t"
#elif defined(_AIX)
"%r='11:31:30 PM'\t"
#elif defined(__APPLE__) || defined(__FreeBSD__)
"%r=''\t"
#else
"%r='11:31:30 '\t"
#endif
"%X='23:31:30'\t"
"%EX='23:31:30'\t"
"\n"),
lfmt,
std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms));
check(SV("-%H='03'\t"
"%OH='03'\t"
"%I='03'\t"
"%OI='03'\t"
"%M='02'\t"
"%OM='02'\t"
"%S='01,123456789012'\t"
"%OS='01,123456789012'\t"
#if defined(_AIX)
"%p='AM'\t"
#else
"%p=''\t"
#endif
"%R='03:02'\t"
"%T='03:02:01,123456789012'\t"
#ifdef _WIN32
"%r='03:02:01'\t"
#elif defined(_AIX)
"%r='03:02:01 AM'\t"
#elif defined(__APPLE__) || defined(__FreeBSD__)
"%r=''\t"
#else
"%r='03:02:01 '\t"
#endif
"%X='03:02:01'\t"
"%EX='03:02:01'\t"
"\n"),
lfmt,
std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<std::int64_t, std::pico>(123456789012))));
check(SV("%H='01'\t"
"%OH='01'\t"
"%I='01'\t"
"%OI='01'\t"
"%M='01'\t"
"%OM='01'\t"
"%S='01'\t"
"%OS='01'\t"
#if defined(_AIX)
"%p='AM'\t"
#else
"%p=''\t"
#endif
"%R='01:01'\t"
"%T='01:01:01'\t"
#ifdef _WIN32
"%r='01:01:01'\t"
#elif defined(_AIX)
"%r='01:01:01 AM'\t"
#elif defined(__APPLE__) || defined(__FreeBSD__)
"%r=''\t"
#else
"%r='01:01:01 '\t"
#endif
"%X='01:01:01'\t"
"%EX='01:01:01'\t"
"\n"),
lfmt,
std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456)));
// Use supplied locale (ja_JP). This locale has a different alternate.
#if defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__)
check(loc,
SV("%H='00'\t"
"%OH='00'\t"
"%I='12'\t"
"%OI='12'\t"
"%M='00'\t"
"%OM='00'\t"
"%S='00'\t"
"%OS='00'\t"
# if defined(__APPLE__)
"%p='AM'\t"
# else
"%p='午前'\t"
# endif
"%R='00:00'\t"
"%T='00:00:00'\t"
# if defined(__APPLE__) || defined(__FreeBSD__)
# if defined(__APPLE__)
"%r='12:00:00 AM'\t"
# else
"%r='12:00:00 午前'\t"
# endif
"%X='00時00分00秒'\t"
"%EX='00時00分00秒'\t"
# elif defined(_WIN32)
"%r='0:00:00'\t"
"%X='0:00:00'\t"
"%EX='0:00:00'\t"
# else
"%r='午前12:00:00'\t"
"%X='00:00:00'\t"
"%EX='00:00:00'\t"
# endif
"\n"),
lfmt,
std::chrono::hh_mm_ss(0s));
check(loc,
SV("%H='23'\t"
"%OH='23'\t"
"%I='11'\t"
"%OI='11'\t"
"%M='31'\t"
"%OM='31'\t"
"%S='30.123'\t"
"%OS='30.123'\t"
# if defined(__APPLE__)
"%p='PM'\t"
# else
"%p='午後'\t"
# endif
"%R='23:31'\t"
"%T='23:31:30.123'\t"
# if defined(__APPLE__) || defined(__FreeBSD__)
# if defined(__APPLE__)
"%r='11:31:30 PM'\t"
# else
"%r='11:31:30 午後'\t"
# endif
"%X='23時31分30秒'\t"
"%EX='23時31分30秒'\t"
# elif defined(_WIN32)
"%r='23:31:30'\t"
"%X='23:31:30'\t"
"%EX='23:31:30'\t"
# else
"%r='午後11:31:30'\t"
"%X='23:31:30'\t"
"%EX='23:31:30'\t"
# endif
"\n"),
lfmt,
std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms));
check(loc,
SV("-%H='03'\t"
"%OH='03'\t"
"%I='03'\t"
"%OI='03'\t"
"%M='02'\t"
"%OM='02'\t"
"%S='01.123456789012'\t"
"%OS='01.123456789012'\t"
# if defined(__APPLE__)
"%p='AM'\t"
# else
"%p='午前'\t"
# endif
"%R='03:02'\t"
"%T='03:02:01.123456789012'\t"
# if defined(__APPLE__) || defined(__FreeBSD__)
# if defined(__APPLE__)
"%r='03:02:01 AM'\t"
# else
"%r='03:02:01 午前'\t"
# endif
"%X='03時02分01秒'\t"
"%EX='03時02分01秒'\t"
# elif defined(_WIN32)
"%r='3:02:01'\t"
"%X='3:02:01'\t"
"%EX='3:02:01'\t"
# else
"%r='午前03:02:01'\t"
"%X='03:02:01'\t"
"%EX='03:02:01'\t"
# endif
"\n"),
lfmt,
std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<std::int64_t, std::pico>(123456789012))));
check(loc,
SV("%H='01'\t"
"%OH='01'\t"
"%I='01'\t"
"%OI='01'\t"
"%M='01'\t"
"%OM='01'\t"
"%S='01'\t"
"%OS='01'\t"
# if defined(__APPLE__)
"%p='AM'\t"
# else
"%p='午前'\t"
# endif
"%R='01:01'\t"
"%T='01:01:01'\t"
# if defined(__APPLE__) || defined(__FreeBSD__)
# if defined(__APPLE__)
"%r='01:01:01 AM'\t"
# else
"%r='01:01:01 午前'\t"
# endif
"%X='01時01分01秒'\t"
"%EX='01時01分01秒'\t"
# elif defined(_WIN32)
"%r='1:01:01'\t"
"%X='1:01:01'\t"
"%EX='1:01:01'\t"
# else
"%r='午前01:01:01'\t"
"%X='01:01:01'\t"
"%EX='01:01:01'\t"
# endif
"\n"),
lfmt,
std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456)));
#else // defined(__APPLE__) || defined(_AIX) || defined(_WIN32)
check(loc,
SV("%H='00'\t"
"%OH='〇'\t"
"%I='12'\t"
"%OI='十二'\t"
"%M='00'\t"
"%OM='〇'\t"
"%S='00'\t"
"%OS='〇'\t"
"%p='午前'\t"
"%R='00:00'\t"
"%T='00:00:00'\t"
"%r='午前12時00分00秒'\t"
"%X='00時00分00秒'\t"
"%EX='00時00分00秒'\t"
"\n"),
lfmt,
std::chrono::hh_mm_ss(0s));
// TODO FMT What should fractions be in alternate display mode?
check(loc,
SV("%H='23'\t"
"%OH='二十三'\t"
"%I='11'\t"
"%OI='十一'\t"
"%M='31'\t"
"%OM='三十一'\t"
"%S='30.123'\t"
"%OS='三十.123'\t"
"%p='午後'\t"
"%R='23:31'\t"
"%T='23:31:30.123'\t"
"%r='午後11時31分30秒'\t"
"%X='23時31分30秒'\t"
"%EX='23時31分30秒'\t"
"\n"),
lfmt,
std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms));
check(loc,
SV("-%H='03'\t"
"%OH='三'\t"
"%I='03'\t"
"%OI='三'\t"
"%M='02'\t"
"%OM='二'\t"
"%S='01.123456789012'\t"
"%OS='一.123456789012'\t"
"%p='午前'\t"
"%R='03:02'\t"
"%T='03:02:01.123456789012'\t"
"%r='午前03時02分01秒'\t"
"%X='03時02分01秒'\t"
"%EX='03時02分01秒'\t"
"\n"),
lfmt,
std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<std::int64_t, std::pico>(123456789012))));
check(loc,
SV("%H='01'\t"
"%OH='一'\t"
"%I='01'\t"
"%OI='一'\t"
"%M='01'\t"
"%OM='一'\t"
"%S='01'\t"
"%OS='一'\t"
"%p='午前'\t"
"%R='01:01'\t"
"%T='01:01:01'\t"
"%r='午前01時01分01秒'\t"
"%X='01時01分01秒'\t"
"%EX='01時01分01秒'\t"
"\n"),
lfmt,
std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456)));
#endif // defined(__APPLE__) || defined(_AIX) || defined(_WIN32)
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_invalid_values() {
using namespace std::literals::chrono_literals;
// This looks odd, however the 24 hours is not valid for a 24 hour clock.
// TODO FMT discuss what the "proper" behaviour is.
check_exception("Formatting a hour needs a valid value", SV("{:%H"), std::chrono::hh_mm_ss{24h});
check_exception("Formatting a hour needs a valid value", SV("{:%OH"), std::chrono::hh_mm_ss{24h});
check_exception("Formatting a hour needs a valid value", SV("{:%I"), std::chrono::hh_mm_ss{24h});
check_exception("Formatting a hour needs a valid value", SV("{:%OI"), std::chrono::hh_mm_ss{24h});
check(SV("00"), SV("{:%M}"), std::chrono::hh_mm_ss{24h});
check(SV("00"), SV("{:%OM}"), std::chrono::hh_mm_ss{24h});
check(SV("00"), SV("{:%S}"), std::chrono::hh_mm_ss{24h});
check(SV("00"), SV("{:%OS}"), std::chrono::hh_mm_ss{24h});
check_exception("Formatting a hour needs a valid value", SV("{:%p"), std::chrono::hh_mm_ss{24h});
check_exception("Formatting a hour needs a valid value", SV("{:%R"), std::chrono::hh_mm_ss{24h});
check_exception("Formatting a hour needs a valid value", SV("{:%T"), std::chrono::hh_mm_ss{24h});
check_exception("Formatting a hour needs a valid value", SV("{:%r"), std::chrono::hh_mm_ss{24h});
check_exception("Formatting a hour needs a valid value", SV("{:%X"), std::chrono::hh_mm_ss{24h});
check_exception("Formatting a hour needs a valid value", SV("{:%EX"), std::chrono::hh_mm_ss{24h});
}
template <class CharT>
static void test() {
using namespace std::literals::chrono_literals;
test_no_chrono_specs<CharT>();
test_valid_values<CharT>();
test_invalid_values<CharT>();
check_invalid_types<CharT>(
{SV("H"),
SV("I"),
SV("M"),
SV("S"),
SV("p"),
SV("r"),
SV("R"),
SV("T"),
SV("X"),
SV("OH"),
SV("OI"),
SV("OM"),
SV("OS"),
SV("EX")},
std::chrono::hh_mm_ss{0ms});
check_exception("The format specifier expects a '%' or a '}'", SV("{:A"), std::chrono::hh_mm_ss{0ms});
check_exception("The chrono specifiers contain a '{'", SV("{:%%{"), std::chrono::hh_mm_ss{0ms});
check_exception("End of input while parsing a conversion specifier", SV("{:%"), std::chrono::hh_mm_ss{0ms});
check_exception("End of input while parsing the modifier E", SV("{:%E"), std::chrono::hh_mm_ss{0ms});
check_exception("End of input while parsing the modifier O", SV("{:%O"), std::chrono::hh_mm_ss{0ms});
check_exception("The format specifier expects a '%' or a '}'", SV("{:.3}"), std::chrono::hh_mm_ss{0ms});
}
int main(int, char**) {
test<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif
return 0;
}