Skip to content

Commit 729b854

Browse files
committed
more
1 parent 4e13c53 commit 729b854

23 files changed

+865
-401
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ target_include_directories(mjd2ymd PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
7575
add_executable(mjd2ydoy src/bin/mjd2ydoy.cpp)
7676
target_link_libraries(mjd2ydoy PRIVATE datetime)
7777
target_include_directories(mjd2ydoy PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
78+
add_executable(integral_seconds_limits src/bin/integral_datetime_limits.cpp)
79+
target_link_libraries(integral_seconds_limits PRIVATE datetime)
80+
target_include_directories(integral_seconds_limits PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
7881

7982
# disable clang-tidy (targets that follow will not be checked)
8083
set(CMAKE_CXX_CLANG_TIDY "")

include/date_integral_types.hpp

Lines changed: 133 additions & 50 deletions
Large diffs are not rendered by default.

include/datetime_interval.hpp

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
/** @file
2+
*
3+
* Define a datetime_interval type. This corresponds, physically, to a time
4+
* duration, measured in a given precision (e.g. seconds, milliseconds, etc).
5+
* This class will assist the datetime class; for example, the difference of
6+
* two datetime's will be a datetime_interval.
7+
*/
8+
9+
#ifndef __DSO_DATETIME_INTEGRAL_INTERVAL__HPP__
10+
#define __DSO_DATETIME_INTEGRAL_INTERVAL__HPP__
11+
12+
#include "date_integral_types.hpp"
13+
#include "hms_time.hpp"
14+
#ifdef DEBUG
15+
#include <cassert>
16+
#endif
17+
18+
namespace dso {
19+
20+
namespace core {
21+
/** @brief Return +1 or -1 depending on the sign of the input (arithmetic)
22+
* parameter.
23+
*
24+
* @param[in] value Any arithmetic value
25+
* @return +1 if the passed in value is >=0; -1 otherwise
26+
*/
27+
template <typename T, typename = std::enable_if_t<std::is_arithmetic<T>::value>>
28+
constexpr int sgn(T val) noexcept {
29+
return (T(0) <= val) - (val < T(0));
30+
}
31+
32+
/** @brief Specialization of core::sgn for fundamental datetime types. */
33+
#if __cplusplus >= 202002L
34+
template <gconcepts::is_fundamental_and_has_ref DType>
35+
#else
36+
template <typename DType,
37+
typename = std::enable_if_t<DType::is_dt_fundamental_type>,
38+
typename = std::enable_if_t<std::is_member_function_pointer<
39+
decltype(&DType::__member_ref__)>::value>>
40+
#endif
41+
constexpr int sgn(DType val) noexcept {
42+
return (DType(0) <= val) - (val < DType(0));
43+
}
44+
45+
/** @brief A copysign implementation for *seconds.
46+
*
47+
* Returns the magnitude of
48+
* */
49+
#if __cplusplus >= 202002L
50+
template <gconcepts::is_fundamental_and_has_ref DType, typename I>
51+
requires integral<I>
52+
#else
53+
template <typename DType, typename I,
54+
typename = std::enable_if_t<DType::is_dt_fundamental_type>,
55+
typename = std::enable_if_t<std::is_member_function_pointer<
56+
decltype(&DType::__member_ref__)>::value>,
57+
typename = std::enable_if_t<std::is_integral_v<I>>>
58+
#endif
59+
constexpr DType copysign(DType val, I isgn) noexcept {
60+
return DType{((val >= DType(0)) ? (val.as_underlying_type())
61+
: (val.as_underlying_type() * -1)) *
62+
sgn(isgn)};
63+
}
64+
65+
/** @brief A copysign implementation for integral types. */
66+
#if __cplusplus >= 202002L
67+
template <typename Iv, Is>
68+
requires integral<Iv> && integral<Is>
69+
#else
70+
template <typename Iv, typename Is,
71+
typename = std::enable_if_t<std::is_integral_v<Iv>>,
72+
typename = std::enable_if_t<std::is_integral_v<Is>>>
73+
#endif
74+
constexpr Iv copysign(Iv val, Is isgn) noexcept {
75+
return ((val >= 0) ? (val) : (val * -1)) * sgn(isgn);
76+
}
77+
78+
} /* namespace core*/
79+
80+
/** @brief A generic, templatized class to hold a datetime period/interval.
81+
*
82+
* A datetime_interval represents a time (datetime) interval or period, i.e.
83+
* 5 days, 12 hours and 49 seconds. We assume a continuous time scale, no leap
84+
* seconds are taken into consideration --this is only an interval not an
85+
* actual datetime instance--.
86+
*
87+
* A datetime_interval instance can only have positive (or zero) values (for
88+
* both of its members). However, seperate field is stored (i.e. \p m_sign) to
89+
* hold 'sign' information, so that a datetime_interval instance can be easily
90+
* used with a datetime instance. That means that e.g. 'adding' a negative
91+
* interval, will extend the datetime in the past.
92+
*
93+
* A datetime_interval instance has two fundamental parts (members):
94+
* - a day part (i.e. holding the integral days), and
95+
* - a time part (i.e. holding any type S of *second type)
96+
* - a sign (i.e. an integer, we only consider its sign here, not the value)
97+
*
98+
* The purpose of this class is to work together with the datetime and
99+
* datetimeUTC classes.
100+
*
101+
* @tparam S Any class of 'second type', i.e. any class S that has a (static)
102+
* member variable S::is_of_sec_type set to true. This can be
103+
* dso::seconds, dso::milliseconds, dso::microseconds.
104+
*/
105+
#if __cplusplus >= 202002L
106+
template <gconcepts::is_sec_dt S>
107+
#else
108+
template <class S, typename = std::enable_if_t<S::is_of_sec_type>>
109+
#endif
110+
class datetime_interval {
111+
/* the integral type of (whole) days */
112+
typedef typename S::underlying_type SecIntType;
113+
/* the integral type of S (*seconds) */
114+
typedef modified_julian_day::underlying_type DaysIntType;
115+
116+
private:
117+
/** number of whole days in interval */
118+
DaysIntType m_days;
119+
/** number of *sec in interval */
120+
S m_secs;
121+
/** sign of interval (only care for the sign of m_sign) */
122+
int m_sign;
123+
124+
public:
125+
/** @brief Default constructor (everything set to 0). */
126+
explicit constexpr datetime_interval() noexcept : m_days(0), m_secs(0) {};
127+
128+
/** @brief Constructor from number of days and number of *seconds.
129+
*
130+
* The sign of the interval is extracted from \p days. The number of days
131+
* and number of *seconds of the interval, are considered positive (i.e.
132+
* absolute values of input parameters).
133+
* For example to construct an interval of 2days + 2sec, use:
134+
* datetime_interval<sec> interval(2, seconds(2));
135+
* If you want to mark this interval as 'negative' (normally for algebraic
136+
* operations with a datetime isntance), use:
137+
* datetime_interval<sec> interval(-2, seconds(2));
138+
* If you want to mark a 'negative' interval but the number of days is zero,
139+
* then you should use:
140+
* datetime_interval<sec> interval(0, seconds(-2));
141+
* or
142+
* datetime_interval<sec> interval(seconds(-2));
143+
* and **NOT**
144+
* datetime_interval<sec> interval(-0, seconds(2));
145+
* since there is no 'signed 0' value.
146+
*
147+
* @warning It is not possible to differentiate between -0 and +0. Hence,
148+
* to construct an interval of 0 days and 1 secs (S), use:
149+
* datetime_interval(S(-1)) and **NOT**
150+
* datetime_interval(-0, S(1))
151+
*/
152+
constexpr datetime_interval(DaysIntType days, S secs) noexcept
153+
: m_days(days), m_secs(secs), m_sign(core::sgn(days)) {
154+
normalize();
155+
};
156+
157+
/** @brief Constructor from number of *seconds.
158+
*
159+
* The sign of the interval is taken from \p secs.
160+
*/
161+
constexpr datetime_interval(S secs) noexcept
162+
: m_days(0), m_secs(secs), m_sign(1) {
163+
normalize();
164+
};
165+
166+
/** @brief Normalize, i.e. split to integral days and *seconds of day. */
167+
constexpr void normalize() noexcept {
168+
/* number of whole days in seconds */
169+
const DaysIntType more =
170+
core::copysign(m_secs.as_underlying_type(), 1) / S::max_in_day;
171+
/* leftover seconds (positive) */
172+
const SecIntType s =
173+
core::copysign(m_secs.as_underlying_type(), 1) - more * S::max_in_day;
174+
/* add to current days, with the right sign */
175+
const DaysIntType days = core::copysign(m_days, m_sign) +
176+
core::copysign(more, m_secs.as_underlying_type());
177+
/* member vars */
178+
m_sign =
179+
(days > 0) * 1 + (days < 0) * (-1) + (days == 0) * core::sgn(m_secs);
180+
m_secs = S(s);
181+
m_days = core::copysign(days, 1);
182+
#ifdef DEBUG
183+
assert(m_days >= 0 && (m_secs >= 0 && m_secs < S::max_in_day));
184+
#endif
185+
}
186+
187+
/** @brief Return number of days in interval, always positive. */
188+
constexpr DaysIntType days() const noexcept { return m_days; }
189+
190+
/** @brief Return number of *secs (of day) in interval, always positive. */
191+
constexpr S sec() const noexcept { return m_secs; }
192+
193+
/** @brief Return number of *secs (of day) in interval, signed. */
194+
constexpr S signed_sec() const noexcept {
195+
return core::copysign(m_secs, m_sign);
196+
}
197+
198+
/** @brief Return the sign of the interval. */
199+
constexpr int sign() const noexcept { return m_sign; }
200+
201+
/** @brief Return the interval as days + *seconds in seconds type S,
202+
* ignoring sign (i.e. always positive).
203+
*/
204+
constexpr S unsigned_total_sec() const noexcept {
205+
return m_secs + S(S::max_in_day * m_days);
206+
}
207+
208+
/** @brief Return the interval as days+ *seconds in seconds type S,
209+
* using a negative value if the instance is marked as 'negative'.
210+
*/
211+
constexpr S signed_total_sec() const noexcept {
212+
return core::copysign(unsigned_total_sec(), m_sign);
213+
}
214+
215+
/** @brief Cast the interval to a signed floating point representation, i.e.
216+
* FractionalSeconds, FractionalDays or FractionalYears.
217+
*/
218+
template <DateTimeDifferenceType DT>
219+
typename DateTimeDifferenceTypeTraits<DT>::dif_type
220+
to_fraction() const noexcept {
221+
/* the return type */
222+
using RT = typename DateTimeDifferenceTypeTraits<DT>::dif_type;
223+
224+
if constexpr (DT == DateTimeDifferenceType::FractionalSeconds) {
225+
/* difference in fractional seconds */
226+
const double big = static_cast<double>(seconds::max_in_day * m_days);
227+
return RT(m_sign * (big + to_fractional_seconds(m_secs)));
228+
} else if constexpr (DT == DateTimeDifferenceType::FractionalDays) {
229+
/* difference in fractional days */
230+
const double big = static_cast<double>(m_days);
231+
return RT(m_sign * (big + to_fractional_days(m_secs)));
232+
} else {
233+
/* difference in fractional years */
234+
const double big = static_cast<double>(m_days);
235+
return RT(m_sign * (big + to_fractional_days(m_secs)) /
236+
DAYS_IN_JULIAN_YEAR);
237+
}
238+
}
239+
240+
/** @brief Overload equality operator.
241+
* @warning Comparing only the 'absolute value' of the interval, the sign
242+
* is not considered.
243+
*/
244+
constexpr bool operator==(const datetime_interval &d) const noexcept {
245+
return m_days == d.m_days && m_secs == d.m_secs;
246+
}
247+
248+
/** @brief Overload in-equality operator.
249+
* @warning Comparing only the 'absolute value' of the interval, the sign
250+
* is not considered.
251+
*/
252+
constexpr bool operator!=(const datetime_interval &d) const noexcept {
253+
return !(this->operator==(d));
254+
}
255+
256+
/** @brief Overload ">" operator.
257+
* @warning Comparing only the 'absolute value' of the interval, the sign
258+
* is not considered.
259+
*/
260+
constexpr bool operator>(const datetime_interval &d) const noexcept {
261+
return m_days > d.m_days || (m_days == d.m_days && m_secs > d.m_secs);
262+
}
263+
264+
/** @brief Overload ">=" operator.
265+
* @warning Comparing only the 'absolute value' of the interval, the sign
266+
* is not considered.
267+
*/
268+
constexpr bool operator>=(const datetime_interval &d) const noexcept {
269+
return m_days > d.m_days || (m_days == d.m_days && m_secs >= d.m_secs);
270+
}
271+
272+
/** @brief Overload "<" operator.
273+
* @warning Comparing only the 'absolute value' of the interval, the sign
274+
* is not considered.
275+
*/
276+
constexpr bool operator<(const datetime_interval &d) const noexcept {
277+
return m_days < d.m_days || (m_days == d.m_days && m_secs < d.m_secs);
278+
}
279+
280+
/** @brief Overload "<=" operator.
281+
* @warning Comparing only the 'absolute value' of the interval, the sign
282+
* is not considered.
283+
*/
284+
constexpr bool operator<=(const datetime_interval &d) const noexcept {
285+
return m_days < d.m_days || (m_days == d.m_days && m_secs <= d.m_secs);
286+
}
287+
}; /* class datetime_interval */
288+
289+
} /* namespace dso */
290+
291+
#endif

include/datetime_read.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#ifndef __DSO_DATETIME_IO_READ_HPP__
77
#define __DSO_DATETIME_IO_READ_HPP__
88

9-
#include "datetime_io_core.hpp"
9+
#include "core/datetime_io_core.hpp"
1010
#include "dtdatetime.hpp"
1111
#include "hms_time.hpp"
1212
#include "tpdate.hpp"

include/datetime_write.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@
66
#ifndef __DSO_DATETIME_IO_WRITE_HPP__
77
#define __DSO_DATETIME_IO_WRITE_HPP__
88

9-
#include "datetime_io_core.hpp"
9+
#include "core/datetime_io_core.hpp"
1010
#include "dtdatetime.hpp"
11-
#include "hms_time.hpp"
1211
#include "tpdate.hpp"
1312
#include <cstdio>
1413
#include <stdexcept>

0 commit comments

Comments
 (0)