From 5fab73caaa593c334290d2298791700333858a79 Mon Sep 17 00:00:00 2001 From: "xanthos@durruti" Date: Mon, 21 Oct 2024 14:16:56 +0300 Subject: [PATCH] more --- include/tpdate.hpp | 300 +++++++++++------- src/lib/tpdateutc.cpp | 3 +- .../fractional_seconds_op_equal.cpp | 2 +- .../fractional_seconds_op_greater.cpp | 2 +- .../fund_type_op_plusequal.cpp | 3 +- test/sofa/epj_date.cpp | 2 +- test/unit_tests/dread3.cpp | 8 +- test/unit_tests/tpdate_add.cpp | 16 +- 8 files changed, 196 insertions(+), 140 deletions(-) diff --git a/include/tpdate.hpp b/include/tpdate.hpp index a50efc4..da5215d 100644 --- a/include/tpdate.hpp +++ b/include/tpdate.hpp @@ -1,14 +1,21 @@ /** @file - * A utility class to hold datetime instances, in a continuous time-scale - * (e.g. TT, TAI, etc). In construst to datetime, this is not a template - * class and uses a storage method of two numeric values (one for MJD and one - * for seconds of day) to represent datetime epochs. + * + * Define classes TwoPartDate and TwoPartDateUTC. These are classes to + * represent time-points, i.e. epochs or instances in the datetime domain. The + * difference between the two, is that the first (TwoPartDate) is meant to + * work on a 'continuous time basis' e.g. in time scales such as TAI, or TT. + * The latter, i.e. TwoPartDateUTC is meant to be used solely to represent + * intances in the UTC time-scale. This needs special treatment cause of the + * leap seconds that are introduced, making the time-scale non-continuous. + * + * Both classes store the datetime instances as a mixture of (integral) MJDay + * and (floating point) seconds of day. */ #ifndef __DSO_DATETIME_TWOPARTDATES2_HPP__ #define __DSO_DATETIME_TWOPARTDATES2_HPP__ -#include "dtdatetime.hpp" +#include "datetime_utc.hpp" #include namespace dso { @@ -16,17 +23,33 @@ namespace dso { /** forward decleration */ class TwoPartDate; +/** A datetime class to represent epochs in UTC time system. + * + * A TwoPartDate instance conviniently splits a datetime into two numeric + * values: + * - the Modified Julian Day (i.e. an integral value), and + * - the time of day, which is stored in fractional seconds of day + * + * The methods of the class, including constructors, take special care to + * always keep the seconds as seconds of day, i.e. in the range [0,86400) and + * correspondingly increase/decrease the day count. + * + */ class TwoPartDateUTC { private: using FDOUBLE = /*long*/ double; - int _mjd; /** Mjd */ - FDOUBLE _fsec; /** fractional seconds of day */ + /** MJDay */ + int _mjd; + /** fractional seconds of day. */ + FDOUBLE _fsec; - /** Check the integral (MJD) part of the date; if we are on a leap + /** @brief Return 1 or 0 depending on leap seconds. + * + * Check the integral (MJD) part of the date; if we are on a leap * insertation day, return 1, otherwise return 0 */ - int extra_seconds_in_day() const noexcept { - int extra; + constexpr int extra_seconds_in_day() const noexcept { + int extra{0}; dat(modified_julian_day(_mjd), extra); return extra; } @@ -38,7 +61,7 @@ class TwoPartDateUTC { * 2. The seconds of day part (fractional) * Note that the fractional seconds part may be over 86400. */ - int utc2tai(FDOUBLE &taisec) const noexcept { + constexpr int utc2tai(FDOUBLE &taisec) const noexcept { taisec = _fsec + dat(modified_julian_day(_mjd)); return _mjd; } @@ -50,18 +73,28 @@ class TwoPartDateUTC { * 2. The seconds of day part (fractional) * Note that the fractional seconds part may be over 86400. */ - int utc2tt(FDOUBLE &taisec) const noexcept { + constexpr int utc2tt(FDOUBLE &taisec) const noexcept { constexpr const FDOUBLE dtat = TT_MINUS_TAI; taisec = _fsec + (dat(modified_julian_day(_mjd)) + dtat); return _mjd; } + /** @brief Constructor from MJDay (integral) and seconds of day. */ explicit TwoPartDateUTC(int mjd, FDOUBLE secday) noexcept : _mjd(mjd), _fsec(secday) { normalize(); } - /** Add seconds to instance, taking into account leap seconds. + /** @brief Constructor from MJDay (integral) and seconds of day. + * + * @warning This constructor will not call normalize! Be sure that the + * input arguments are correct, i.e. secday is indeed seconds of day. + * */ + constexpr explicit TwoPartDateUTC(int mjd, FDOUBLE secday, + [[maybe_unused]] char c) noexcept + : _mjd(mjd), _fsec(secday) {} + + /** @brief Add seconds to instance, taking into account leap seconds. */ void add_seconds(FDOUBLE sec) noexcept { _fsec += sec; @@ -75,26 +108,47 @@ class TwoPartDateUTC { #else template > #endif - TwoPartDateUTC(const datetime &d) noexcept + constexpr TwoPartDateUTC(const datetime &d) noexcept : _mjd(d.imjd().as_underlying_type()), _fsec(to_fractional_seconds(d.sec()).seconds()) { - this->normalize(); } - /** Constructor from a pair of doubles, such that MJD = a + b */ + /** @brief Constructor from MJDays (integral) and seconds of day. */ explicit TwoPartDateUTC(int b = 0, FractionalSeconds s = FractionalSeconds{0}) noexcept : _mjd(b), _fsec(s.seconds()) { - if (b != 0 && - s.seconds() != 0e0) /* do not normalize for default constructor! */ + /* do not normalize for default constructor! */ + if (b != 0 && s.seconds() != 0e0) this->normalize(); } - /** Get the MJD as an intgral number, i.e. no fractional part */ - constexpr int imjd() const noexcept { return _mjd; } + /** @brief Reference epoch (J2000.0), as a Modified Julian Date. */ + static constexpr TwoPartDateUTC j2000_mjd() noexcept { + return TwoPartDateUTC(51544, 86400e0 / 2e0, 'y'); + } + + /** @brief Min date. This is the same as datetime::min(). */ + static constexpr TwoPartDateUTC min() noexcept { + return TwoPartDateUTC(datetime::min()); + } + + /** @brief Max date. This is the same as datetime::max(). */ + static constexpr TwoPartDateUTC max() noexcept { + return TwoPartDateUTC(datetime::max()); + } - /** Get the fractional seconds of the MJD */ - constexpr FDOUBLE seconds() const noexcept { return _fsec; } + /** @brief Get the MJD as an intgral number, i.e. no fractional part */ + int imjd() const noexcept { return _mjd; } + + /** @brief Get the (fractional) seconds of the MJD. Always in [0, 86400). */ + FractionalSeconds seconds() const noexcept { + return FractionalSeconds(_fsec); + } + + /** @brief Get the seconds of the MJD as fractional day. Always in [0,1) */ + FractionalDays fractional_days() const noexcept { + return FractionalDays(_fsec / (86400e0 + this->extra_seconds_in_day())); + } /** @brief Get the fractional seconds of day as some multiple of seconds. * @@ -116,14 +170,14 @@ class TwoPartDateUTC { template > #endif constexpr FDOUBLE sec_of_day() const noexcept { - return seconds() * T::template sec_factor(); + // return TwoPartDate(_mjd, _fsec, 'c').sec_of_day(); + return seconds().seconds() * T::template sec_factor(); } /** @brief Transform the (integral part of the) date to Year Month Day */ ymd_date to_ymd() const noexcept { return core::mjd2ymd((long)_mjd); } - /** Add seconds to instance, taking into account leap seconds. - */ + /** @brief Add seconds to instance, taking into account leap seconds. */ void add_seconds(FractionalSeconds fsec) noexcept { this->add_seconds(fsec.seconds()); } @@ -153,9 +207,9 @@ class TwoPartDateUTC { * be used at next iteration. If this is the first call, * set err to 0e0. */ - void add_seconds(FDOUBLE sec, FDOUBLE &err) noexcept { + void add_seconds(FractionalSeconds sec, FDOUBLE &err) noexcept { FDOUBLE a = _fsec; - FDOUBLE b = sec; + FDOUBLE b = sec.seconds(); FDOUBLE y = b - err; b = a + y; err = (b - a) - y; @@ -171,7 +225,9 @@ class TwoPartDateUTC { * This function will take into consideration the leap seconds between the * two days. * Note that the whole days returned are days of 86400[sec] aka **NOT** - * always UTC days + * UTC days. This is because this (i.e. the returned instance) is actually + * a time interval, not a datetime! + * * Example: * a leap insertion date * d1 = TwoPartDateUTC(ymd_date{year(2016), month(12), day_of_month(31)) @@ -181,45 +237,24 @@ class TwoPartDateUTC { */ TwoPartDate operator-(const TwoPartDateUTC &d) const noexcept; - bool operator>(const TwoPartDateUTC &d) const noexcept { - return (_mjd > d._mjd) || ((_mjd == d._mjd) && (_fsec > d._fsec)); - } - bool operator>=(const TwoPartDateUTC &d) const noexcept { - return (_mjd > d._mjd) || ((_mjd == d._mjd) && (_fsec >= d._fsec)); - } - bool operator<(const TwoPartDateUTC &d) const noexcept { - return (_mjd < d._mjd) || ((_mjd == d._mjd) && (_fsec < d._fsec)); - } - bool operator<=(const TwoPartDateUTC &d) const noexcept { - return (_mjd < d._mjd) || ((_mjd == d._mjd) && (_fsec <= d._fsec)); - } - - /** @brief Equality operator */ - bool operator==(const TwoPartDateUTC &d) const noexcept { - return (_mjd == d._mjd) && (_fsec == d._fsec); - } - - /** @brief In-equality operator */ - bool operator!=(const TwoPartDateUTC &d) const noexcept { - return !(this->operator==(d)); - } - /** @brief Normalize an instance. * * Normalize here is meant in the sense that the fractional seconds of day - * (\p _fsec) are always in the range [0,86400), while the integral part - * (\p _mjd) is always a valid integer. + * (\p _fsec) are always in the range [0,86400+leap_insertion_secs), while + * the integral part (\p _mjd) is always a valid integer. * The only case where negative seconds are allowed, is when the MJD part is * zero. In this case, the seconds of day are allowed to be negative, so * that they can hold the sign. */ void normalize() noexcept { + if (_fsec >= 0e0 && _fsec < 86400e0) + return; assert(_fsec >= 0e0); int extra_sec_in_day; dat(modified_julian_day(_mjd), extra_sec_in_day); /* for each MJD, remove integral days. Each MJD may have a different * number of seconds, since we are in UTC time scale. Hence, iteratively - * remove whole days using the number of seconds for each day + * remove whole days using the number of seconds for each day. */ while (_fsec >= 86400e0 + extra_sec_in_day) { _fsec -= (86400e0 + extra_sec_in_day); @@ -236,7 +271,7 @@ class TwoPartDateUTC { return; } - /** @brief Transform a UTC date to a TAI date */ + /** @brief Transform a UTC date to a TAI date, via TAI = UTC + ΔAT. */ TwoPartDate utc2tai() const noexcept; /** @brief Transform a UTC date to a TT date */ @@ -251,7 +286,7 @@ class TwoPartDateUTC { * - the Modified Julian Day (i.e. an integral value), and * - the time of day, which is stored in fractional seconds of day * - * The methods of the class, invluding constructors, take special care to + * The methods of the class, including constructors, take special care to * always keep the seconds as seconds of day, i.e. in the range [0,86400) and * correspondingly increase/decrease the day count. * @@ -262,56 +297,64 @@ class TwoPartDate { int _mjd; /** Mjd */ FDOUBLE _fsec; /** fractional seconds of day */ - /** Construct from MJD and fractional seconds **of day**. - * - * A constexpr constructor that will not check arguments, and will NOT - * normalize the date. Be very carefull with this one! - */ - constexpr explicit TwoPartDate(int mjd, FDOUBLE secday, - [[maybe_unused]] char c) noexcept - : _mjd(mjd), _fsec(secday) {} - - /** Construct from MJD and fractional seconds. + /** @brief Construct from MJD and fractional seconds. * * This is only private and should be used in rare cases. Normal users, * should explicitely cast the second argument to FractionalSeconds to - * avoid misconceptions (i.e. is the parameters fractional seconds or - * fractional days?). + * avoid misconceptions (i.e. avoid ambiguous parameters, fractional seconds + * or fractional days, or ...). */ - explicit TwoPartDate(int mjd, FDOUBLE secday) noexcept - : _mjd(mjd), _fsec(secday) { + TwoPartDate(int mjd, FDOUBLE secday) noexcept : _mjd(mjd), _fsec(secday) { normalize(); } - /** Add seconds to instance. - * @warning Does not take into account leap seconds. + /** @brief Add seconds to instance. + * + * This version is private; users should use the + * add_seconds(FractionalSeconds ...) version, which enforces type safety. */ void add_seconds(FDOUBLE sec) noexcept { _fsec += sec; this->normalize(); } +protected: + /** @brief Construct from MJD and fractional seconds **of day**. + * + * A constexpr constructor that will not check arguments, and will NOT + * normalize the date. Be very carefull with this one! + * The char parameter actually does nothing, but is there to discriminate + * this constructor from the 'normalizing' ones. + * + * @wanrning Will not call nomralize, given secday should be in range + * [0. 86400) + */ + constexpr TwoPartDate(int mjd, FDOUBLE secday, + [[maybe_unused]] char c) noexcept + : _mjd(mjd), _fsec(secday) {} + public: - /** Constructor from datetime + /** @brief Constructor from datetime. + * * Note that we are not (explicitly) normalizing the instance here, because * the parameter \p d is already considered to hold a 'normalized' datetime. - * */ + */ #if __cplusplus >= 202002L template #else template > #endif - constexpr TwoPartDate(const datetime &d) noexcept + constexpr explicit TwoPartDate(const datetime &d) noexcept : _mjd(d.imjd().as_underlying_type()), _fsec(to_fractional_seconds(d.sec()).seconds()) { } - /** Reference epoch (J2000.0), as a Modified Julian Date. */ + /** @brief Reference epoch (J2000.0), as a Modified Julian Date. */ static constexpr TwoPartDate j2000_mjd() noexcept { return TwoPartDate(51544, 86400e0 / 2e0, 'y'); } - /** Random Date within some MJD limits + /** @brief Random Date within some MJD limits * @todo transfer this into a .cpp file */ static TwoPartDate @@ -326,46 +369,53 @@ class TwoPartDate { return TwoPartDate(distr(gen), unif(gen), 'y'); } - /** Min date */ + /** @brief Min date. This is the same as datetime::min(). */ static constexpr TwoPartDate min() noexcept { return TwoPartDate(datetime::min()); } - /** Max date */ + /** @brief Max date. This is the same as datetime::max(). */ static constexpr TwoPartDate max() noexcept { return TwoPartDate(datetime::max()); } - /** Constructor from a pair of doubles, such that TODO */ + /** @brief Constructor from MJDay and FractionalSeconds. */ explicit TwoPartDate(int b = 0, FractionalSeconds s = FractionalSeconds{0}) noexcept : _mjd(b), _fsec(s.seconds()) { this->normalize(); } + /** @brief Constructor from MJDay; FractionalSeconds are set to 0. */ constexpr explicit TwoPartDate(modified_julian_day mjd) noexcept : _mjd(mjd.as_underlying_type()), _fsec(0) {}; + /** @brief Constructor from calendar date. */ explicit TwoPartDate(year y, month m, day_of_month d, double sec_of_day = 0e0) : _mjd(modified_julian_day(y, m, d).as_underlying_type()), _fsec(sec_of_day) { this->normalize(); } + /** @brief Constructor from year, day of year and time of day. */ explicit TwoPartDate(year y, day_of_year d, double sec_of_day = 0e0) : _mjd(modified_julian_day(y, d).as_underlying_type()), _fsec(sec_of_day) { this->normalize(); } - /** Get the MJD as an intgral number, i.e. no fractional part */ + /** @brief Get the MJD as an intgral number, i.e. no fractional part. */ int imjd() const noexcept { return _mjd; } - /** Get the (fractional) seconds of the MJD */ - FDOUBLE seconds() const noexcept { return _fsec; } + /** @brief Get the (fractional) seconds of the MJD. Always in [0, 86400). */ + FractionalSeconds seconds() const noexcept { + return FractionalSeconds(_fsec); + } - /** Get the seconds of the MJD as fractional day. Always in range [0,1) */ - FDOUBLE fractional_days() const noexcept { return _fsec / 86400e0; } + /** @brief Get the seconds of the MJD as fractional day. Always in [0,1). */ + FractionalDays fractional_days() const noexcept { + return FractionalDays(_fsec / 86400e0); + } /** @brief Get the fractional seconds of day as some multiple of seconds. * @@ -387,7 +437,7 @@ class TwoPartDate { template > #endif FDOUBLE sec_of_day() const noexcept { - return seconds() * T::template sec_factor(); + return seconds().seconds() * T::template sec_factor(); } /** @brief Transform the (integral part of the) date to Year Month Day */ @@ -397,17 +447,14 @@ class TwoPartDate { ydoy_date to_ydoy() const noexcept { const modified_julian_day mjd(_mjd); return mjd.to_ydoy(); - // return modified_julian_day(_mjd).to_ydoy(); } - /** Add seconds to instance. - * @warning Does not take into account leap seconds. - */ + /** @brief Add seconds to instance. */ void add_seconds(FractionalSeconds fsec) noexcept { this->add_seconds(fsec.seconds()); } - /** Add seconds to instance and return the "Kahan summation" error. + /** @brief Add seconds to instance and return the "Kahan summation" error. * * This function implements a "Kahan summation" scheme to iteratively add * seconds (to the instance). Especially in the case of adding multiple @@ -432,9 +479,9 @@ class TwoPartDate { * be used at next iteration. If this is the first call, * set err to 0e0. */ - void add_seconds(FDOUBLE sec, FDOUBLE &err) noexcept { + void add_seconds(FractionalSeconds sec, FDOUBLE &err) noexcept { FDOUBLE a = _fsec; - FDOUBLE b = sec; + FDOUBLE b = sec.seconds(); FDOUBLE y = b - err; b = a + y; err = (b - a) - y; @@ -442,8 +489,8 @@ class TwoPartDate { this->normalize(); } - /** Difference between two dates as integral number of days and seconds of - * day + /** @brief Difference between two dates as integral number of days and + * seconds of day. * * This is not an 'actual date' but rather a datetime interval, but can be * represented by a TwoPartDate instance. If the calling instance is prior to @@ -455,19 +502,19 @@ class TwoPartDate { return TwoPartDate(_mjd - d._mjd, _fsec - d._fsec); } - /** Remove integral days */ + /** @brief Remove (subtract) whole days. */ TwoPartDate operator-(const modified_julian_day days) const noexcept { return TwoPartDate(_mjd - days.as_underlying_type(), _fsec); } - /** Add integral days */ + /** @brief Add integral days. */ TwoPartDate operator+(const modified_julian_day days) const noexcept { return TwoPartDate(_mjd + days.as_underlying_type(), _fsec); } - /** Add two instances. + /** @brief Add two instances. * - * In this case, the second instance can be though of a datetime interval + * In this case, the second instance can be though of as an interval * (rather than an actual datetime instance). The right operand can be a * negative interval, which means that we are going backwards in time. */ @@ -475,7 +522,8 @@ class TwoPartDate { return TwoPartDate(_mjd + d._mjd, _fsec + d._fsec); } - /** Get the difference between two datetime instances an an arithmetic value. + /** @brief Get the difference between two datetime instances as a floating + * point value. * * The difference can be obtained as a fractional days or fractional seconds, * depending on the template parameter \p DT. @@ -485,7 +533,8 @@ class TwoPartDate { * @warning Does not take into account leap seconds. */ template - FDOUBLE diff(const TwoPartDate &d) const noexcept { + typename DateTimeDifferenceTypeTraits
::dif_type + diff(const TwoPartDate &d) const noexcept { if constexpr (DT == DateTimeDifferenceType::FractionalDays) { /* difference as fractional days */ return (_mjd - d._mjd) + (_fsec - d._fsec) / SEC_PER_DAY; @@ -494,17 +543,17 @@ class TwoPartDate { return (_fsec - d._fsec) + (_mjd - d._mjd) * SEC_PER_DAY; } else { /* difference as fractional (julian) years */ - return this->diff(d) / + return this->diff(d).days() / DAYS_IN_JULIAN_YEAR; } } - /** Get the date as (fractional) Julian Date */ + /** @brief Get the date as (fractional) Julian Date. */ FDOUBLE julian_date() const noexcept { return _fsec / SEC_PER_DAY + (_mjd + dso::MJD0_JD); } - /** @brief Transform instance to TT, assuming it is in TAI + /** @brief Transform instance to TT, assuming it is in TAI. * * The two time scales are connected by the formula: * \f$ TT = TAI + ΔT \$ where \f$ ΔT = TT - TAI = 32.184 [sec] \f$ @@ -514,7 +563,7 @@ class TwoPartDate { return TwoPartDate(_mjd, _fsec + dtat); } - /** @brief Transform an instance to TAI assuming it is in TT + /** @brief Transform an instance to TAI assuming it is in TT. * * The two time scales are connected by the formula: * \f$ TT = TAI + ΔT \$ where \f$ ΔT = TT - TAI = 32.184 [sec] \f$ @@ -524,7 +573,7 @@ class TwoPartDate { return TwoPartDate(_mjd, _fsec - dtat); } - /** @brief Transform an instance to GPS Time assuming it is in TAI + /** @brief Transform an instance to GPS Time assuming it is in TAI. * * The two time scales are connected by the formula: * \f$ TAI = GPSTime + 19 [sec] \f$ @@ -533,12 +582,12 @@ class TwoPartDate { return TwoPartDate(_mjd, _fsec - 19e0); } + /** @brief Transform an instance to TAI Time assuming it is in GPS Time. */ TwoPartDate gps2tai() const noexcept { return TwoPartDate(_mjd, _fsec + 19e0); } - /** Transform an instance to UTC assuming it is in TAI */ - /* taisec = _fsec + dat(modified_julian_day(_mjd)); */ + /** @brief Transform an instance to UTC assuming it is in TAI. */ TwoPartDateUTC tai2utc() const noexcept { FDOUBLE utcsec = _fsec - dat(modified_julian_day(_mjd)); int utcmjd = _mjd; @@ -555,13 +604,13 @@ class TwoPartDate { return TwoPartDateUTC(utcmjd, FractionalSeconds{utcsec}); } - /** Transform an instance to UTC assuming it is in TT */ + /** @brief Transform an instance to UTC assuming it is in TT */ TwoPartDateUTC tt2utc() const noexcept { const auto tai = this->tai2tt(); return tai.tai2utc(); } - /* @brief TT to UT1 MJD + /** @brief TT to UT1 MJD. * * Note that because the Earth’s rotation is slowing due to tidal friction, * and the rotation rate decreases approximately linearly with time, ∆T @@ -582,6 +631,7 @@ class TwoPartDate { return ut1; } + /** @brief TAI to UT1 MJD. */ TwoPartDate tai2ut1(FDOUBLE dut1) const noexcept { /* UT1 = TAI + ΔUT1 - ΔAT * = UTC + ΔUT1 @@ -592,8 +642,10 @@ class TwoPartDate { return ut1; } + /** @brief Return instance as fractional MJD. */ FDOUBLE as_mjd() const noexcept { return _fsec / SEC_PER_DAY + _mjd; } + /** @brief Return Julian Centuries since J2000.0 */ FDOUBLE jcenturies_sinceJ2000() const noexcept { return ((static_cast(_mjd) - J2000_MJD) + _fsec / SEC_PER_DAY) / DAYS_IN_JULIAN_CENT; @@ -601,18 +653,25 @@ class TwoPartDate { /** @brief Convert to Julian Epoch, assuming the TT time-scale. */ FDOUBLE epj() const noexcept { - return core::mjd2epj((double)imjd(), seconds() / SEC_PER_DAY); + return core::mjd2epj((double)imjd(), seconds().seconds() / SEC_PER_DAY); } + /** @brief Overload the '>' operator. */ bool operator>(const TwoPartDate &d) const noexcept { return (_mjd > d._mjd) || ((_mjd == d._mjd) && (_fsec > d._fsec)); } + + /** @brief Overload the '>=' operator. */ bool operator>=(const TwoPartDate &d) const noexcept { return (_mjd > d._mjd) || ((_mjd == d._mjd) && (_fsec >= d._fsec)); } + + /** @brief Overload the '<' operator. */ bool operator<(const TwoPartDate &d) const noexcept { return (_mjd < d._mjd) || ((_mjd == d._mjd) && (_fsec < d._fsec)); } + + /** @brief Overload the '<=' operator. */ bool operator<=(const TwoPartDate &d) const noexcept { return (_mjd < d._mjd) || ((_mjd == d._mjd) && (_fsec <= d._fsec)); } @@ -627,6 +686,8 @@ class TwoPartDate { * that they can hold the sign. */ void normalize() noexcept { + if (_fsec >= 0e0 && _fsec < 86400e0) + return; /* remove whole days from seconds and compute signed remainder */ auto srem = std::fmod(_fsec, seconds::max_in_day); int extradays = _fsec / seconds::max_in_day; @@ -652,22 +713,17 @@ class TwoPartDate { return; } - TwoPartDate normalized() const noexcept { - TwoPartDate d(*this); - d.normalize(); - return d; - } - - /** @brief Equality operator */ + /** @brief Overload equality operator. */ bool operator==(const TwoPartDate &d) const noexcept { return (_mjd == d._mjd) && (_fsec == d._fsec); } - /** @brief In-equality operator */ + /** @brief Overload in-equality operator */ bool operator!=(const TwoPartDate &d) const noexcept { return !(this->operator==(d)); } + friend class TwoPartDateUTC; }; /* class TwoPartDate */ /** @brief Julian Epoch to two-part Modified Julian Date (TT). @@ -692,9 +748,9 @@ template template > #endif inline datetime from_mjdepoch(const TwoPartDate &t) noexcept { - dso::nanoseconds nsec(static_cast( - t.seconds() * nanoseconds::sec_factor())); - return datetime(t.imjd(), cast_to(nsec)); + dso::picoseconds nsec(static_cast( + t.seconds().seconds() * picoseconds::sec_factor())); + return datetime(t.imjd(), cast_to(nsec)); } } /* namespace dso */ diff --git a/src/lib/tpdateutc.cpp b/src/lib/tpdateutc.cpp index 05962fa..2df65c1 100644 --- a/src/lib/tpdateutc.cpp +++ b/src/lib/tpdateutc.cpp @@ -1,9 +1,10 @@ #include "tpdate.hpp" +#include dso::TwoPartDate dso::TwoPartDateUTC::operator-(const dso::TwoPartDateUTC &d) const noexcept { int days = imjd() - d.imjd(); - double sec = seconds() - d.seconds(); + FDOUBLE sec = seconds().seconds() - d.seconds().seconds(); if (!days) { /*return dso::TwoPartDate(days, sec);*/ ; diff --git a/test/should_not_compile/fractional_seconds_op_equal.cpp b/test/should_not_compile/fractional_seconds_op_equal.cpp index 0352b28..613ba31 100644 --- a/test/should_not_compile/fractional_seconds_op_equal.cpp +++ b/test/should_not_compile/fractional_seconds_op_equal.cpp @@ -1,4 +1,4 @@ -#include "dtfund.hpp" +#include "calendar.hpp" using namespace dso; diff --git a/test/should_not_compile/fractional_seconds_op_greater.cpp b/test/should_not_compile/fractional_seconds_op_greater.cpp index a821499..3bfddac 100644 --- a/test/should_not_compile/fractional_seconds_op_greater.cpp +++ b/test/should_not_compile/fractional_seconds_op_greater.cpp @@ -1,4 +1,4 @@ -#include "dtfund.hpp" +#include "calendar.hpp" using namespace dso; diff --git a/test/should_not_compile/fund_type_op_plusequal.cpp b/test/should_not_compile/fund_type_op_plusequal.cpp index 17d946d..8570ece 100644 --- a/test/should_not_compile/fund_type_op_plusequal.cpp +++ b/test/should_not_compile/fund_type_op_plusequal.cpp @@ -1,5 +1,4 @@ -#include "dtfund.hpp" -#include +#include "calendar.hpp" int main() { diff --git a/test/sofa/epj_date.cpp b/test/sofa/epj_date.cpp index 37bdb9e..7ea3db8 100644 --- a/test/sofa/epj_date.cpp +++ b/test/sofa/epj_date.cpp @@ -63,7 +63,7 @@ int main() { /* do the same with SOFA */ double epj_sofa; { - const double jd1 = d.seconds() / SEC_PER_DAY; + const double jd1 = d.seconds().seconds() / SEC_PER_DAY; const double jd2 = d.imjd() + MJD0_JD; const double sje = iauEpj(jd2, jd1); double jd11, jd21; diff --git a/test/unit_tests/dread3.cpp b/test/unit_tests/dread3.cpp index f9861f4..e95e5e3 100644 --- a/test/unit_tests/dread3.cpp +++ b/test/unit_tests/dread3.cpp @@ -136,7 +136,7 @@ int main() { /* reach 23:59:59.999 999 999 */ err = 0e0; for (int i = 0; i < (int)(1e9) - 1; i++) - tai.add_seconds(1e-9, err); + tai.add_seconds(FractionalSeconds(1e-9), err); std::strcat(reset_buffer(buf1), leap_insertion_dates_str[it]); std::strcat(buf1, s_2359599); d1 = from_char(buf1); @@ -144,7 +144,7 @@ int main() { assert(equal_within_ulps(d1.seconds(), tai.seconds(), 1)); /* one more nanosec will take to the next day */ - tai.add_seconds(1e-9, err); + tai.add_seconds(FractionalSeconds(1e-9), err); assert(tai.imjd() == d1.imjd() + 1); d1 = TwoPartDate(modified_julian_day(d).as_underlying_type() + 1, FractionalSeconds(0e0)); @@ -170,7 +170,7 @@ int main() { /* reach 23:59:59.999 999 999 */ err = 0e0; for (int i = 0; i < (int)(1e9) - 1; i++) - utc.add_seconds(1e-9, err); + utc.add_seconds(FractionalSeconds(1e-9), err); std::strcat(reset_buffer(buf1), leap_insertion_dates_str[it]); std::strcat(buf1, s_2359599); d1 = from_utc_char(buf1); @@ -178,7 +178,7 @@ int main() { assert(equal_within_ulps(d1.seconds(), utc.seconds(), 1)); /* one more nanosec will take to next second, NOT the next day */ - utc.add_seconds(1e-9, err); + utc.add_seconds(FractionalSeconds(1e-9), err); assert(utc.imjd() == d1.imjd()); /* equal to YYYY-MM-DD 23:59:60 */ std::strcat(reset_buffer(buf1), leap_insertion_dates_str[it]); diff --git a/test/unit_tests/tpdate_add.cpp b/test/unit_tests/tpdate_add.cpp index 296803d..8bbbe0d 100644 --- a/test/unit_tests/tpdate_add.cpp +++ b/test/unit_tests/tpdate_add.cpp @@ -25,32 +25,32 @@ int main() { assert(td1.imjd() - 1 == d1.imjd().as_underlying_type()); assert(td1.seconds() == 0e0); - td1 = datetime(year(2023), month(10), day_of_month(24), - nanoseconds(0)); + td1 = TwoPartDate(datetime(year(2023), month(10), + day_of_month(24), nanoseconds(0))); for (int i = 0; i < 2 * 86400; i++) { td1.add_seconds(FractionalSeconds(1e0)); } assert(td1.imjd() - 2 == d1.imjd().as_underlying_type()); assert(td1.seconds() == 0e0); - td1 = datetime(year(2023), month(10), day_of_month(24), - nanoseconds(0)); + td1 = TwoPartDate(datetime(year(2023), month(10), + day_of_month(24), nanoseconds(0))); for (int i = 0; i < 86400; i++) { td1.add_seconds(FractionalSeconds(-1e0)); } assert(td1.imjd() + 1 == d1.imjd().as_underlying_type()); assert(td1.seconds() == 0e0); - td1 = datetime(year(2023), month(10), day_of_month(24), - nanoseconds(0)); + td1 = TwoPartDate(datetime(year(2023), month(10), + day_of_month(24), nanoseconds(0))); for (int i = 0; i < 2 * 86400; i++) { td1.add_seconds(FractionalSeconds(-1e0)); } assert(td1.imjd() + 2 == d1.imjd().as_underlying_type()); assert(td1.seconds() == 0e0); - td1 = datetime(year(2023), month(10), day_of_month(24), - nanoseconds(0)); + td1 = TwoPartDate(datetime(year(2023), month(10), + day_of_month(24), nanoseconds(0))); for (int i = 0; i < 86400; i++) { td1.add_seconds(FractionalSeconds(2e0)); td1.add_seconds(FractionalSeconds(-1e0));