[date_time] boost time is lacking a big bang

Hi! My first goal in boostifying my proposed interval template library (ITL http://sourceforge.net/projects/itl) was to provide good examples for interval_sets and interval_maps using boost::date_time objects. This was much harder than I had expected, because incidentally the most important syntactical and semantical requirements that I do rely on for a parameter T of itl::interval<T> are not provided by boost::date_time. The one property of boost::date_time that knocked my socks off was the default constructor that 'is-not-a-date-time'. I paraphrase this as: boost::time is lacking a big bang ;-) Default constructors in nearly all the types that I've seen in my lifetime (I know the realm of my unknown unknowns is vast ;-) are intended to be elements of the type they do construct. Moreover in so many cases including built in types, default ctors serve as *initial elements*. Other elements can be *reached* from T() applying operations of T on them. This applies to all built in types and many std::types, like e.g. string, set, list etc. The most simple instance is unsigned int() with increment operation ++ that resembles the Peano axiomatic operations 0 and successor for natural numbers. This semantics of default ctors, at least in common built in and standard types and also in so many user defined types is so fundamental that I took it as a *semantical invariant* for the construction of my library. In the ITL an empty interval<T> [1,0] for instance, has to be defined generically: [ type<T>::unon(), type<T>::neutron() ] where type<T>::neutron() is implemented via T() : the default ctor and type<T>::unon() is implemented via ++T() : the default ctor incremented exactly once. So, striving to be as generic as possible I tried to set those minimal requirements that I assumed to be common sense: For discrete types T, the existence of an initial value T() and In(De)crementation. Instead of being a big bang of time, boost::date_times default constructors are cannibals. They give birth to a date_time() object that they immediately swallow. Then saying schizophrenic things about themselves like: cout << to_simple_string(ptime()); "not-a-date-time" They are singularities having an infinite gravitational pull on any operators. You can apply as many of them on the constructed black whole and they are never seen again having no effect. In addition to making the expected source of all elements of boost::date_time an awesome creature to be avoided, the universal c++ way of saying the next object within minimal distance '++' has also been taken away from me. May be with the good intension to prevent me from doing silly things like iterating over whole centuries in nanoseconds ;-) Yet all my applications of ++ and -- in my generic code use them only once like in comparing intervals of discrete types (a, b] == [c, d] is true, if(++a==c && b == d) that have different borders. I only have to make sure here that the minimal distance is proceeded only once. To summarize: What I have been relying on in my design, an initial default ctor T() and an increment operator ++ on the least availabel distance unit is exactly not provided by boost::date_time. Now the good news: Both libraries are so well designed that these seemingly contrarian designs can still be coupled with relatively small changes. Specifically, which is crucial for me, I can adapt my libraries code so that I can use all of boost::date_time in it's current form without patching anything (which would also work as I wrote in my last posting). The seemingly most decoupled remedy was, to implement the the missing functions type<T>::neutron() as well as increment ++ and decrement -- operators using the public interface of boost::date_time. This had to be done for every boost::date_time type like e.g. ptime: itl/ptime_adapter.hpp: ------------------------------------------------------------- boost::posix_time::ptime operator ++(boost::posix_time::ptime& x) { return x += boost::posix_time::ptime::time_duration_type::unit(); } boost::posix_time::ptime operator --(boost::posix_time::ptime& x) { return x -= boost::posix_time::ptime::time_duration_type::unit(); } template<> inline boost::posix_time::ptime type<boost::posix_time::ptime>::neutron() { return boost::posix_time::ptime(boost::posix_time::min_date_time); } ------------------------------------------------------------- Drawbacks of this are: (1) Designs are not completely decoupled, for every boost::a_date_time the matching adapterfunctions must be included. This is a complication for the usage. (2) *Every* generic library that relies on the existence of an initial default ctor and incrementation has to provide an adapter. (3) Every adapter has to be maintained, when boost::date_time is changed. (Kind of a distributed responsibility that is error prone) I would suggest, that a type that in it's basic characteristics resembles an intergral numeric type like boosts date_time types, that type should follow the concept Integral. cheers Joachim Interval Template Library download from http://sourceforge.net/projects/itl documentation http://www.herold-faulhaber.de/

on Mon May 26 2008, "Joachim Faulhaber" <afojgo-AT-googlemail.com> wrote:
Hi!
My first goal in boostifying my proposed interval template library (ITL http://sourceforge.net/projects/itl) was to provide good examples for interval_sets and interval_maps using boost::date_time objects. This was much harder than I had expected, because incidentally the most important syntactical and semantical requirements that I do rely on for a parameter T of itl::interval<T> are not provided by boost::date_time.
The one property of boost::date_time that knocked my socks off was the default constructor that 'is-not-a-date-time'. I paraphrase this as: boost::time is lacking a big bang ;-)
Default constructors in nearly all the types that I've seen in my lifetime (I know the realm of my unknown unknowns is vast ;-) are intended to be elements of the type they do construct.
Well, sure, but the built-ins in C++ generally default construct as singular "degenerate" elements that you can assign to and destruct, and nothing else.
Moreover in so many cases including built in types, default ctors serve as *initial elements*. Other elements can be *reached* from T() applying operations of T on them.
This applies to all built in types
You must be thinking of value-initialization. Please read http://www.boost.org/doc/libs/1_35_0/libs/utility/value_init.htm
and many std::types, like e.g. string, set, list etc. The most simple instance is unsigned int() with increment operation ++ that resembles the Peano axiomatic operations 0 and successor for natural numbers.
This semantics of default ctors, at least in common built in and standard types and also in so many user defined types is so fundamental that I took it as a *semantical invariant* for the construction of my library.
Having a singular value definitely weakens the invariant of a type, but that doesn't make it an invalid approach. It just means the invariant is broader than it would be otherwise, and includes the possibility of this singular value. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

2008/5/27 David Abrahams <dave@boostpro.com>:
The one property of boost::date_time that knocked my socks off was the default constructor that 'is-not-a-date-time'. I paraphrase this as: boost::time is lacking a big bang ;-)
Default constructors in nearly all the types that I've seen in my lifetime (I know the realm of my unknown unknowns is vast ;-) are intended to be elements of the type they do construct.
Well, sure, but the built-ins in C++ generally default construct as singular "degenerate" elements that you can assign to and destruct, and nothing else.
My generic library code has no problem with the 'value nature' of built in types. Because from the initial default constructed value unsigned int x = int(); // x = 0 you can reach ++x // x = 1 ... ++x // x = 2^32 and thus all values of the types set of values. This is not the case for boost::date_time objects like e.g. ptime ptime x = ptime(); // x = "not-a-date-time" // ++x error ++ not available x += ptime::time_duration_type::unit(); // x = "not-a-date-time" ... x += ptime::time_duration_type::unit(); // x = "not-a-date-time" no element of the set of values of ptime is reachable from the default ctor with any available ptime operations or functions.
Moreover in so many cases including built in types, default ctors serve as *initial elements*. Other elements can be *reached* from T() applying operations of T on them.
This applies to all built in types
You must be thinking of value-initialization. Please read http://www.boost.org/doc/libs/1_35_0/libs/utility/value_init.htm
This is helpful in general. Yet, as far as I can see no remedy for the singularity default constructor problem that I had with boost::date_time value_initialized<ptime> x; // x = "not-a-date-time" // ++x error ++ not available x += ptime::time_duration_type::unit(); // x = "not-a-date-time" ... x += ptime::time_duration_type::unit(); // x = "not-a-date-time" same problems. I am not yet able to discover the advantages of such default ctor construction especially if it derails generic code that works well with standard types. Regards Joachim --- Interval Template Library (ITL) download from http://sourceforge.net/projects/itl documentation http://www.herold-faulhaber.de/

