From 203efdc6b7b57329ee3431e468300ca01a99fd4c Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Mon, 4 Nov 2024 11:23:30 +0000 Subject: [PATCH 1/7] Build programs as C++20 Use ubuntu-24.04 and g++-14 for GitHub workflows. --- .github/workflows/check-build.yml | 4 ++-- .github/workflows/check-pr.yml | 4 ++-- .github/workflows/deploy.yml | 4 ++-- Makefile | 2 +- bin/build_pgms.bat | 10 +++++----- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 9f4f278138..5b9e82393f 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -15,7 +15,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -23,7 +23,7 @@ jobs: fetch-depth: 1 - name: Compile binary programs - run: make pgms + run: make CXX=g++-14 pgms - name: Check for carriage returns run: | diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml index 9e11dcbca1..d6bfc601f4 100644 --- a/.github/workflows/check-pr.yml +++ b/.github/workflows/check-pr.yml @@ -13,7 +13,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -21,7 +21,7 @@ jobs: fetch-depth: 1 - name: Compile binary programs - run: make pgms + run: make CXX=g++-14 pgms - name: Extract issue timestamps from published files run: | diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 270a7d452c..6401694f4f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,7 +9,7 @@ on: jobs: update-html-pages: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Use cached git repo @@ -51,7 +51,7 @@ jobs: run: make dates - name: Build programs - run: make pgms -j 4 + run: make CXX=g++-14 pgms -j 4 - name: Generate HTML lists run: make lists diff --git a/Makefile b/Makefile index 77e5bbf2fd..8e8b3501df 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # The binaries that we want to build PGMS := bin/lists bin/section_data bin/toc_diff bin/list_issues bin/set_status -CXXFLAGS := -std=c++17 -Wall -g -O2 -D_GLIBCXX_ASSERTIONS +CXXFLAGS := -std=c++20 -Wall -g -O2 -D_GLIBCXX_ASSERTIONS CPPFLAGS := -MMD # Running 'make debug' is equivalent to 'make DEBUG=1' diff --git a/bin/build_pgms.bat b/bin/build_pgms.bat index 54fa375b30..0982debb4c 100644 --- a/bin/build_pgms.bat +++ b/bin/build_pgms.bat @@ -1,7 +1,7 @@ echo "Use -m32 switch to force 32-bit build" -g++ %* -std=c++17 -DNDEBUG -O2 -o bin/lists.exe src/date.cpp src/issues.cpp src/status.cpp src/sections.cpp src/mailing_info.cpp src/report_generator.cpp src/metadata.cpp src/lists.cpp -g++ %* -std=c++17 -o bin/section_data.exe src/section_data.cpp -g++ %* -std=c++17 -o bin/toc_diff.exe src/toc_diff.cpp -g++ %* -std=c++17 -DNDEBUG -O2 -o bin/list_issues.exe src/date.cpp src/issues.cpp src/status.cpp src/sections.cpp src/metadata.cpp src/list_issues.cpp -g++ %* -std=c++17 -DNDEBUG -O2 -o bin/set_status.exe src/set_status.cpp src/status.cpp +g++ %* -std=c++20 -DNDEBUG -O2 -o bin/lists.exe src/date.cpp src/issues.cpp src/status.cpp src/sections.cpp src/mailing_info.cpp src/report_generator.cpp src/metadata.cpp src/lists.cpp +g++ %* -std=c++20 -o bin/section_data.exe src/section_data.cpp +g++ %* -std=c++20 -o bin/toc_diff.exe src/toc_diff.cpp +g++ %* -std=c++20 -DNDEBUG -O2 -o bin/list_issues.exe src/date.cpp src/issues.cpp src/status.cpp src/sections.cpp src/metadata.cpp src/list_issues.cpp +g++ %* -std=c++20 -DNDEBUG -O2 -o bin/set_status.exe src/set_status.cpp src/status.cpp From 8e9f7deecaf1a3dad8cbda6b36927ef3239052d9 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Mon, 4 Nov 2024 11:24:33 +0000 Subject: [PATCH 2/7] Use string::starts_with --- src/list_issues.cpp | 2 +- src/lists.cpp | 2 +- src/sections.cpp | 2 +- src/status.cpp | 6 ++---- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/list_issues.cpp b/src/list_issues.cpp index d112723027..09e3b20124 100644 --- a/src/list_issues.cpp +++ b/src/list_issues.cpp @@ -61,7 +61,7 @@ auto read_file_into_string(fs::path const & filename) -> std::string { auto is_issue_xml_file(fs::directory_entry const & e) { if (e.is_regular_file()) { fs::path f = e.path().filename(); - return f.string().compare(0, 5, "issue") == 0 && f.extension() == ".xml"; + return f.string().starts_with("issue") && f.extension() == ".xml"; } return false; } diff --git a/src/lists.cpp b/src/lists.cpp index 01d5a5b86f..96e476e5e9 100644 --- a/src/lists.cpp +++ b/src/lists.cpp @@ -73,7 +73,7 @@ auto read_file_into_string(fs::path const & filename) -> std::string { auto is_issue_xml_file(fs::directory_entry const & e) { if (e.is_regular_file()) { fs::path f = e.path().filename(); - return f.string().compare(0, 5, "issue") == 0 && f.extension() == ".xml"; + return f.string().starts_with("issue") && f.extension() == ".xml"; } return false; } diff --git a/src/sections.cpp b/src/sections.cpp index 0dac83ebbb..546ee39bba 100644 --- a/src/sections.cpp +++ b/src/sections.cpp @@ -180,7 +180,7 @@ auto lwg::format_section_tag_as_link(section_map & section_db, section_tag const std::string url; if (!tag.prefix.empty()) { std::string_view fund_ts = "fund.ts"; - if (tag.prefix.compare(0, fund_ts.size(), fund_ts) == 0) { + if (tag.prefix.starts_with(fund_ts)) { std::string_view version = tag.prefix; version.remove_prefix(fund_ts.size()); if (version.empty()) diff --git a/src/status.cpp b/src/status.cpp index 642807aba2..12badc2b47 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -73,9 +73,7 @@ auto lwg::is_closed(std::string_view stat) -> bool { } auto lwg::is_tentative(std::string_view stat) -> bool { - // a more efficient implementation will use some variation of strcmp - std::string_view tent{"Tentatively"}; - return 0 == stat.compare(0, tent.size(), tent); + return stat.starts_with("Tentatively"); } auto lwg::is_assigned_to_another_group(std::string_view stat) -> bool { @@ -104,7 +102,7 @@ auto lwg::is_ready(std::string_view stat) -> bool { // Functions to "normalize" a status string namespace { auto remove_prefix(std::string_view str, std::string_view prefix) -> std::string_view { - if (0 == str.compare(0, prefix.size(), prefix)) { + if (str.starts_with(prefix)) { str.remove_prefix(prefix.size() + 1); } return str; From 53445bf3b918ceb8db05e6799643355bb0fbbff5 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Mon, 4 Nov 2024 12:41:23 +0000 Subject: [PATCH 3/7] Replace src/date.cpp with C++20 and Use workarounds for missing chrono::parse and chrono::clock_cast. --- Makefile | 4 +- bin/build_pgms.bat | 4 +- src/date.cpp | 344 --------------------------------------- src/date.h | 328 ------------------------------------- src/issues.cpp | 69 ++++---- src/issues.h | 7 +- src/list_issues.cpp | 2 +- src/lists.cpp | 4 +- src/report_generator.cpp | 43 +---- 9 files changed, 51 insertions(+), 754 deletions(-) delete mode 100644 src/date.cpp delete mode 100644 src/date.h diff --git a/Makefile b/Makefile index 8e8b3501df..ff9210079c 100644 --- a/Makefile +++ b/Makefile @@ -35,13 +35,13 @@ pgms: $(PGMS) -include src/*.d -bin/lists: src/date.o src/issues.o src/status.o src/sections.o src/mailing_info.o src/report_generator.o src/lists.o src/metadata.o +bin/lists: src/issues.o src/status.o src/sections.o src/mailing_info.o src/report_generator.o src/lists.o src/metadata.o bin/section_data: src/section_data.o bin/toc_diff: src/toc_diff.o -bin/list_issues: src/date.o src/issues.o src/status.o src/sections.o src/list_issues.o src/metadata.o +bin/list_issues: src/issues.o src/status.o src/sections.o src/list_issues.o src/metadata.o bin/set_status: src/set_status.o src/status.o diff --git a/bin/build_pgms.bat b/bin/build_pgms.bat index 0982debb4c..0c09376f44 100644 --- a/bin/build_pgms.bat +++ b/bin/build_pgms.bat @@ -1,7 +1,7 @@ echo "Use -m32 switch to force 32-bit build" -g++ %* -std=c++20 -DNDEBUG -O2 -o bin/lists.exe src/date.cpp src/issues.cpp src/status.cpp src/sections.cpp src/mailing_info.cpp src/report_generator.cpp src/metadata.cpp src/lists.cpp +g++ %* -std=c++20 -DNDEBUG -O2 -o bin/lists.exe src/issues.cpp src/status.cpp src/sections.cpp src/mailing_info.cpp src/report_generator.cpp src/metadata.cpp src/lists.cpp g++ %* -std=c++20 -o bin/section_data.exe src/section_data.cpp g++ %* -std=c++20 -o bin/toc_diff.exe src/toc_diff.cpp -g++ %* -std=c++20 -DNDEBUG -O2 -o bin/list_issues.exe src/date.cpp src/issues.cpp src/status.cpp src/sections.cpp src/metadata.cpp src/list_issues.cpp +g++ %* -std=c++20 -DNDEBUG -O2 -o bin/list_issues.exe src/issues.cpp src/status.cpp src/sections.cpp src/metadata.cpp src/list_issues.cpp g++ %* -std=c++20 -DNDEBUG -O2 -o bin/set_status.exe src/set_status.cpp src/status.cpp diff --git a/src/date.cpp b/src/date.cpp deleted file mode 100644 index 72b5ee80e4..0000000000 --- a/src/date.cpp +++ /dev/null @@ -1,344 +0,0 @@ -// I, Howard Hinnant, hereby place this code in the public domain. - -#ifdef _MSC_VER -# define _CRT_SECURE_NO_WARNINGS -#endif - -#include "date.h" -#include - -namespace gregorian -{ - -namespace detail -{ - -unsigned spec::id_next = 0; - -} // detail - - -//static const unsigned char date::lastDay_s[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -static constexpr unsigned char date_lastDay_s[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -const detail::spec last; -const detail::spec first; - - -date::date() { - time_t systime; - time(&systime); - struct tm* now = std::localtime(&systime); - year_ = (unsigned short)(now->tm_year+1900); - month_ = (unsigned char)(now->tm_mon+1); - day_ = (unsigned char)(now->tm_mday); - fix_from_ymd(); -} - -date::date(detail::day_month_spec dm, gregorian::year y) - : year_{(unsigned short)y.value} - , month_{(unsigned char)dm.m_.value} - , day_{dm.d_.d_} - { - init(); -} - -date::date(gregorian::day d, detail::month_year_spec my) - : year_{(unsigned short)my.y_.value} - , month_{(unsigned char)my.m_.value} - , day_{d.d_} - { - init(); -} - - -void date::init() { - if (day_ & 0xE0) { - int dow = ((day_ & 0xE0) >> 5) - 1; - int n = day_ & 0x1F; - if (n == 0) { - n = 6; - } - day_ = 0; - encode(dow, n); - } - else if (day_ == 0) { - encode(-1, 6); - } - fix_from_ymd(); -} - - -void date::decode(int& dow, int& n) const noexcept { - if (day_ & 0xE0) { - dow = ((day_ & 0xE0) >> 5) - 1; - } - else { - dow = -1; - } - n = month_ >> 4; -} - - -void date::encode(int dow, int n) noexcept { - day_ &= 0x1F; - month_ &= 0x0F; - if (dow >= 0) { - day_ |= (unsigned char)((dow + 1) << 5); - } - month_ |= (unsigned char)(n << 4); -} - -// This could be turned into a constexpr free-function taking 'year' by value as its argument. -// However, there would be no advantage in the application we are currently working on. -auto date::is_leap() const noexcept -> bool { - if (year_ > 1582) { - if (year_ % 400 == 0) { - return true; - } - if (year_ % 100 == 0) { - return false; - } - } - return year_ % 4 == 0; -} - -void date::fix_from_ymd() { - int dow, n; - decode(dow, n); - int d = day(); - int m = month(); - int y = year(); - if (0 <= dow and dow <= 6) { - if (n == 0 or n > 6) { - throw bad_date{}; - } - date tmp1 = gregorian::day(1) / m / y; - date tmp2 = last / m / y; - int wd = tmp1.day_of_week(); - int delta{0}; - if (dow != wd) { - if (dow < wd) { - delta += 7 - (wd - dow); - } - else { - delta += dow - wd; - } - } - delta += (n-1)*7; - if (n == 6 and delta >= tmp2.day()) { - delta -= 7 * ((delta - tmp2.day()) / 7 + 1); - } - tmp1 += delta; - if (tmp1.month() != m) { - throw bad_date{}; - } - *this = tmp1; - encode(dow, n); - return; - } - - if (dow != -1) { - throw bad_date{}; - } - if (n != 0 and n != 6) { - throw bad_date(); - } - if ((d == 0 and n == 0) or m == 0 or m > 12) { - throw bad_date{}; - } - - if (n == 6) { - d = 0; - } - - unsigned long jdate = 0; - bool leap = is_leap(); - if (leap and m == 2) { - if (d > 29) { - throw bad_date{}; - } - if (n == 6) { - d = 29; - } - } - else { - if (d > date_lastDay_s[m-1]) { - throw bad_date{}; - } - if (n == 6) { - d = date_lastDay_s[m-1]; - } - } - - if (y == 1582 and m == 10 and d >= 5 and d <= 14) { - throw bad_date{}; - } - - if (y > 0) { - jdate = 365U * y + 1; // Jan 1, 0 == 1 and y 0 is leap y - --y; - jdate += y / 4; - if (y >= 1700) { - jdate += -(y-1600) / 100 + (y-1600) / 400; - } - ++y; - } - - for (int i = 0; i < m-1; ++i) { - jdate += date_lastDay_s[i]; - } - if (leap and m > 2) { - ++jdate; - } - jdate += d; - // If date >= 10/15/1582 then subtract 10 - if (jdate >= 578114) { - jdate -= 10; - } - -// if (y >= 1582) { -// if (y == 1582) { -// if (m >= 10) { -// if (m == 10) { -// if (d >= 15) { -// jdate -= 10; -// } -// } -// else { // m > 10 -// jdate -= 10; -// } -// } -// } -// else { // y > 1582 -// jdate -= 10; -// } -// } - - if (jdate <= 0) { - throw bad_date{}; - } - - jdate_ = jdate; - year_ = (unsigned short)y; - month_ = (unsigned char)m; - day_ = (unsigned char)d; - encode(dow, n); -} - - -void date::fix_from_jdate() { - if (jdate_ <= 0) { - throw bad_date{}; - } - - int dow, n; - decode(dow, n); - year_ = static_cast(jdate_ / 365.2475); - date lower = gregorian::day(1) / 1 / year_; - date upper = gregorian::day(31) / 12 / year_; - for(;;) { - if (lower.jdate_ > jdate_) { - --year_; - } - else if (upper.jdate_ < jdate_) { - ++year_; - } - else { - break; - } - lower = gregorian::day(1) / 1 / year_; - upper = gregorian::day(31) / 12 / year_; - } - - month_ = static_cast((jdate_ - lower.jdate_) / 30 + 1); - if (month_ > 12) { - month_ = 12; - } - - for(;;) { - lower = gregorian::day(1) / month_ / year_; - upper = gregorian::last / month_ / year_; - if (lower.jdate_ > jdate_) { - --month_; - } - else if (upper.jdate_ < jdate_) { - ++month_; - } - else { - break; - } - } - - day_ = static_cast(jdate_ - lower.jdate_ + 1); - if (year_ == 1582 and month_ == 10 and day_ >= 5) { - day_ += 10; - } -} - - -auto date::operator+=(int d) -> date & { - jdate_ += d; - fix_from_jdate(); - return *this; -} - -} // gregorian - -// gcc will not recognise 'noexcept' specifier as matching the header -auto gregorian::operator*(detail::spec s, week_day wd) -> day { - day d{1}; - d.d_ = (unsigned char)((wd.d_+1) << 5); - if (s == first) { - d.d_ |= 1; - } - return d; -} - - -auto gregorian::operator*(int n, week_day wd) -> day { - day d{1}; - if (n < 1 or n > 5) { - throw bad_date{}; - } - d.d_ = (unsigned char)(((wd.d_+1) << 5) | n); - return d; -} - - -auto gregorian::operator+(const date& dt, month mnth) -> date { - int dow, n; - dt.decode(dow, n); - int y = dt.year(); - int m = dt.month(); - int d = dt.day(); - int new_month = m - 1 + mnth.value; - if (new_month < 0) { - int delta = ((-new_month - 1) / 12 + 1) * 12; - y -= delta / 12; - new_month += delta; - } - else if (new_month > 11) { - int delta = ((new_month - 12) / 12 + 1) * 12; - y += delta / 12; - new_month -= delta; - } - m = new_month + 1; - date result{y, m, d}; - result.encode(dow, n); - result.fix_from_ymd(); - return result; -} - - -auto gregorian::operator+(date const & dt, year yr) -> date { - int dow, n; - dt.decode(dow, n); - int y = dt.year() + yr.value; - int m = dt.month(); - int d = dt.day(); - date result{y, m, d}; - result.encode(dow, n); - result.fix_from_ymd(); - return result; -} diff --git a/src/date.h b/src/date.h deleted file mode 100644 index bd54f9f32e..0000000000 --- a/src/date.h +++ /dev/null @@ -1,328 +0,0 @@ -// I, Howard Hinnant, hereby place this code in the public domain. -// Since tweaked by Alisdair Meredith, also in the public domain. - -#ifndef DATE_H -#define DATE_H - -#include -#include -#include -#include -#include -#include - -namespace gregorian -{ - -struct day; -struct date; - -namespace detail -{ - -struct spec { - spec() noexcept : id_{id_next++} {} - - bool operator == (const spec& y) const noexcept { return id_ == y.id_; } - bool operator != (const spec& y) const noexcept { return id_ != y.id_; } - -private: - unsigned id_; - static unsigned id_next; - - friend struct gregorian::day; -}; - -} // detail - -extern detail::spec const last; -extern detail::spec const first; - -struct bad_date : std::exception { - char const * what() const noexcept override { return "bad_date"; } -}; - - -struct week_day -{ - constexpr week_day(int d) - : d_{(d < 0 or d > 6) ? throw bad_date{} : d} - { - } - -private: - int d_; - - friend struct ::gregorian::date; - friend day operator*(detail::spec s, week_day wd); - friend day operator*( int n, week_day wd); -}; - - -struct day { - constexpr day(int d) - : d_{(d < 1 or d > 31) ? throw bad_date{} : (unsigned char)d} - { - } - - day(detail::spec s) noexcept : d_{(unsigned char)s.id_} {} - -private: - unsigned char d_; - - friend struct gregorian::date; - friend day operator*(detail::spec s, week_day wd); - friend day operator*( int n, week_day wd); -}; - - -struct month { - constexpr month(int m) noexcept : value{m} {} - int value; -}; - - -struct year { - constexpr year(int y) noexcept : value{y} {} - int value; -}; - - -static constexpr week_day sun{0}; -static constexpr week_day mon{1}; -static constexpr week_day tue{2}; -static constexpr week_day wed{3}; -static constexpr week_day thu{4}; -static constexpr week_day fri{5}; -static constexpr week_day sat{6}; - -static constexpr month jan{1}; -static constexpr month feb{2}; -static constexpr month mar{3}; -static constexpr month apr{4}; -static constexpr month may{5}; -static constexpr month jun{6}; -static constexpr month jul{7}; -static constexpr month aug{8}; -static constexpr month sep{9}; -static constexpr month oct{10}; -static constexpr month nov{11}; -static constexpr month dec{12}; - -namespace detail -{ - -struct day_month_spec { - constexpr day_month_spec(day d, month m); - -private: - day d_; - month m_; - - friend struct gregorian::date; -}; - -inline constexpr -day_month_spec::day_month_spec(day d, month m) - : d_{d} - , m_{m} - { -} - -struct month_year_spec -{ - constexpr month_year_spec(month m, year y); - -private: - month m_; - year y_; - - friend struct gregorian::date; -}; - -inline constexpr -month_year_spec::month_year_spec(month m, year y) - : m_{m} - , y_{y} - { -} - -} // detail - -date operator+(date const &, month); -date operator+(date const &, year); - -struct date { - date(); - date(detail::day_month_spec dm, gregorian::year y); - date(gregorian::day d, detail::month_year_spec my); - - constexpr int day() const noexcept {return day_ & 0x1F;} - constexpr int month() const noexcept {return month_ & 0x0F;} - constexpr int year() const noexcept {return year_;} - constexpr int day_of_week() const noexcept {return static_cast((jdate_+3) % 7);} - bool is_leap() const noexcept; - - date& operator+=(int d); - date& operator++() {return *this += 1;} - date operator++(int) {date tmp(*this); *this += 1; return tmp;} - date& operator-=(int d) {return *this += -d;} - date& operator--() {return *this -= 1;} - date operator--(int) {date tmp(*this); *this -= 1; return tmp;} - friend date operator+(const date& x, int y) {date r(x); r += y; return r;} - friend date operator+(int x, const date& y) {return y + x;} - friend date operator-(const date& x, int y) {date r(x); r += -y; return r;} - - date& operator+=(gregorian::month m) {*this = *this + m; return *this;} - date& operator-=(gregorian::month m) {return *this += gregorian::month(-m.value);} - friend date operator+(gregorian::month m, const date& y) {return y + m;} - friend date operator-(const date& x, gregorian::month m) {date r(x); r -= m; return r;} - - date& operator+=(gregorian::year y) {*this = *this + y; return *this;} - date& operator-=(gregorian::year y) {return *this += gregorian::year(-y.value);} - friend date operator+(gregorian::year y, const date& x) {return x + y;} - friend date operator-(const date& x, gregorian::year y) {date r(x); r -= y; return r;} - - friend constexpr long operator- (const date& x, const date& y) noexcept {return (long)(x.jdate_ - y.jdate_);} - friend constexpr bool operator==(const date& x, const date& y) noexcept {return x.jdate_ == y.jdate_;} - friend constexpr bool operator!=(const date& x, const date& y) noexcept {return x.jdate_ != y.jdate_;} - friend constexpr bool operator< (const date& x, const date& y) noexcept {return x.jdate_ < y.jdate_;} - friend constexpr bool operator<=(const date& x, const date& y) noexcept {return x.jdate_ <= y.jdate_;} - friend constexpr bool operator> (const date& x, const date& y) noexcept {return x.jdate_ > y.jdate_;} - friend constexpr bool operator>=(const date& x, const date& y) noexcept {return x.jdate_ >= y.jdate_;} - -private: - - constexpr date(int y, int m, int d) - : jdate_{} - , year_{(unsigned short)y} - , month_{(unsigned char)m} - , day_{(unsigned char)d} - { - } - - void init(); - void fix_from_ymd(); - void fix_from_jdate(); - void decode(int& dow, int& n) const noexcept; - void encode(int dow, int n) noexcept; - - friend date operator+(const date&, gregorian::month); - friend date operator+(const date&, gregorian::year); - -private: - unsigned long jdate_; - unsigned short year_; - unsigned char month_; - unsigned char day_; - -// static const unsigned char lastDay_s[12]; -}; - -//constexpr unsigned char date::lastDay_s[12] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -auto operator*(detail::spec s, week_day wd) -> day; -auto operator*(int n, week_day wd) -> day; - - -inline constexpr -auto operator/(day d, month m) -> detail::day_month_spec { - return detail::day_month_spec{d, m}; -} - -inline constexpr -auto operator/(month m, day d) -> detail::day_month_spec { - return detail::day_month_spec{d, m}; -} - -inline constexpr -auto operator/(year y, month m) -> detail::month_year_spec { - return detail::month_year_spec{m, y}; -} - -inline -auto operator/(detail::day_month_spec dm, year y) -> date { - return date{dm, y}; -} - -inline -auto operator/(detail::month_year_spec my, day d) -> date { - return date{d, my}; -} - - -namespace detail -{ - -inline -auto operator/(spec d, int m) -> detail::day_month_spec { - return day{d} / month{m}; -} - -inline -auto operator/(int m, spec d) -> detail::day_month_spec { - return day{d} / month{m}; -} - -inline -auto operator/(detail::day_month_spec dm, int y) -> date { - return date{dm, year{y}}; -} - -inline -auto operator/(detail::month_year_spec my, int d) -> date { - return date{day{d}, my}; -} - -} // detail - -template -auto operator >>(std::basic_istream & is, date & item) -> std::basic_istream & { - typename std::basic_istream::sentry ok(is); - if (ok) { - std::ios_base::iostate err = std::ios_base::goodbit; - try { - std::time_get const & tg = std::use_facet >(is.getloc()); - std::tm t; - tg.get_date(is, 0, is, err, &t); - if (!(err & std::ios_base::failbit)) { - item = date(month(t.tm_mon+1) / day(t.tm_mday) / year(t.tm_year+1900)); - } - } - catch (...) { - err |= std::ios_base::badbit | std::ios_base::failbit; - } - is.setstate(err); - } - return is; -} - -template -auto operator <<(std::basic_ostream & os, date const & item) -> std::basic_ostream & { - typename std::basic_ostream::sentry ok{os}; - if (ok) { - bool failed; - try { - std::time_put const & tp = std::use_facet >(os.getloc()); - std::tm t; - t.tm_mday = item.day(); - t.tm_mon = item.month() - 1; - t.tm_year = item.year() - 1900; - t.tm_wday = item.day_of_week(); - charT pattern[2] = {'%', 'x'}; - failed = tp.put(os, os, os.fill(), &t, pattern, pattern+2).failed(); - } - catch (...) { - failed = true; - } - - if (failed) { - os.setstate(std::ios_base::failbit | std::ios_base::badbit); - } - } - return os; -} - -} // gregorian - -#endif // DATE_H diff --git a/src/issues.cpp b/src/issues.cpp index 550901a410..dbeb7904cf 100644 --- a/src/issues.cpp +++ b/src/issues.cpp @@ -26,25 +26,13 @@ namespace fs = std::filesystem; namespace { -// date utilites may factor out again -auto parse_month(std::string const & m) -> gregorian::month { - // This could be turned into an efficient map lookup with a suitable indexed container - return (m == "Jan") ? gregorian::jan - : (m == "Feb") ? gregorian::feb - : (m == "Mar") ? gregorian::mar - : (m == "Apr") ? gregorian::apr - : (m == "May") ? gregorian::may - : (m == "Jun") ? gregorian::jun - : (m == "Jul") ? gregorian::jul - : (m == "Aug") ? gregorian::aug - : (m == "Sep") ? gregorian::sep - : (m == "Oct") ? gregorian::oct - : (m == "Nov") ? gregorian::nov - : (m == "Dec") ? gregorian::dec - : throw std::runtime_error{"unknown month abbreviation " + m}; -} - -auto parse_date(std::istream & temp) -> gregorian::date { +auto parse_date(std::istream & temp) -> std::chrono::year_month_day { +#if __cpp_lib_chrono >= 201803L + std::chrono::year_month_day date{}; + if (temp >> std::chrono::parse(" %d %b %Y", date)) + return date; + throw std::runtime_error{"date format error"}; +#else int d; temp >> d; if (temp.fail()) { @@ -54,29 +42,34 @@ auto parse_date(std::istream & temp) -> gregorian::date { std::string month; temp >> month; - auto m = parse_month(month); + std::map months{ + {"Jan", 1}, {"Feb", 2}, {"Mar", 3}, {"Apr", 4}, {"May", 5}, {"Jun", 6}, + {"Jul", 7}, {"Aug", 8}, {"Sep", 9}, {"Oct",10}, {"Nov",11}, {"Dec",12} + }; + int y{ 0 }; temp >> y; - return m/gregorian::day{d}/y; -} - -auto make_date(std::tm const & mod) -> gregorian::date { - return gregorian::year((unsigned short)(mod.tm_year+1900)) / (mod.tm_mon+1) / mod.tm_mday; + return std::chrono::year{y}/months[month]/d; +#endif } -auto report_date_file_last_modified(std::filesystem::path const & filename, lwg::metadata const& meta) -> gregorian::date { - std::time_t mtime; +auto report_date_file_last_modified(std::filesystem::path const & filename, lwg::metadata const& meta) -> std::chrono::year_month_day { + using namespace std::chrono; + system_clock::time_point t; int id = std::stoi(filename.filename().stem().native().substr(5)); if (auto it = meta.git_commit_times.find(id); it != meta.git_commit_times.end()) - mtime = it->second; - else - { - auto file_mtime = fs::last_write_time(filename); - auto sys_mtime = std::chrono::system_clock::now() - (fs::file_time_type::clock::now() - file_mtime); - mtime = std::chrono::duration_cast(sys_mtime.time_since_epoch()).count(); + t = system_clock::from_time_t(it->second); + else { + auto mtime = fs::last_write_time(filename); +#if __cpp_lib_chrono >= 201803L + t = clock_cast(mtime); +#else + auto fnow = fs::file_time_type::clock::now(); + t = system_clock::now() - round(fnow - mtime); +#endif } - return make_date(*std::gmtime(&mtime)); + return year_month_day(floor(t)); } // Replace '<' and '>' and '&' with HTML character references. @@ -246,14 +239,14 @@ auto lwg::parse_issue_from_file(std::string tx, std::string const & filename, try { std::istringstream temp{tx.substr(k, l-k)}; is.date = parse_date(temp); - - // Get modification date - is.mod_date = report_date_file_last_modified(filename, meta); } catch(std::exception const & ex) { - throw bad_issue_file{filename, ex.what()}; + throw bad_issue_file{filename, "date format error"}; } + // Get modification date + is.mod_date = report_date_file_last_modified(filename, meta); + // Get priority - this element is optional k = tx.find("", l); if (k != std::string::npos) { diff --git a/src/issues.h b/src/issues.h index 0bead73792..8981b8f995 100644 --- a/src/issues.h +++ b/src/issues.h @@ -2,18 +2,19 @@ #define INCLUDE_LWG_ISSUES_H // standard headers +#include #include #include #include #include // solution specific headers -#include "date.h" #include "metadata.h" #include "status.h" namespace lwg { +namespace chrono = std::chrono; struct issue { int num; // ID - issue number @@ -22,8 +23,8 @@ struct issue { std::string doc_prefix; // extracted from title; e.g. filesys.ts std::vector tags; // section(s) of the standard affected by the issue std::string submitter; // original submitter of the issue - gregorian::date date; // date the issue was filed - gregorian::date mod_date; // date the issue was last changed + chrono::year_month_day date; // date the issue was filed + chrono::year_month_day mod_date; // date the issue was last changed std::set duplicates; // sorted list of duplicate issues, stored as html anchor references. std::string text; // text representing the issue int priority = 99; // severity, 1 = critical, 4 = minor concern, 0 = trivial to resolve, 99 = not yet prioritised diff --git a/src/list_issues.cpp b/src/list_issues.cpp index 09e3b20124..bc8874108d 100644 --- a/src/list_issues.cpp +++ b/src/list_issues.cpp @@ -18,7 +18,7 @@ // . sort-by-last-modified-date should offer some filter or separation to see only the issues modified since the last meeting // Missing standard facilities that we work around -// . Date +// . (none at present) // Missing standard library facilities that would probably not change this program // . XML parser diff --git a/src/lists.cpp b/src/lists.cpp index 96e476e5e9..12b7d8c2d8 100644 --- a/src/lists.cpp +++ b/src/lists.cpp @@ -18,7 +18,7 @@ // . sort-by-last-modified-date should offer some filter or separation to see only the issues modified since the last meeting // Missing standard facilities that we work around -// . Date +// . (none at present) // Missing standard library facilities that would probably not change this program // . XML parser @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -46,7 +47,6 @@ namespace fs = std::filesystem; // solution specific headers -#include "date.h" #include "html_utils.h" #include "issues.h" #include "mailing_info.h" diff --git a/src/report_generator.cpp b/src/report_generator.cpp index cd14fbada7..8f83727455 100644 --- a/src/report_generator.cpp +++ b/src/report_generator.cpp @@ -10,6 +10,8 @@ #include #include +#include +#include #include #include #include @@ -23,28 +25,11 @@ namespace // Generic utilities that are useful and do not rely on context or types from our domain (issue-list processing) // ============================================================================================================= -auto format_time(std::string const & format, std::tm const & t) -> std::string { - std::string s; - std::size_t maxsize{format.size() + 256}; - //for (std::size_t maxsize = format.size() + 64; s.size() == 0 ; maxsize += 64) - //{ - std::unique_ptr buf{new char[maxsize]}; - std::size_t size{std::strftime( buf.get(), maxsize, format.c_str(), &t ) }; - if(size > 0) { - s += buf.get(); - } - // } - return s; -} - -auto utc_timestamp() -> std::tm const & { - static std::time_t t{ std::time(nullptr) }; - static std::tm utc = *std::gmtime(&t); - return utc; -} +auto const timestamp{std::chrono::floor(std::chrono::system_clock::now())}; // global data - would like to do something about that. -std::string const build_timestamp{format_time("Revised %Y-%m-%d at %H:%M:%S UTC\n", utc_timestamp())}; +std::string const build_date{std::format("{:%F}", timestamp)}; +std::string const build_timestamp{std::format("Revised {} at {:%T} UTC\n", build_date, timestamp)}; std::string const maintainer_email{"lwgchair@gmail.com"}; @@ -152,14 +137,6 @@ auto major_section(lwg::section_num const & sn) -> std::string { return out.str(); } -void print_date(std::ostream & out, gregorian::date const & mod_date) { - out << mod_date.year() << '-'; - if (mod_date.month() < 10) { out << '0'; } - out << mod_date.month() << '-'; - if (mod_date.day() < 10) { out << '0'; } - out << mod_date.day(); -} - template void print_list(std::ostream & out, Container const & source, char const * separator) { char const * sep{""}; @@ -363,11 +340,9 @@ void print_issue(std::ostream & out, lwg::issue const & iss, lwg::section_map & out << " Status: " << iss.stat << "\n"; out << " Submitter: " << iss.submitter - << " Opened: "; - print_date(out, iss.date); - out << " Last modified: "; - print_date(out, iss.mod_date); - out << "

\n"; + << " Opened: " << iss.date + << " Last modified: " << iss.mod_date + << "

\n"; // priority out << "

Priority: "; @@ -459,7 +434,7 @@ R"( - + From 9a5d45398e3f2064ee63050c6b6d34261f6d7668 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Mon, 4 Nov 2024 12:57:29 +0000 Subject: [PATCH 4/7] Update Prerequisites in how-to-docs --- how-to-docs.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/how-to-docs.html b/how-to-docs.html index 4f280e75db..d1f4807a71 100644 --- a/how-to-docs.html +++ b/how-to-docs.html @@ -25,7 +25,7 @@

How To Instructions

Prerequisites

Date:)" << format_time("%Y-%m-%d", utc_timestamp()) << R"()" << build_date << R"(
Project: