Boost logo

Boost :

Subject: Re: [boost] [gsoc 2013] draft proposal for chrono::date
From: Howard Hinnant (howard.hinnant_at_[hidden])
Date: 2013-05-03 10:58:20


I have been reading with interest this thread, but have been sitting on my hands until now. After all this is a gsoc project for Anurag, not for me or anyone else.

But as long as everyone seems free to throw in their design preferences, I will add mine, several of which I've already seen repeated here, but some not.

Unchecked interface:
--------------------

   date(year y, month m, day d);

This is the only order, there is no checking, the year, month and date objects do no validation themselves. They exist only to disambiguate the order of this low level constructor. This single order is chosen above any other because of the ISO standards. The use of the year, month and day objects add some type safety with no run time overhead, and so are acceptable. Each of these objects will *explicitly* convert from int:

   date(year(2013), month(5), day(3)); // ok

   date(2013, 5, 3); // compile time error

I dislike the use of no_check_t at this level because I see it as unnecessarily verbose and ugly, and I'll have to look up how to spell it every time I use it (see the checked interface for why).

Checked interface:
------------------

I like the use of factory functions here. I also like an easy-to-remember, convenience, forgiving, type-safety. My still-preferred spelling for the factory functions (yes, more than 1) is:

   date d = year(2013) / month(5) / day(3);
   date d = year(2013) / month(5) / 3;
   date d = month(5) / day(3) / year(2013);
   date d = month(5) / day(3) / 2013;
   date d = day(3) / month(5) / year(2013);
   date d = day(3) / month(5) / 2013;

These 3 orders are chosen among the possible 6 because these are the 3 that people actually use:

   http://en.wikipedia.org/wiki/Date_format_by_country

The first two units have to explicit. The last unit can be implicit. Other rules are possible, but this one is a nice compromise between convenience and simplicity. It is an easy rule to remember, and the rule is easily checked by the compiler at compile time.

I like this spelling of the checked factory function over:

make_unchecked_date(2013, may, 3);
or

make_valid_date(2013, may, 3);

because I know I will remember how to spell the former 3 years from now, but I will have to look the latter up in a reference, unless I'm dealing with dates often enough that I memorize it.

In both interfaces, the year, month and day objects do no validity checking. The validity checks in the checked interface happen only at the time the full date is constructed.

-----------------

Two date types:

1. Serial:

    typedef duration<int, ratio_multiply<hours::period, ratio<24>>> days;
    typedef time_point<system_clock, days> day_point;

day_point is the serial date type. This is nothing but integral arithmetic with no validity checking. It has some convenience conversions to/from the serial date_time type: system_clock::time_point, and the field-date type...

2. Field date-type:

   Something that stores year, month and day, maybe named date, maybe not. Provides conversions to and from day_point.

These two date types are analogous to the two C date types: time_t and tm. I have come to the conclusion that two types are necessary because each has its strengths and weaknesses in terms of performance. Each type should offer only the operations which it can do fast. E.g. there is no month() accessor on the serial date type, only on the field date type. If you have serial and you need the month, convert to the field type, and then you have it.

This design follows the philosophy of the containers, and whether or not any given container has an operator[](size_t), or a size() member. If you need an operation, and it isn't available on the container you have, you just have to move your data to a container that supports it. This aids in exposing performance bugs. It makes expensive operations explicit.

-------------------------

Both checked and unchecked interfaces should be made constexpr. day_point is already constexpr (in C++14). The field date-type should be constexpr.

Anurag, please feel free to adopt or ignore any or all of this. I can also offer services in the creation of constexpr field<->serial conversions if that would be helpful. Best of luck in your gsoc!

Howard


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk