Skip to content

Commit

Permalink
dwrite tests
Browse files Browse the repository at this point in the history
  • Loading branch information
xanthospap committed Oct 24, 2023
1 parent 23ce7ef commit 3779e63
Show file tree
Hide file tree
Showing 7 changed files with 432 additions and 112 deletions.
176 changes: 109 additions & 67 deletions src/datetime_write.hpp
Original file line number Diff line number Diff line change
@@ -1,88 +1,130 @@
/// @file datetime_write.hpp
/// @brief Function to format dso::datetime objects as strings
/// @see dso::datetime
/// @author xanthos
/// @bug No known bugs.
/** @file
* Functions to pretty print datetime instances
*/

#ifndef __NGPT_DT_WRITERS__
#define __NGPT_DT_WRITERS__

#include "dtcalendar.hpp"
#include "dtfund.hpp"
#include <iomanip>
#include <iostream>
#include <sstream>
#include "hms_time.hpp"
#include <stdexcept>
#include <cstdio>

namespace dso {

namespace datetime_write {
/// Format a double to an std::string using std::fixed and a precision on
/// n digits
inline std::string _d2s_(double d, int n = 2) noexcept {
std::stringstream stream;
stream << std::fixed << std::setprecision(n) << d;
return stream.str();
}
enum class YMDFormat { YYYYMMDD, DDMMYYYY, YYMMDD, DDMMYY };

template <YMDFormat F> class SpitDate {};
template<> class SpitDate<YMDFormat::YYYYMMDD> {
public:
static const int numChars = 10;
static int spit(const ymd_date &ymd, char *buffer) noexcept {
return std::sprintf(buffer, "%4d/%02d/%02d", ymd.yr().as_underlying_type(),
ymd.mn().as_underlying_type(),
ymd.dm().as_underlying_type());
}
};
template<> class SpitDate<YMDFormat::DDMMYYYY> {
public:
static const int numChars = 10;
static int spit(const ymd_date &ymd, char *buffer) noexcept {
return std::sprintf(buffer, "%02d/%02d/%4d", ymd.dm().as_underlying_type(),
ymd.mn().as_underlying_type(),
ymd.yr().as_underlying_type());
}
};

/// Format a double to an std::string with a width of w chars
inline std::string _i2s_(int i, int w = 2) noexcept {
std::stringstream stream;
stream << std::setfill('0') << std::setw(w) << i;
return stream.str();
/** Format a ymd_date instace to a string and write it to buffer.
*
* The way the date is formated, is dictated by the teplate parameter \p F.
* The buffer must be of size able to store all characters specified by \p F
* Note that months and day of months are written using width of 2 chatacters,
* that is if < 10, a '0' is prepended.
* For example ymd_date(year(2023), month(10), day_of_month(3)) will be:
* '2023/10/03' in the YMDFormat::YYYYMMDD format.
*
* @param[in] ymd The ymd_date instance to format
* @param[out] buffer The non-null terminated C-string holding the ymd_date
* instance in the specified format. Its size is dicated by \p F
* and (at input) must be long enough to hold all charactes needed
* to represent the date
* @return On success, a pointer to \p buffer
*/
template<YMDFormat F>
const char *to_char(const ymd_date &ymd, char *buffer) {
if (SpitDate<F>::spit(ymd, buffer) != SpitDate<F>::numChars) {
throw std::runtime_error("[ERROR] Failed to fromat date to string\n");
}
return buffer;
}
} // namespace datetime_write

/// @brief Format as YYYY-MM-DD
///
/// Read and return a date from a c-string of type: YYYY-MM-DD, where the
/// delimeters can be whatever (but something, i.e. two numbers must be
/// seperated by some char -- 20150930 is wrong --).
/// Hours, minutes and seconds are set to 0.
/// If the argument stop is passed, it will be set to the last character (of
/// str) interpreted.
///
/// @throw std::invalid_argument if the input string cannot be resolved.
template <typename T>
std::string strftime_ymd_hmfs(const datetime<T> &t, char del = '-') {
auto ymd = t.as_ymd();
auto hmsf = as_hmsf(t.sec());
enum class HMSFormat { HHMMSS, HHMMSSF };

return datetime_write::_i2s_((ymd.__year).as_underlying_type(), 4) + del +
datetime_write::_i2s_((ymd.__month).as_underlying_type(), 2) + del +
datetime_write::_i2s_((ymd.__dom).as_underlying_type(), 2) + ' ' +
datetime_write::_i2s_(hmsf._hours.as_underlying_type(), 2) + ':' +
datetime_write::_i2s_(hmsf._minutes.as_underlying_type(), 2) + ':' +
datetime_write::_i2s_(hmsf._seconds.as_underlying_type(), 2) + ':' +
datetime_write::_d2s_(hmsf._fraction, 5);
}
#if __cplusplus >= 202002L
template <gconcepts::is_sec_dt S, HMSFormat F>
#else
template <typename S, HMSFormat F, typename = std::enable_if_t<S::is_of_sec_type>>
#endif
class SpitTime {};

template <typename T>
const char *strftime_ymd_hmfs(const datetime<T> &t, char *buf, char del = '-') {
auto ymd = t.as_ymd();
auto hmsf = as_hmsf(t.sec());
sprintf(buf, "%4d%c%02d%c%02d %02d:%02d:%012.9f",
ymd.__year.as_underlying_type(), del,
ymd.__month.as_underlying_type(), del, ymd.__dom.as_underlying_type(),
hmsf._hours.as_underlying_type(), hmsf._minutes.as_underlying_type(),
hmsf.fractional_seconds());
return buf;
}
#if __cplusplus >= 202002L
template <gconcepts::is_sec_dt S>
#else
template <typename S>
#endif
class SpitTime<S, HMSFormat::HHMMSS> {
typedef typename S::underlying_type SecIntType;
public:
static const int numChars = 8;
static int spit(const hms_time<S> &hms, char *buffer) noexcept {
/* (integral) seconds of minute */
SecIntType sec = cast_to<S, seconds>(hms.nsec()).as_underlying_type();
return std::sprintf(
buffer, "%02d:%02d:%02ld", hms.hr().as_underlying_type(),
hms.mn().as_underlying_type(), sec);
}
};

template <typename T>
std::string strftime_ymd_hms(const datetime<T> &t, char del = '-') {
auto ymd = t.as_ymd();
auto hmsf = as_hmsf(t.sec());
#if __cplusplus >= 202002L
template <gconcepts::is_sec_dt S>
#else
template <typename S>
#endif
class SpitTime<S, HMSFormat::HHMMSSF> {
public:
static const int numChars = 17;
static int spit(const hms_time<S> &hms, char *buffer) noexcept {
/* seconds of minute (real) */
double sec = to_fractional_seconds(hms.nsec());
return std::sprintf(
buffer, "%02d:%02d:%011.9f", hms.hr().as_underlying_type(),
hms.mn().as_underlying_type(), sec);
}
};

return datetime_write::_i2s_((ymd.__year).as_underlying_type(), 4) + del +
datetime_write::_i2s_((ymd.__month).as_underlying_type(), 2) + del +
datetime_write::_i2s_((ymd.__dom).as_underlying_type(), 2) + ' ' +
datetime_write::_i2s_(hmsf._hours.as_underlying_type(), 2) + ':' +
datetime_write::_i2s_(hmsf._minutes.as_underlying_type(), 2) + ':' +
datetime_write::_i2s_(hmsf._seconds.as_underlying_type(), 2) + ':' +
datetime_write::_d2s_(hmsf._fraction, 5);
/** Format a hms_time<S> instace to a string and write it to buffer.
*
* The way the time is formated, is dictated by the teplate parameter \p F.
* The buffer must be of size able to store all characters specified by \p F
* Note that hours and minutes of day are written using width of 2 chatacters,
* that is if < 10, a '0' is prepended.
*
* @param[in] hms The hms_time<S> instance to format
* @param[out] buffer The non-null terminated C-string holding the hms_time<S>
* instance in the specified format. Its size is dicated by \p F
* and (at input) must be long enough to hold all characters
* needed to represent the time
* @return On success, a pointer to \p buffer
*/
template<HMSFormat F, typename S>
const char *to_char(const hms_time<S> &hms, char *buffer) {
if (SpitTime<S,F>::spit(hms, buffer) != SpitTime<S,F>::numChars) {
throw std::runtime_error("[ERROR] Failed to fromat time to string\n");
}
return buffer;
}

} // namespace dso
} /* namespace dso */

#endif
42 changes: 0 additions & 42 deletions src/dtfund.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1215,43 +1215,6 @@ class seconds {
return m_sec;
}

/*
/// @brief Normalize seconds and return the integeral days.
///
/// If the seconds sum up to more (or equal to) one day, remove the integer
/// days and return them as integer; reset the seconds to seconds of the
/// new day.
///
/// @return The integer number of days (if the seconds are more than a day).
/// @throw Does not throw.
///
constexpr int remove_days() noexcept {
underlying_type days = m_sec / max_in_day;
m_sec = m_sec % max_in_day;
return static_cast<int>(days);
}
/// @brief Cast to days.
///
/// If the seconds sum up to more (or equal to) one day, return the
/// (integral) number of days.
///
/// @return The integer number of days (if the seconds are more than a day).
/// @throw Does not throw.
///
/// @warning Negative seconds are not handled.
///
constexpr int to_days() const noexcept {
return static_cast<int>(m_sec / max_in_day);
}
/// @brief Seconds as fractional days.
/// Interpret (cast) the seconds as fractional days; returns a double.
constexpr double fractional_days() const noexcept {
return static_cast<double>(m_sec) / static_cast<double>(max_in_day);
}
*/

private:
/** Cast to any arithmetic type. */
#if __cplusplus >= 202002L
Expand Down Expand Up @@ -1369,11 +1332,6 @@ class milliseconds {
return m_sec;
}

/** Cast to fractional days. */
constexpr double fractional_days() const noexcept {
return static_cast<double>(m_sec) / static_cast<double>(max_in_day);
}

/** Cast to fractional hours */
constexpr double to_fractional_hours() const noexcept {
constexpr const underlying_type secinh =
Expand Down
11 changes: 8 additions & 3 deletions src/hms_time.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ template <gconcepts::is_sec_dt S>
#else
template <typename S, typename = std::enable_if_t<S::is_of_sec_type>>
#endif
struct hms_time {
class hms_time {
/** int type of seconds */
using SecIntType = typename S::underlying_type;
/** hours */
Expand All @@ -25,6 +25,11 @@ struct hms_time {
/** seconds */
S _sec;

public:
hours hr() const noexcept {return _hours;}
minutes mn() const noexcept {return _minutes;}
S nsec() const noexcept {return _sec;}

/** Constructor from any second type */
explicit hms_time(S seconds) noexcept {
/* given integral seconds */
Expand All @@ -35,7 +40,7 @@ struct hms_time {
/* compute/remove hours */
const SecIntType hr = isecs / secInHour;
_hours = hr;
SecIntType remaining = isecs - _hours * secInHour;
SecIntType remaining = isecs - _hours.as_underlying_type() * secInHour;
#ifdef DEBUG
assert(remaining < secInHour);
#endif
Expand All @@ -44,7 +49,7 @@ struct hms_time {
/* compute/remove minutes */
const SecIntType mn = remaining / secInMin;
_minutes = mn;
remaining = remaining - _minutes * secInMin;
remaining = remaining - _minutes.as_underlying_type() * secInMin;
#ifdef DEBUG
assert(remaining < secInMin);
#endif
Expand Down
60 changes: 60 additions & 0 deletions test/unit_tests/dwrite.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include "datetime_write.hpp"
#include <cstring>
#include <cassert>

using namespace dso;

int main() {

char buffer[64];
int sz;

/* check dates */
ymd_date d1(year(2023), month(10), day_of_month(24));
to_char<YMDFormat::YYYYMMDD>(d1, buffer);
sz = dso::SpitDate<YMDFormat::YYYYMMDD>::numChars;
assert(!std::strncmp(buffer, "2023/10/24", sz));

d1 = ymd_date(year(2023), month(1), day_of_month(4));
to_char<YMDFormat::YYYYMMDD>(d1, buffer);
sz = dso::SpitDate<YMDFormat::YYYYMMDD>::numChars;
assert(!std::strncmp(buffer, "2023/01/04", sz));

/* check time */
hms_time<seconds> t1(seconds(1));
to_char<HMSFormat::HHMMSS>(t1, buffer);
sz = dso::SpitTime<seconds,HMSFormat::HHMMSS>::numChars;
assert(!std::strncmp(buffer, "00:00:01", sz));

t1 = hms_time<seconds>(seconds(59));
to_char<HMSFormat::HHMMSS>(t1, buffer);
sz = dso::SpitTime<seconds,HMSFormat::HHMMSS>::numChars;
assert(!std::strncmp(buffer, "00:00:59", sz));

t1 = hms_time<seconds>(seconds(60));
to_char<HMSFormat::HHMMSS>(t1, buffer);
sz = dso::SpitTime<seconds,HMSFormat::HHMMSS>::numChars;
assert(!std::strncmp(buffer, "00:01:00", sz));

t1 = hms_time<seconds>(seconds(59*60));
to_char<HMSFormat::HHMMSS>(t1, buffer);
sz = dso::SpitTime<seconds,HMSFormat::HHMMSS>::numChars;
assert(!std::strncmp(buffer, "00:59:00", sz));

t1 = hms_time<seconds>(seconds(60*60));
to_char<HMSFormat::HHMMSS>(t1, buffer);
sz = dso::SpitTime<seconds,HMSFormat::HHMMSS>::numChars;
assert(!std::strncmp(buffer, "01:00:00", sz));

t1 = hms_time<seconds>(seconds(60*60+1));
to_char<HMSFormat::HHMMSS>(t1, buffer);
sz = dso::SpitTime<seconds,HMSFormat::HHMMSS>::numChars;
assert(!std::strncmp(buffer, "01:00:01", sz));

t1 = hms_time<seconds>(seconds(86400-1));
to_char<HMSFormat::HHMMSS>(t1, buffer);
sz = dso::SpitTime<seconds,HMSFormat::HHMMSS>::numChars;
assert(!std::strncmp(buffer, "23:59:59", sz));

return 0;
}
Loading

0 comments on commit 3779e63

Please sign in to comment.