*
*/
+#include <cstdio>
#include <errno.h>
#include <string>
#include <sstream>
* - strptime(3) will not parse the leading dash with that format
* but apart from that it works well.
*/
-static const char *const iso8601_fmt_d = "%4Y-%m-%d";
-static const char *const iso8601_fmt_t = "%T";
-static const char *const iso8601_fmt_tz = "%TZ%z";
-static const char *const iso8601_fmt_dt = "%4Y-%m-%dT%T";
-static const char *const iso8601_fmt_dtz = "%4Y-%m-%dT%TZ%z";
-
-static inline const char *
-pick_iso8601_fmt (const bool date, const bool time, const bool tz)
-{
- const char *format = NULL;
- if (date) {
- if (time) {
- if (tz) {
- format = iso8601_fmt_dtz;
+namespace iso8601 {
+
+ /*
+ * max: -YYYYYYYYYY-MM-DDThh:mm:ssZ+zzzz ⇒ 32
+ * That is assuming the year in broken down time is an int we
+ * need to reserve ten decimal places.
+ */
+// static_assert (sizeof (((struct tm *)NULL)->tm_year) == 4);
+ static const size_t bufsize = 33;
+
+ enum kind {
+ d = 0,
+ t = 1,
+ tz = 2,
+ dt = 3,
+ dtz = 4,
+ ISO8601_SIZE = 5,
+ };
+
+ /*
+ * Unfortunately the glibc strptime(3) on the Intranator trips over
+ * the length specifier in field descriptors so we can’t reuse the
+ * formatters here. This problem is fixed in newer glibc. For the time
+ * being we keep two tables of formatters and choose the appropriate
+ * at runtime.
+ */
+
+ static const char *const formatter [ISO8601_SIZE] =
+ { /* [iso8601::d ] = */ "%4Y-%m-%d",
+ /* [iso8601::t ] = */ "%T",
+ /* [iso8601::tz ] = */ "%TZ%z",
+ /* [iso8601::dt ] = */ "%4Y-%m-%dT%T",
+ /* [iso8601::dtz] = */ "%4Y-%m-%dT%TZ%z",
+ };
+
+ static const char *const scanner [ISO8601_SIZE] =
+ { /* [iso8601::d ] = */ "%Y-%m-%d",
+ /* [iso8601::t ] = */ "%T",
+ /* [iso8601::tz ] = */ "%TZ%z",
+ /* [iso8601::dt ] = */ "%Y-%m-%dT%T",
+ /* [iso8601::dtz] = */ "%Y-%m-%dT%TZ%z",
+ };
+
+ static inline const char *
+ pick_fmt (const bool date, const bool time, const bool tz, const bool scan=false)
+ {
+ const char *const *table = scan ? iso8601::scanner : iso8601::formatter;
+ enum iso8601::kind format = iso8601::dtz;
+
+ if (date) {
+ if (time) {
+ if (tz) {
+ format = iso8601::dtz;
+ } else {
+ format = iso8601::dt;
+ }
} else {
- format = iso8601_fmt_dt;
+ format = iso8601::d;
}
+ } else if (time && tz) {
+ format = iso8601::tz;
} else {
- format = iso8601_fmt_d;
+ format = iso8601::t; /* default to %T */
}
- } else if (time && tz) {
- format = iso8601_fmt_tz;
- } else {
- format = iso8601_fmt_t; /* default to %T */
+
+ return table [format];
}
- return format;
-}
+} /* [iso8601] */
-namespace {
- /*
- * max: -YYYYYYYYYY-MM-DDThh:mm:ssZ+zzzz ⇒ 32
- * That is assuming the year in broken down time is an int we
- * need to reserve ten decimal places.
- */
-// static_assert (sizeof (((struct tm *)NULL)->tm_year) == 4);
- static const size_t iso8601_bufsize = 33;
+namespace {
static inline int flip_tm_year (const int y)
{ return (y + 1900) * -1 - 1900; }
* @param tm Time to format as broken-down \c struct tm.
* @param date Include the day part ([-]YYYY-MM-DD).
* @param time Include the time part (hh:mm:ss).
- * @param tz Include the timezone ([±]ZZZZ); only heeded if
+ * @param tz Include the timezone ([±]ZZZZ); only needed if
* \c time is requested as well.
*
* @return The formatted timestamp.
const bool time, const bool tz)
{
struct tm tmp;
- char buf [iso8601_bufsize] = { 0 };
+ char buf [iso8601::bufsize] = { 0 };
char *start = &buf [0];
- const char *format = pick_iso8601_fmt (date, time, tz);
+ const char *format = iso8601::pick_fmt (date, time, tz);
memcpy (&tmp, &tm, sizeof (tmp));
}
/*
- * The sign is *always* handled above so the formatted string her
+ * The sign is *always* handled above so the formatted string here
* is always one character shorter.
- * */
- const size_t n = strftime (start, iso8601_bufsize-1, format, &tmp);
+ */
+ if (strftime (start, iso8601::bufsize-1, format, &tmp) == 0)
+ {
+ return std::string ();
+ }
- buf [n+1] = '\0';
+ buf [iso8601::bufsize-1] = '\0'; /* Just in case. */
return std::string (buf);
}
const bool date, const bool time, const bool tz) NOEXCEPT
{
struct tm tm;
- const char *format = pick_iso8601_fmt (date, time, tz);
+ const char *format = iso8601::pick_fmt (date, time, tz, true);
const char *start = s;
bool negyear = false;
return tm;
}
+/**
+ * @brief Format a \c struct timespec in the schema established by
+ * time(1): “3m14.159s”.
+ *
+ * @param ts The time spec to format.
+ *
+ * @return \c boost:none in case of error during formatting, an optional
+ * \c std::string otherwise.
+ */
+boost::optional<std::string>
+format_min_sec_msec (const struct timespec &ts)
+{
+ char ms [4] = { '\0', '\0', '\0', '\0' };
+
+ if (snprintf (ms, 4, "%.3ld", ts.tv_nsec / 1000000) < 0) {
+ return boost::none;
+ }
+
+ const time_t min = ts.tv_sec / 60;
+ const time_t sec = ts.tv_sec - min * 60;
+
+ return I2n::to_string (min) + "m"
+ + I2n::to_string (sec) + "."
+ + ms + "s"
+ ;
+}
namespace I2n {
namespace {
+ /**
+ * @brief <b>For internal use only</b>. Translates clock
+ * specification flags to kernel clock types.
+ *
+ * @param id Master clock id: \c mono, \c real, \c boot, or \c
+ * cpu.
+ * @param var Variant of clock if appropriate: \c raw, \c exact, \c
+ * process, or \c thread. Use \c dflt for the base
+ * variant.
+ *
+ * @return The clock id for using with kernel APIs.
+ */
static inline clockid_t
clockid_of_flags (const enum type::id id,
const enum type::variant var) NOEXCEPT
}
case type::boot: {
- if (var & type::exact) {
- cid = CLOCK_BOOTTIME;
- }
+ cid = CLOCK_BOOTTIME;
break;
}
, err (0)
{ }
+ /*
+ * Ctor from *struct tm*. On 32 bit systems the conversion to *time_t* will
+ * fail with years outside the range from epoch to 2038.
+ */
Time::Time (const struct tm &tm,
const enum type::id id,
- const enum type::variant var) NOEXCEPT
+ const enum type::variant var)
{
struct tm tmp_tm; /* dummy for mktime(3) */
Time tmp_time;
memcpy (&tmp_tm, &tm, sizeof (tmp_tm));
- tmp_time = Time (mktime (&tmp_tm), 0l, id, var);
+
+ errno = 0;
+ const time_t t = mktime (&tmp_tm);
+ if (t == - 1) { /* Glibc does not set errno on out-of-range here! */
+ const char *datestr = asctime (&tm);
+ throw conversion_error (errno,
+ std::string ("mktime: from struct tm {")
+ + std::string (datestr, 0, strlen(datestr)-1)
+ + "}");
+ }
+
+ tmp_time = Time (t, 0l, id, var);
this->swap (tmp_time);
}
return *this;
}
+ /*
+ * @note This operator is an up-assignment from a type containing less
+ * information than the structure assigned from. Since the
+ * operator can only be two valued we must normalize the remaining
+ * fields to the default clock. When assigning from non-default
+ * clocks, use the appropriate constructor and pass it the desired
+ * id and variant so as to assign the result.
+ */
Time &
Time::operator= (struct timespec ts) NOEXCEPT
{
}
Time &
- Time::scale (const time_t factor) NOEXCEPT
+ Time::scale (const int64_t factor) NOEXCEPT
{
this->value.tv_sec *= factor;
this->value.tv_nsec *= factor;
return *this;
}
+ /*
+ * Below division code purposely does not attempt to handle divide-
+ * by-zero just as any other C++ division function does. It is up to
+ * the caller to ensure that the divisor is not zero.
+ */
+ Time &
+ Time::divide (const int64_t divisor) NOEXCEPT
+ {
+ const long sec = static_cast<long> (this->value.tv_sec );
+ int64_t nsec = static_cast<int64_t> (this->value.tv_nsec);
+ const ldiv_t div = ldiv (sec, divisor);
+
+ if (div.rem != 0) {
+ nsec += div.rem * TIME_CONST_FACTOR_NANO;
+ }
+
+ nsec /= divisor;
+
+ this->value.tv_sec = static_cast<time_t> (div.quot);
+ this->value.tv_nsec = static_cast<long> (nsec);
+
+ this->carry_nsec ();
+
+ return *this;
+ }
+
boost::optional<std::string>
Time::format_iso8601 (const bool utc,
const bool date,
return ::format_iso8601 (tm, date, time, tz);
}
- boost::optional<std::string>
+ std::string
Time::make_nice_time (void) const
{
/* XXX the cast below results in loss of precision with 64 bit time_t! */
return ::make_nice_time (static_cast<int> (this->value.tv_sec));
}
- boost::optional<std::string>
+ std::string
Time::format_full_time (void) const
{ return ::format_full_time (this->value.tv_sec); }
- boost::optional<std::string>
+ std::string
Time::format_date (void) const
{ return ::format_date (this->value.tv_sec); }
return boost::none;
}
- return Time (*tm, id, var);
+ try {
+ return Time (*tm, id, var);
+ }
+ catch (conversion_error &_unused) { }
+
+ return boost::none;
}
} /* [namespace clock] */