llvm/libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.nonmembers/ostream.pass.cpp

//===----------------------------------------------------------------------===//
//
// 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-filesystem, no-localization, no-tzdb

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

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

// REQUIRES: locale.fr_FR.UTF-8
// REQUIRES: locale.ja_JP.UTF-8

// <chrono>

// template<class charT, class traits, class Duration, class TimeZonePtr>
// basic_ostream<charT, traits>&
// operator<<(basic_ostream<charT, traits>& os,
//            const zoned_time<Duration, TimeZonePtr>& t);

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

#include "assert_macros.h"
#include "concat_macros.h"
#include "make_string.h"
#include "platform_support.h" // locale name macros
#include "test_macros.h"
#include "../test_offset_time_zone.h"

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

#define TEST_EQUAL(OUT, EXPECTED)                                                                                      \
  TEST_REQUIRE(OUT == EXPECTED,                                                                                        \
               TEST_WRITE_CONCATENATED(                                                                                \
                   "\nExpression      ", #OUT, "\nExpected output ", EXPECTED, "\nActual output   ", OUT, '\n'));

template <class CharT, class Duration, class TimeZonePtr>
static std::basic_string<CharT> stream_c_locale(std::chrono::zoned_time<Duration, TimeZonePtr> time_point) {
  std::basic_stringstream<CharT> sstr;
  sstr << time_point;
  return sstr.str();
}

template <class CharT, class Duration, class TimeZonePtr>
static std::basic_string<CharT> stream_fr_FR_locale(std::chrono::zoned_time<Duration, TimeZonePtr> time_point) {
  std::basic_stringstream<CharT> sstr;
  const std::locale locale(LOCALE_fr_FR_UTF_8);
  sstr.imbue(locale);
  sstr << time_point;
  return sstr.str();
}

template <class CharT, class Duration, class TimeZonePtr>
static std::basic_string<CharT> stream_ja_JP_locale(std::chrono::zoned_time<Duration, TimeZonePtr> time_point) {
  std::basic_stringstream<CharT> sstr;
  const std::locale locale(LOCALE_ja_JP_UTF_8);
  sstr.imbue(locale);
  sstr << time_point;
  return sstr.str();
}

template <class CharT>
static void test_c() {
  using namespace std::literals::chrono_literals;

  { //  Different durations
    TEST_EQUAL(stream_c_locale<CharT>(
                   std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::nanoseconds>{42ns})),
               SV("1970-01-01 01:00:00.000000042 +01"));

    TEST_EQUAL(stream_c_locale<CharT>(
                   std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::microseconds>{42us})),
               SV("1970-01-01 01:00:00.000042 +01"));

    TEST_EQUAL(stream_c_locale<CharT>(
                   std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::milliseconds>{42ms})),
               SV("1970-01-01 01:00:00.042 +01"));

    TEST_EQUAL(
        stream_c_locale<CharT>(std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::seconds>{42s})),
        SV("1970-01-01 01:00:42 +01"));

    TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
                   "Etc/GMT-1", std::chrono::sys_time<std::chrono::days>{std::chrono::days{42}})),
               SV("1970-02-12 01:00:00 +01"));

    TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
                   "Etc/GMT-1", std::chrono::sys_time<std::chrono::weeks>{std::chrono::weeks{42}})),
               SV("1970-10-22 01:00:00 +01"));
  }

  { // Daylight saving time switches
    // Pick an historic date where it's well known what the time zone rules were.
    // This makes it unlikely updates to the database change these rules.

    // Z Europe/Berlin 0:53:28 - LMT 1893 Ap
    // ...
    // 1 DE CE%sT 1980
    // 1 E CE%sT
    //
    // ...
    // R E 1979 1995 - S lastSu 1u 0 -
    // R E 1981 ma - Mar lastSu 1u 1 S

    // Pick an historic date where it's well known what the time zone rules were.
    // This makes it unlikely updates to the database change these rules.

    // Start of daylight saving time
    TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 0h + 59min + 59s)),
               SV("1986-03-30 01:59:59 CET"));

    TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 1h)),
               SV("1986-03-30 03:00:00 CEST"));

    // End of daylight saving time
    TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 0h + 59min + 59s)),
               SV("1986-09-28 02:59:59 CEST"));

    TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h)),
               SV("1986-09-28 02:00:00 CET"));

    TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h + 59min + 59s)),
               SV("1986-09-28 02:59:59 CET"));

    TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h)),
               SV("1986-09-28 03:00:00 CET"));
  }

  { // offset pointer
    TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
                   offset_time_zone<offset_time_zone_flags::none>{}, std::chrono::sys_seconds{})),
               SV("1970-01-01 00:00:00 +00s"));

    TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
                   offset_time_zone<offset_time_zone_flags::none>{"42"}, std::chrono::sys_seconds{})),
               SV("1969-12-31 23:59:18 +42s"));

    TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
                   offset_time_zone<offset_time_zone_flags::none>{"-42"}, std::chrono::sys_seconds{})),
               SV("1970-01-01 00:00:42 -42s"));
  }
}

