11fda1776SMark de Wever //===----------------------------------------------------------------------===// 21fda1776SMark de Wever // 31fda1776SMark de Wever // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 41fda1776SMark de Wever // See https://llvm.org/LICENSE.txt for license information. 51fda1776SMark de Wever // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 61fda1776SMark de Wever // 71fda1776SMark de Wever //===----------------------------------------------------------------------===// 81fda1776SMark de Wever 91fda1776SMark de Wever // UNSUPPORTED: c++03, c++11, c++14, c++17 101fda1776SMark de Wever // UNSUPPORTED: no-filesystem, no-localization, no-tzdb, has-no-zdump 11148417bfSLouis Dionne // REQUIRES: long_tests 121fda1776SMark de Wever 13a4422a51SMark de Wever // XFAIL: libcpp-has-no-experimental-tzdb 141fda1776SMark de Wever // XFAIL: availability-tzdb-missing 151fda1776SMark de Wever 161fda1776SMark de Wever #include <chrono> 171fda1776SMark de Wever #include <format> 181fda1776SMark de Wever #include <fstream> 191fda1776SMark de Wever #include <cassert> 201fda1776SMark de Wever 211fda1776SMark de Wever #include "filesystem_test_helper.h" 221fda1776SMark de Wever #include "assert_macros.h" 231fda1776SMark de Wever #include "concat_macros.h" 241fda1776SMark de Wever 251fda1776SMark de Wever // The year range to validate. The dates used in practice are expected to be 261fda1776SMark de Wever // inside the tested range. 271fda1776SMark de Wever constexpr std::chrono::year first{1800}; 28*cdd608b8SDavid Spickett constexpr std::chrono::year last{sizeof(time_t) == 8 ? 2100 : 2037}; 291fda1776SMark de Wever 301fda1776SMark de Wever // A custom sys_info class that also stores the name of the time zone. 311fda1776SMark de Wever // Its formatter matches the output of zdump. 321fda1776SMark de Wever struct sys_info : public std::chrono::sys_info { 331fda1776SMark de Wever sys_info(std::string_view name_, std::chrono::sys_info info) : std::chrono::sys_info{info}, name{name_} {} 341fda1776SMark de Wever 351fda1776SMark de Wever std::string name; 361fda1776SMark de Wever }; 371fda1776SMark de Wever 381fda1776SMark de Wever template <> 391fda1776SMark de Wever struct std::formatter<sys_info, char> { 401fda1776SMark de Wever template <class ParseContext> 411fda1776SMark de Wever constexpr typename ParseContext::iterator parse(ParseContext& ctx) { 421fda1776SMark de Wever return ctx.begin(); 431fda1776SMark de Wever } 441fda1776SMark de Wever 451fda1776SMark de Wever template <class FormatContext> 461fda1776SMark de Wever typename FormatContext::iterator format(const sys_info& info, FormatContext& ctx) const { 471fda1776SMark de Wever using namespace std::literals::chrono_literals; 481fda1776SMark de Wever 491fda1776SMark de Wever // Every "sys_info" entry of zdump consists of 2 lines. 501fda1776SMark de Wever // - 1 for first second of the range 511fda1776SMark de Wever // - 1 for last second of the range 521fda1776SMark de Wever // For example: 531fda1776SMark de Wever // Africa/Casablanca Sun Mar 25 02:00:00 2018 UT = Sun Mar 25 03:00:00 2018 +01 isdst=1 gmtoff=3600 541fda1776SMark de Wever // Africa/Casablanca Sun May 13 01:59:59 2018 UT = Sun May 13 02:59:59 2018 +01 isdst=1 gmtoff=3600 551fda1776SMark de Wever 561fda1776SMark de Wever if (info.begin != std::chrono::sys_seconds::min()) 571fda1776SMark de Wever ctx.advance_to(std::format_to( 581fda1776SMark de Wever ctx.out(), 591fda1776SMark de Wever "{} {:%a %b %e %H:%M:%S %Y} UT = {:%a %b %e %H:%M:%S %Y} {} isdst={:d} gmtoff={:%Q}\n", 601fda1776SMark de Wever info.name, 611fda1776SMark de Wever info.begin, 621fda1776SMark de Wever info.begin + info.offset, 631fda1776SMark de Wever info.abbrev, 641fda1776SMark de Wever info.save != 0s, 651fda1776SMark de Wever info.offset)); 661fda1776SMark de Wever 671fda1776SMark de Wever if (info.end != std::chrono::sys_seconds::max()) 681fda1776SMark de Wever ctx.advance_to(std::format_to( 691fda1776SMark de Wever ctx.out(), 701fda1776SMark de Wever "{} {:%a %b %e %H:%M:%S %Y} UT = {:%a %b %e %H:%M:%S %Y} {} isdst={:d} gmtoff={:%Q}\n", 711fda1776SMark de Wever info.name, 721fda1776SMark de Wever info.end - 1s, 731fda1776SMark de Wever info.end - 1s + info.offset, 741fda1776SMark de Wever info.abbrev, 751fda1776SMark de Wever info.save != 0s, 761fda1776SMark de Wever info.offset)); 771fda1776SMark de Wever 781fda1776SMark de Wever return ctx.out(); 791fda1776SMark de Wever } 801fda1776SMark de Wever }; 811fda1776SMark de Wever 821fda1776SMark de Wever void process(std::ostream& stream, const std::chrono::time_zone& zone) { 831fda1776SMark de Wever using namespace std::literals::chrono_literals; 841fda1776SMark de Wever 851fda1776SMark de Wever constexpr auto begin = std::chrono::time_point_cast<std::chrono::seconds>( 861fda1776SMark de Wever static_cast<std::chrono::sys_days>(std::chrono::year_month_day{first, std::chrono::January, 1d})); 871fda1776SMark de Wever constexpr auto end = std::chrono::time_point_cast<std::chrono::seconds>( 881fda1776SMark de Wever static_cast<std::chrono::sys_days>(std::chrono::year_month_day{last, std::chrono::January, 1d})); 891fda1776SMark de Wever 901fda1776SMark de Wever std::chrono::sys_seconds s = begin; 911fda1776SMark de Wever do { 921fda1776SMark de Wever sys_info info{zone.name(), zone.get_info(s)}; 931fda1776SMark de Wever 941fda1776SMark de Wever if (info.end >= end) 951fda1776SMark de Wever info.end = std::chrono::sys_seconds::max(); 961fda1776SMark de Wever 971fda1776SMark de Wever stream << std::format("{}", info); 981fda1776SMark de Wever s = info.end; 991fda1776SMark de Wever } while (s != std::chrono::sys_seconds::max()); 1001fda1776SMark de Wever } 1011fda1776SMark de Wever 1021fda1776SMark de Wever // This test compares the output of the zdump against the output based on the 1031fda1776SMark de Wever // standard library implementation. It tests all available time zones and 1041fda1776SMark de Wever // validates them. The specification of how to use the IANA database is limited 1051fda1776SMark de Wever // and the real database contains quite a number of "interesting" cases. 1061fda1776SMark de Wever int main(int, const char**) { 1071fda1776SMark de Wever scoped_test_env env; 1081fda1776SMark de Wever const std::string file = env.create_file("zdump.txt"); 1091fda1776SMark de Wever 1101fda1776SMark de Wever const std::chrono::tzdb& tzdb = std::chrono::get_tzdb(); 1111fda1776SMark de Wever for (const auto& zone : tzdb.zones) { 1121fda1776SMark de Wever std::stringstream libcxx; 1131fda1776SMark de Wever process(libcxx, zone); 1141fda1776SMark de Wever 1151fda1776SMark de Wever int result = std::system(std::format("zdump -V -c{},{} {} > {}", first, last, zone.name(), file).c_str()); 1161fda1776SMark de Wever assert(result == 0); 1171fda1776SMark de Wever 1181fda1776SMark de Wever std::stringstream zdump; 1191fda1776SMark de Wever zdump << std::ifstream(file).rdbuf(); 1201fda1776SMark de Wever 1211fda1776SMark de Wever TEST_REQUIRE( 1221fda1776SMark de Wever libcxx.str() == zdump.str(), 1231fda1776SMark de Wever TEST_WRITE_CONCATENATED("\nTZ=", zone.name(), "\nlibc++\n", libcxx.str(), "|\n\nzdump\n", zdump.str(), "|")); 1241fda1776SMark de Wever } 1251fda1776SMark de Wever 1261fda1776SMark de Wever return 0; 1271fda1776SMark de Wever } 128