-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
23ce7ef
commit 3779e63
Showing
7 changed files
with
432 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.