template <class CharT>
static void test_fr_FR() {
  using namespace std::literals::chrono_literals;

  { //  Different durations

    TEST_EQUAL(stream_fr_FR_locale<CharT>(
                   std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::nanoseconds>{42ns})),
               SV("1970-01-01 01:00:00,000000042 +01"));

    TEST_EQUAL(stream_fr_FR_locale<CharT>(
                   std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::microseconds>{42us})),
               SV("1970-01-01 01:00:00,000042 +01"));

    TEST_EQUAL(stream_fr_FR_locale<CharT>(
                   std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::milliseconds>{42ms})),
               SV("1970-01-01 01:00:00,042 +01"));

    TEST_EQUAL(stream_fr_FR_locale<CharT>(
                   std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::seconds>{42s})),
               SV("1970-01-01 01:00:42 +01"));

    TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
                   "Etc/GMT-1", std::chrono::sys_time<std::chrono::days>{std::chrono::days{42}})),
               SV("1970-02-12 01:00:00 +01"));

    TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
                   "Etc/GMT-1", std::chrono::sys_time<std::chrono::weeks>{std::chrono::weeks{42}})),
               SV("1970-10-22 01:00:00 +01"));
  }

  { // Daylight saving time switches
    // Pick an historic date where it's well known what the time zone rules were.
    // This makes it unlikely updates to the database change these rules.

    // Z Europe/Berlin 0:53:28 - LMT 1893 Ap
    // ...
    // 1 DE CE%sT 1980
    // 1 E CE%sT
    //
    // ...
    // R E 1979 1995 - S lastSu 1u 0 -
    // R E 1981 ma - Mar lastSu 1u 1 S

    // Pick an historic date where it's well known what the time zone rules were.
    // This makes it unlikely updates to the database change these rules.

    // Start of daylight saving time
    TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 0h + 59min + 59s)),
               SV("1986-03-30 01:59:59 CET"));

    TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 1h)),
               SV("1986-03-30 03:00:00 CEST"));

    // End of daylight saving time
    TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 0h + 59min + 59s)),
               SV("1986-09-28 02:59:59 CEST"));

    TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h)),
               SV("1986-09-28 02:00:00 CET"));

    TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h + 59min + 59s)),
               SV("1986-09-28 02:59:59 CET"));

    TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h)),
               SV("1986-09-28 03:00:00 CET"));
  }

  { // offset pointer
    TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
                   offset_time_zone<offset_time_zone_flags::none>{}, std::chrono::sys_seconds{})),
               SV("1970-01-01 00:00:00 +00s"));

    TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
                   offset_time_zone<offset_time_zone_flags::none>{"42"}, std::chrono::sys_seconds{})),
               SV("1969-12-31 23:59:18 +42s"));

    TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
                   offset_time_zone<offset_time_zone_flags::none>{"-42"}, std::chrono::sys_seconds{})),
               SV("1970-01-01 00:00:42 -42s"));
  }
}

template <class CharT>
static void test_ja_JP() {
  using namespace std::literals::chrono_literals;

  { //  Different durations

    TEST_EQUAL(stream_ja_JP_locale<CharT>(
                   std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::nanoseconds>{42ns})),
               SV("1970-01-01 01:00:00.000000042 +01"));

    TEST_EQUAL(stream_ja_JP_locale<CharT>(
                   std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::microseconds>{42us})),
               SV("1970-01-01 01:00:00.000042 +01"));

    TEST_EQUAL(stream_ja_JP_locale<CharT>(
                   std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::milliseconds>{42ms})),
               SV("1970-01-01 01:00:00.042 +01"));

    TEST_EQUAL(stream_ja_JP_locale<CharT>(
                   std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::seconds>{42s})),
               SV("1970-01-01 01:00:42 +01"));

    TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
                   "Etc/GMT-1", std::chrono::sys_time<std::chrono::days>{std::chrono::days{42}})),
               SV("1970-02-12 01:00:00 +01"));

    TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
                   "Etc/GMT-1", std::chrono::sys_time<std::chrono::weeks>{std::chrono::weeks{42}})),
               SV("1970-10-22 01:00:00 +01"));
  }

  { // Daylight saving time switches
    // Pick an historic date where it's well known what the time zone rules were.
    // This makes it unlikely updates to the database change these rules.

    // Z Europe/Berlin 0:53:28 - LMT 1893 Ap
    // ...
    // 1 DE CE%sT 1980
    // 1 E CE%sT
    //
    // ...
    // R E 1979 1995 - S lastSu 1u 0 -
    // R E 1981 ma - Mar lastSu 1u 1 S

    // Pick an historic date where it's well known what the time zone rules were.
    // This makes it unlikely updates to the database change these rules.

    // Start of daylight saving time
    TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 0h + 59min + 59s)),
               SV("1986-03-30 01:59:59 CET"));

    TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 1h)),
               SV("1986-03-30 03:00:00 CEST"));

    // End of daylight saving time
    TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 0h + 59min + 59s)),
               SV("1986-09-28 02:59:59 CEST"));

    TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h)),
               SV("1986-09-28 02:00:00 CET"));

    TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h + 59min + 59s)),
               SV("1986-09-28 02:59:59 CET"));

    TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
                   "Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h)),
               SV("1986-09-28 03:00:00 CET"));
  }

  { // offset pointer
    TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
                   offset_time_zone<offset_time_zone_flags::none>{}, std::chrono::sys_seconds{})),
               SV("1970-01-01 00:00:00 +00s"));

    TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
                   offset_time_zone<offset_time_zone_flags::none>{"42"}, std::chrono::sys_seconds{})),
               SV("1969-12-31 23:59:18 +42s"));

    TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
                   offset_time_zone<offset_time_zone_flags::none>{"-42"}, std::chrono::sys_seconds{})),
               SV("1970-01-01 00:00:42 -42s"));
  }
}

template <class CharT>
static void test() {
  test_c<CharT>();
  test_fr_FR<CharT>();
  test_ja_JP<CharT>();
}

int main(int, char**) {
  test<char>();

#ifndef TEST_HAS_NO_WIDE_CHARACTERS
  test<wchar_t>();
#endif

  return 0;
}