On Tue, May 27, 2008 at 5:06 PM, Joachim Faulhaber <afojgo@googlemail.com> wrote:
My generic library code has no problem with the 'value nature' of built in types. Because from the initial default constructed value
unsigned int x = int(); // x = 0 you can reach ++x // x = 1 ... ++x // x = 2^32
and thus all values of the types set of values.
This is not the case for boost::date_time objects like e.g. ptime
Nor is it the case for floating point numbers. Nor, technically, for signed numbers, where overflow is impl-defined (or worse, afaik).

2008/5/29, Scott McMurray <me22.ca+boost@gmail.com>:
On Tue, May 27, 2008 at 5:06 PM, Joachim Faulhaber <afojgo@googlemail.com> wrote:
My generic library code has no problem with the 'value nature' of built in types. Because from the initial default constructed value
unsigned int x = int(); // x = 0 you can reach ++x // x = 1 ... ++x // x = 2^32
and thus all values of the types set of values.
This is not the case for boost::date_time objects like e.g. ptime
Nor is it the case for floating point numbers.
Yes, but I specifically addressed integral numerical types here
Nor, technically, for signed numbers, where overflow is impl-defined (or worse, afaik).
speaking of the finite set of machine represented values and their reachability by any operators (e.g. --) of a signed integral numerical type, yes. I am only emphasizing on default ctor and ++ because these were the minimally required operations on an integral types T in itl::interval<T> to write code that seemed to be fairly generic. Regards Joachim

