128 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++1710// UNSUPPORTED: no-filesystem, no-localization, no-tzdb, has-no-zdump11// REQUIRES: long_tests12 13// XFAIL: libcpp-has-no-experimental-tzdb14// XFAIL: availability-tzdb-missing15 16#include <chrono>17#include <format>18#include <fstream>19#include <cassert>20 21#include "filesystem_test_helper.h"22#include "assert_macros.h"23#include "concat_macros.h"24 25// The year range to validate. The dates used in practice are expected to be26// inside the tested range.27constexpr std::chrono::year first{1800};28constexpr std::chrono::year last{sizeof(time_t) == 8 ? 2100 : 2037};29 30// A custom sys_info class that also stores the name of the time zone.31// Its formatter matches the output of zdump.32struct sys_info : public std::chrono::sys_info {33 sys_info(std::string_view name_, std::chrono::sys_info info) : std::chrono::sys_info{info}, name{name_} {}34 35 std::string name;36};37 38template <>39struct std::formatter<sys_info, char> {40 template <class ParseContext>41 constexpr typename ParseContext::iterator parse(ParseContext& ctx) {42 return ctx.begin();43 }44 45 template <class FormatContext>46 typename FormatContext::iterator format(const sys_info& info, FormatContext& ctx) const {47 using namespace std::literals::chrono_literals;48 49 // Every "sys_info" entry of zdump consists of 2 lines.50 // - 1 for first second of the range51 // - 1 for last second of the range52 // For example:53 // Africa/Casablanca Sun Mar 25 02:00:00 2018 UT = Sun Mar 25 03:00:00 2018 +01 isdst=1 gmtoff=360054 // Africa/Casablanca Sun May 13 01:59:59 2018 UT = Sun May 13 02:59:59 2018 +01 isdst=1 gmtoff=360055 56 if (info.begin != std::chrono::sys_seconds::min())57 ctx.advance_to(std::format_to(58 ctx.out(),59 "{} {:%a %b %e %H:%M:%S %Y} UT = {:%a %b %e %H:%M:%S %Y} {} isdst={:d} gmtoff={:%Q}\n",60 info.name,61 info.begin,62 info.begin + info.offset,63 info.abbrev,64 info.save != 0s,65 info.offset));66 67 if (info.end != std::chrono::sys_seconds::max())68 ctx.advance_to(std::format_to(69 ctx.out(),70 "{} {:%a %b %e %H:%M:%S %Y} UT = {:%a %b %e %H:%M:%S %Y} {} isdst={:d} gmtoff={:%Q}\n",71 info.name,72 info.end - 1s,73 info.end - 1s + info.offset,74 info.abbrev,75 info.save != 0s,76 info.offset));77 78 return ctx.out();79 }80};81 82void process(std::ostream& stream, const std::chrono::time_zone& zone) {83 using namespace std::literals::chrono_literals;84 85 constexpr auto begin = std::chrono::time_point_cast<std::chrono::seconds>(86 static_cast<std::chrono::sys_days>(std::chrono::year_month_day{first, std::chrono::January, 1d}));87 constexpr auto end = std::chrono::time_point_cast<std::chrono::seconds>(88 static_cast<std::chrono::sys_days>(std::chrono::year_month_day{last, std::chrono::January, 1d}));89 90 std::chrono::sys_seconds s = begin;91 do {92 sys_info info{zone.name(), zone.get_info(s)};93 94 if (info.end >= end)95 info.end = std::chrono::sys_seconds::max();96 97 stream << std::format("{}", info);98 s = info.end;99 } while (s != std::chrono::sys_seconds::max());100}101 102// This test compares the output of the zdump against the output based on the103// standard library implementation. It tests all available time zones and104// validates them. The specification of how to use the IANA database is limited105// and the real database contains quite a number of "interesting" cases.106int main(int, const char**) {107 scoped_test_env env;108 const std::string file = env.create_file("zdump.txt");109 110 const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();111 for (const auto& zone : tzdb.zones) {112 std::stringstream libcxx;113 process(libcxx, zone);114 115 int result = std::system(std::format("zdump -V -c{},{} {} > {}", first, last, zone.name(), file).c_str());116 assert(result == 0);117 118 std::stringstream zdump;119 zdump << std::ifstream(file).rdbuf();120 121 TEST_REQUIRE(122 libcxx.str() == zdump.str(),123 TEST_WRITE_CONCATENATED("\nTZ=", zone.name(), "\nlibc++\n", libcxx.str(), "|\n\nzdump\n", zdump.str(), "|"));124 }125 126 return 0;127}128