4

I'm modifying an old project, and at the same time I'm updating several things to bring it up to C++11.

I'd like to replace various uses of boost::date_time with the new functionality in std::chrono. But I cannot figure out what is the C++11 equivalent of boost::date_time::not_a_date_time.

Isn't there an equivalent in C++11 to indicate that a time_point variable hasn't yet been assigned, or doesn't contain a valid timestamp?

dyp
  • 38,334
  • 13
  • 112
  • 177
Stéphane
  • 19,459
  • 24
  • 95
  • 136
  • I'm a bit confused: How do you create an "invalid" `std::chrono::time_point`? The [constructors](http://en.cppreference.com/w/cpp/chrono/time_point/time_point) seem to allow only creation of "valid" time points. – dyp Sep 17 '14 at 20:34
  • @dyp That is exactly the problem I'm having. I cannot figure out how to do it with std::chrono. In Boost::date_time -- the library I wanted to replace -- the constructor can take an enum of "special value", one of which is boost::date_time::not_a_date_time. http://www.boost.org/doc/libs/1_56_0/doc/html/date_time/doxy.html#header.boost.date_time.special_defs_hpp – Stéphane Sep 18 '14 at 00:18
  • 1
    Ah, I think I'm beginning to understand the problem. `std::chrono` is related to `boost::chrono`, not to `boost::date_time` as far as I can see. – dyp Sep 18 '14 at 00:29
  • There is a constructor that takes an enum. For example: `ptime(special_values sv) Constructor for infinities, not-a-date-time, max_date_time, and min_date_time` from http://www.boost.org/doc/libs/1_56_0/doc/html/date_time/posix_time.html#date_time.posix_time.ptime_class. – Stéphane Sep 18 '14 at 00:29
  • 2
    Correct, date_time and chrono battled it out, and chrono won and made it into C++11. But now some older projects that used date_time need to be converted to chrono. The issue is chrono contains just a tiny fraction of the functionality of date_time. – Stéphane Sep 18 '14 at 00:31
  • Well, then, there is no equivalent of these special values. As Ben Voigt suggested, you can use `std::chrono::time_point` to build some type that does support special values, e.g. by interpreting some of the valid values of `rep` as those special values (for `Rep == float`, NaN could be interpreted as `not_a_date_time`). – dyp Sep 18 '14 at 00:35
  • @dyp: Since `is_not_a_date_time` forwards to `ticks.is_nan()` in `boost::date_time`, I think that's how it was done there too. – Ben Voigt Sep 18 '14 at 01:08
  • @Stéphane seeing as `std::chrono` is *not* the C++11 equivalent of `boost::date_time`, why convert at all? What's wrong with `boost::date_time`? – Ohad Schneider Jul 19 '15 at 17:42

2 Answers2

2

Given the fact that it exists as part of a group

bool is_infinity() const
bool is_neg_infinity() const
bool is_pos_infinity() const
bool is_not_a_date_time() const

it is pretty clear that this is done by using a floating-point type for the internal representation and setting the value to a NaN (not-a-number).

In std::chrono, the representation type is required to be an arithmetic type. Floating-point types therefore qualify, and you can use the same trick.

Given a std::duration, you could then test it using

std::isnan(dur.count())

(Naturally, you should use a quiet NaN value, not a signalling NaN, so you don't trigger floating-point traps)

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I have to admit: I don't understand this answer. How is it clear that the enum for not_a_date_time is a floating point type, and assuming that was true (http://www.boost.org/doc/libs/1_56_0/doc/html/date_time/doxy.html#header.boost.date_time.special_defs_hpp) how does this help me achieve the same functionality in std::chrono? – Stéphane Sep 18 '14 at 00:22
  • @Stéphane: The special values for `boost::date_time` correspond exactly to the special values for IEEE floating-point types. All the types in `std::chrono` are parameterized (via `std::chrono::duration`) on an arithmetic type and unit. That arithmetic type can be `float` or `double`. – Ben Voigt Sep 18 '14 at 01:03
  • Additionally the `rep` for a `chrono::duration` can be a class type that emulates an arithmetic type. Therefore one could create an integral-based `Number` class which reserved one of its values (e.g. `LLONG_MIN`) for `NaN`, and implemented its arithmetic accordingly. – Howard Hinnant Sep 21 '14 at 18:33
  • Just stumbled over this. The answer is not correct. Check [`boost/date_time/int_adapter.hpp`](http://www.boost.org/doc/libs/1_48_0/boost/date_time/int_adapter.hpp). Nothing inside `boost:;date_time` uses floating-point values. – mindriot Jul 19 '16 at 15:00
  • @mindriot: How does your observation, even if it were accurate, make the suggestion to use a NaN value in the duration "not correct"? – Ben Voigt Jul 19 '16 at 21:19
  • 1
    OK, let me clarify: You're saying it is "pretty clear that this is done using a floating-point type". This is definitely not correct, as `boost::date_time` does not use floating-point types. Of course you are correct that in principle you could use a floating-point representation for `std::chrono` types, but that is a hack; semantically, the chrono types have no concept of "not-a-date-time". It doesn't play nice; casting back to an integer is actually [UB](http://en.cppreference.com/w/cpp/chrono/duration/duration_cast). – mindriot Jul 19 '16 at 21:46
  • @mindriot: Of course you cannot cast "not a date time" to an integer. Since when has that been a problem? Are you saying that Boost allows that, and gives a result that is somehow *useful*? – Ben Voigt Jul 19 '16 at 23:10
  • 1
    The casting case is just an example. And I mean `duration_cast`ing to a chrono type with integer representation. Much more importantly, since the chrono types have no notion of not-a-date-time, you cannot expect any software that uses these types to do anything useful whatsoever with a manually created NaN floating-point value. In other words, using a floating-point NaN in a `std::duration` is an accident waiting to happen, since _no software other than that written by the OP themselves_ can be expected to deal with this in any meaningful way. – mindriot Jul 20 '16 at 10:47
  • I think your answer just needs two improvements: (1) point out that `boost::date_time` reserves certain integer values to have special meanings (feel free to take stuff from my answer, I can delete it if you edit yours); and (2) make it clear that using a floating-point rep for `std::duration`, and manually setting NaN values, is at best a workaround that has to be treated carefully, since chrono does not prescribe such semantics. – mindriot Jul 20 '16 at 12:19
2

boost::date_time uses integer time representations internally, and defines the special values inside boost/date_time/int_adapter.hpp:

static const int_adapter  pos_infinity()
{
  return (::std::numeric_limits<int_type>::max)();
}
static const int_adapter  neg_infinity()
{
  return (::std::numeric_limits<int_type>::min)();
}
static const int_adapter  not_a_number()
{
  return (::std::numeric_limits<int_type>::max)()-1;
}
static  int_adapter max BOOST_PREVENT_MACRO_SUBSTITUTION ()
{
  return (::std::numeric_limits<int_type>::max)()-2;
}
static  int_adapter min BOOST_PREVENT_MACRO_SUBSTITUTION ()
{
  return (::std::numeric_limits<int_type>::min)()+1;
}

Essentially, it reserves certain integer values to have special meanings.

However, as others have pointed out, std::chrono does not offer these special values (there are only min and max functions); and std::numeric_limits is also not specialized (see Why does std::numeric_limits<seconds>::max() return 0?).

Ben Voigt's answer presents a possible workaround, but be aware that since the std::chrono classes do not specify such semantics, handing a NaN timestamp or duration to any function you did not write yourself is likely going to trigger undefined behaviour.

Community
  • 1
  • 1
mindriot
  • 5,413
  • 1
  • 25
  • 34