To answer your question with a question: What actual date/time do you think a default-constructed date_time should be, and by what time interval should ++ increment it? To me it seems that any answers to these two questions must be completely arbitrary, in which case I'd question the usefulness of defining them. While I understand your initial distaste for the idea, a singular value actually seems like a pretty good way out. I haven't looked at your interval library (although I probably will, it sounds interesting), but one idea for using it with date_time might be to wrap date_time objects in a class of your own, which defines its own "big bang" time and default increment interval, e.g. 1 second. Colin

Colin Caughie wrote:
To answer your question with a question: What actual date/time do you think a default-constructed date_time should be, and by what time interval should ++ increment it?
To me it seems that any answers to these two questions must be completely arbitrary, in which case I'd question the usefulness of defining them. While I understand your initial distaste for the idea, a singular value actually seems like a pretty good way out.
Some date libraries consider default constructed dates to mean "today". JavaScript for example. -- Sohail Somani http://uint32t.blogspot.com

2008/5/29, Sohail Somani <sohail@taggedtype.net>:
Colin Caughie wrote:
To answer your question with a question: What actual date/time do you think a default-constructed date_time should be, and by what time interval should ++ increment it?
To me it seems that any answers to these two questions must be completely arbitrary, in which case I'd question the usefulness of defining them. While I understand your initial distaste for the idea, a singular value actually seems like a pretty good way out.
Some date libraries consider default constructed dates to mean "today". JavaScript for example.
and may be today is the only day that ever exists ;-) Joachim

Joachim Faulhaber wrote:
2008/5/29, Sohail Somani <sohail@taggedtype.net>:
Colin Caughie wrote:
To answer your question with a question: What actual date/time do you think a default-constructed date_time should be[snip]
Some date libraries consider default constructed dates to mean "today". JavaScript for example.
and may be today is the only day that ever exists ;-)
I don't think boost::date_time should handle such philosophical questions. ;-) -- Sohail Somani http://uint32t.blogspot.com

Hi Colin, 2008/5/29, Colin Caughie <c.caughie@indigovision.com>:
To answer your question with a question: What actual date/time do you think a default-constructed date_time should be, and by what time interval should ++ increment it?
To me it seems that any answers to these two questions must be completely arbitrary, in which case I'd question the usefulness of defining them.
From my view as an author of generic code (in this case interval-template code) these questions have been quite determined:
Coding for instance equality of intervals with open and closed bounds, if a,b,c and d are values of a discrete type, like an integer based time, the assumption, that there is a least unit (resolution) on the values of that type is inevitable. (a, b] == [c, d] is true, if(++a==c && b == d) If the resolution is nanosec and I would increment in seconds the result would be wrong. In addition, for an object like an empty interval [1,0] you need to generically express nullness and oneness. Time is theoretically continuous but technically it's defined by a finite unit "the duration of 9 192 631 770 periods of the radiation corresponding to the transition between the two hyperfine levels of the ground state of the caesium 133 atom" (http://en.wikipedia.org/wiki/Second). the ++ here is not the second but the time of the transition between levels. Many date and time classes like boost::date_time classes are implemented via an unsigned integral. So there is a finest resolution on them and thus a smallest representable duration. Incrementation (operator ++) has to be the incrementaton of such smallest unit. Integer implemented date and time classes also have a minimal representable time. This is naturally given by the 0 value of the implementing unsigned integer. From the (limited) point of view of such a special implementation, this is the beginning of all time and thus the awesome event of a big bang ;-) This would be my favourite candidate for a default constructor. Of course the time origin is still a little arbitrary. Yet for my generic code any value between min_time and max_time-1 was workable, even a moving one (like today, which I won't prefer), but unfortunately not a singularity.
While I understand your initial distaste for the idea, a singular value actually seems like a pretty good way out.
I haven't looked at your interval library (although I probably will, it sounds interesting), but one idea for using it with date_time might be to wrap date_time objects in a class of your own, which defines its own "big bang" time and default increment interval, e.g. 1 second.
That's what I had to do to make the librares play together: itl/ptime_adapter.hpp: ------------------------------------------------------------- boost::posix_time::ptime operator ++(boost::posix_time::ptime& x) { return x += boost::posix_time::ptime::time_duration_type::unit(); } boost::posix_time::ptime operator --(boost::posix_time::ptime& x) { return x -= boost::posix_time::ptime::time_duration_type::unit(); } template<> inline boost::posix_time::ptime type<boost::posix_time::ptime>::neutron() { return boost::posix_time::ptime(boost::posix_time::min_date_time); } ------------------------------------------------------------- cheers Joachim Interval Template Library download from http://sourceforge.net/projects/itl documentation http://www.herold-faulhaber.de/
participants (5)
-
Colin Caughie
-
David Abrahams
-
Joachim Faulhaber
-
Scott McMurray
-
Sohail Somani