diff --git a/SConstruct b/SConstruct index 75e6383..b7c9780 100644 --- a/SConstruct +++ b/SConstruct @@ -80,6 +80,8 @@ env.Alias(target='install', source=env.InstallVersionedLib(dir=os.path.join(pref ## Tests ... if test: + env.Append(CXXFLAGS=' -Wno-error=inline') + if '-Winline' in env['CXXFLAGS']: env['CXXFLAGS'].replace('-Winline','') cmp_error_fn = 'test/unit_tests/test_compilation_error.json' cerror_dct = {} if os.path.isfile(cmp_error_fn): os.remove(cmp_error_fn) diff --git a/src/dtfund.hpp b/src/dtfund.hpp index 02f57d6..bafc273 100644 --- a/src/dtfund.hpp +++ b/src/dtfund.hpp @@ -688,7 +688,8 @@ class day_of_year { * If users want to check the instance for validity, then they should use the * ymd_date::is_valid function. */ -struct ymd_date { +class ymd_date { +public: /** @brief ymd_date constructor * No check for validity will be performed. If you want to check the * validity of the created instance, use ymd_date::is_valid @@ -705,11 +706,27 @@ struct ymd_date { } /** @brief Transform to year and day-of-year - * Note that no validation checks are performed on the instance. If needed, - * (e.g. before the conversion), use the is_valid method on the instance. + * The function will first check that the instance is a valid date, before + * performing the transformation (to year and day of year). This is done + * because an invalid ymd_date can result in a seamingly valid ydoy_date + * (e.g. constructing a 29/2 date on a non-leap year). */ - ydoy_date to_ydoy() const noexcept; + ydoy_date to_ydoy() const; + + /** get/set year */ + constexpr year &yr() noexcept {return __year;} + /** get/set month */ + constexpr month &mn() noexcept {return __month;} + /** get/set day of month */ + constexpr day_of_month &dm() noexcept {return __dom;} + /** get year */ + constexpr year yr() const noexcept {return __year;} + /** get month */ + constexpr month mn() const noexcept {return __month;} + /** get day of month */ + constexpr day_of_month dm() const noexcept {return __dom;} +private: year __year; /** the year */ month __month; /** the month */ day_of_month __dom; /** day of month */ @@ -723,7 +740,8 @@ struct ymd_date { * If users want to check the instance for validity, then they should use the * ymd_date::is_valid function. */ -struct ydoy_date { +class ydoy_date { +public: /** @brief ymd_date constructor * No check for validity will be performed. If you want to check the * validity of the created instance, use ymd_date::is_valid @@ -731,6 +749,13 @@ struct ydoy_date { constexpr ydoy_date(year y = year{}, day_of_year d = day_of_year{}) noexcept : __year(y), __doy(d) {} + /** @brief Constructor from a Year/Month/DayOfMonth instance + * In case the input argument \p ymd is not a valid date, the constructor + * will throw. + */ + ydoy_date(const ymd_date &ymd) + : __year(ymd.yr()), __doy(ymd.to_ydoy().dy()) {} + /** @brief Check if the date is a valid calendar date * @return True if the date is valid, false otherwise. */ @@ -753,7 +778,17 @@ struct ydoy_date { (double)days_in_year; } } + + /** get/set year */ + year &yr() noexcept {return __year;} + /** get/set day of year */ + day_of_year &dy() noexcept {return __doy;} + /** get year */ + constexpr year yr() const noexcept {return __year;} + /** get day of year */ + constexpr day_of_year dy() const noexcept {return __doy;} +private: year __year; /** the year */ day_of_year __doy; /** day of year */ }; /* ydoy_date */ diff --git a/src/ydoy_date.cpp b/src/ydoy_date.cpp index d97e176..b9ee6ea 100644 --- a/src/ydoy_date.cpp +++ b/src/ydoy_date.cpp @@ -1,15 +1,15 @@ #include "dtfund.hpp" constexpr dso::ymd_date dso::ydoy_date::to_ymd() const noexcept { - ymd_date yd; int guess = static_cast(__doy.as_underlying_type() * 0.032); - int leap = __year.is_leap(); - int more = ((__doy.as_underlying_type() - + int leap = yr().is_leap(); + int more = ((dy().as_underlying_type() - dso::core::month_day[leap][guess + 1]) > 0); /* assign */ - yd.__year = __year; - yd.__month = month{guess + more + 1}; - yd.__dom = day_of_month(__doy.as_underlying_type() - + ymd_date yd; + yd.yr() = yr(); + yd.mn() = month(guess + more + 1); + yd.dm() = day_of_month(dy().as_underlying_type() - dso::core::month_day[leap][guess + more]); return yd; } diff --git a/src/ymd_date.cpp b/src/ymd_date.cpp index 75c1929..567a8f8 100644 --- a/src/ymd_date.cpp +++ b/src/ymd_date.cpp @@ -1,8 +1,15 @@ #include "dtfund.hpp" +#include -dso::ydoy_date dso::ymd_date::to_ydoy() const noexcept { - int leap = __year.is_leap(); - int md = __month.as_underlying_type() - 1; - return dso::ydoy_date(__year, dso::day_of_year(core::month_day[leap][md] + - __dom.as_underlying_type())); +dso::ydoy_date dso::ymd_date::to_ydoy() const { + if (!is_valid()) { + throw std::invalid_argument( + "[ERROR] Tring to compute year/day_of_year from an invalid " + "year/month/day instance (traceback:" + + std::string(__func__) + ")\n"); + } + int leap = yr().is_leap(); + int md = mn().as_underlying_type() - 1; + return dso::ydoy_date(yr(), dso::day_of_year(core::month_day[leap][md] + + dm().as_underlying_type())); } diff --git a/test/unit_tests/ymd_date.cpp b/test/unit_tests/ymd_date.cpp index f0683f6..318e9df 100644 --- a/test/unit_tests/ymd_date.cpp +++ b/test/unit_tests/ymd_date.cpp @@ -7,21 +7,46 @@ using dso::day_of_month; using dso::month; using dso::year; using dso::ymd_date; +using dso::ydoy_date; +using dso::day_of_year; int main() { + /* note that we can construct an invali date */ + ymd_date d1(year(2023), month(13), day_of_month(56)); + /* however, we can always validate a ymd_date instance */ + assert(!(d1.is_valid())); + for (int y = 1804; y <= 2400; y++) { if (dso::year(y).is_leap()) { /* leap year, should have a valid date for YYYY/02/29 */ ymd_date ymd(year(y), month(2), day_of_month(29)); assert(ymd.is_valid()); + /* transform to Year - Day of Year */ + const ydoy_date ydoy (ymd); + assert((ydoy.yr() == year(y)) && (ydoy.dy() == day_of_year(31 + 29))); + assert(ydoy.is_valid()); } else { /* non-leap year, should not have a valid date for YYYY/02/29 */ ymd_date iymd(year(y), month(2), day_of_month(29)); assert(!(iymd.is_valid())); + /* transform to Year - Day of Year; since this is an invalid date, the + * construction should throw! + */ + try { + const ydoy_date iydoy(iymd); + /* shit,it didn't throw*/ + assert(1 == 2); + } catch (std::exception &) { + ; /* good, it throwed */ + } /* ...but should have a valid date for YYYY/02/28 */ ymd_date vymd(year(y), month(2), day_of_month(28)); assert(vymd.is_valid()); + /* transform to Year - Day of Year */ + const ydoy_date vydoy (vymd); + assert((vydoy.yr() == year(y)) && (vydoy.dy() == day_of_year(31 + 28))); + assert(vydoy.is_valid()); } }