Boost logo

Boost :

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


On May 4, 2013, at 7:33 PM, "Vicente J. Botet Escriba" <vicente.botet_at_[hidden]> wrote:

> Le 04/05/13 23:09, Howard Hinnant a écrit :
>> Thanks. I'm curious about some of your semantics...
>>
>> On May 4, 2013, at 4:15 PM, "Vicente J. Botet Escriba" <vicente.botet_at_[hidden]> wrote:
>>
>>> I have just extracted from my current interface some of the functions that we could have in a full featured date class.
> Glad to see that we can start to talk of concrete things.

Me too. :-)

>>>
>>> class date;
>>>
>>> date today();
>> In what timezone?
> UTC

Ok.

>>
>>> class date {
>> What is the range of validity? Proleptic gregorian or auto-coverting to julian?
> [year(-32767)/jan/1 thru year(32767)/dec/31]

Sounds familiar. ;-) I'm a fan of making the year range so large, that maybe, just maybe, explicit range checking on the year becomes unnecessary. But I'm not confident in the definition of "so large" that would enable dropping range checking on the year. I've recently been experimenting with field<->serial conversions that go out +/- 5 million years with 32 bit arithmetic (or far past the age of the universe with 64 bit arithmetic).

>
> Proleptic gregorian

My strong preference too. Julian is a different calendar/type. <aside> And I've never learned why the Gregorian committee changed the epoch by 2 days with respect to the Julian calendar. I.e. the calendars don't align at year 0 or year 1, they align at year 200. Probably had to do with calculating Easter... </aside>.

>> What is the internal representation?
> I'm not talking here of a concrete date class yet, but much more of an archetype of Date concept. We could see the if it is better to make concrete classes that provide less as not efficient.
>>
>>> public:
>>> // construct/copy/destruct
>>> date(); // |year(0)/jan/1|
>>> date(year, month, day);
>>> date(year, month, day, no_check_t);
>> Do the year, month and day types range check?
> Let me say for the time been that the re is no check.
>> Is the conversion from int to year implicit or explicit?
> explicit.
>> Is there a conversion from year to int? And if so, explicit or implicit?
> yes implicit.

Sounds good to me.

>>
>>> // and all the usual combinations
>>> bool set_if_valid_date(year, month, day);
>> Return true if was set?
> Yes.
>>
>>> day day() const; // or explict operator day(); the same for the other accessors.
>> I've been experimenting with the explicit operator too. Haven't decided whether or not I like it yet.
> Neither me.
>>
>>> month month() const;
>>> year year() const;
>>>
>>> date(year, week, weekday);
>>> date(year, week, weekday, no_check_t);
>>> bool set_if_valid_date(year, week, weekday);
>>> week week() const;
>>> weekday weekday() const;
>>>
>>> date(year, day_of_year);
>>> date(year, day_of_year, no_check_t);
>>> bool set_if_valid_date(year, day_of_year);
>>> day_of_year day_of_year() const;
>>>
>>> explicit date(days);
>>> date(days, no_check_t);
>>> bool set_if_valid_date(days);
>>> days days_since_epoch(); // or time_since_epoch() or just days()
>> Is the epoch specified or left unspecified?
> specified.
>
> year(0)/jan/1

My more recent field<->serial conversion formulas have been using year(1)/jan/1 as day 0, just because it made the conversion formulas simpler (and the range got extended to +/- 5 million years!). But the formulas can always be easily adjusted by an offset. Indeed, I think the most convenient epoch for the serial type, and the client, is going to be unspecified, except that it is equal to the chrono::system_clock epoch. That allows arithmetic to naturally mix system_clock::time_point and the serial date type, especially if the serial date type is a chrono::time_point. E.g. time since midnight:

system_clock::time_point tp = ... // serial date_time type
auto dp = time_point_cast<days>(tp); // serial date type
auto time_since_midnight = tp - dp; // serial date_time - serial date == duration type
year_month_day ymd = dp; // field date type
...

>
>>
>>> explicit date(system_clock::time_point);
>>> operator system_clock::time_point() const;
>> Do the above two assume the UTC timezone?
> Copy/paste from your original proposal.
>
> explicit date(chrono::system_clock::time_point tp);
>
> /Effects:/ |tp| is converted to UTC, and then trucated to 00:00:00
> hours. A |date |is created which reflects this point in time.
> /Throws:/ If the conversion from |tp| overflows the range of |date|,
> throws an exception of type |bad_date|.
> explicit operator chrono::system_clock::time_point () const;
>
> /Returns:/ A |chrono::system_clock::time_point| which represents the
> date referred to by |*this| at 00:00:00 UTC.
> /Throws:/ If the conversion to |tp| overflows the range of
> |chrono::system_clock::time_point|, throws an exception of type
> |bad_date|.
>>
>>> bool is_valid() const;
>>> bool is_leap_year() const;
>>>
>>> date & operator+=(days);
>>> date & operator++();
>>> date operator++(int);
>>> date & operator-=(days);
>>> date & operator--();
>>> date operator--(int);
>>> date & operator+=(months);
>>> date & operator-=(months);
>> What semantics do you use for month arithmetic?
> For non-contextual dates (absolute) increase the month(module 12) and carry on the year and throw exception if the resulting date is invalid.
>>
>>> date & operator+=(years);
>>> date & operator-=(years);
>> What semantics do you use for year arithmetic?
> For non-contextual dates (absolute) increase the year and throw exception if the resulting date is invalid.
>>
>>> // friend functions
>>> friend date operator+(date, days);
>>> friend date operator+(days, date);
>>> friend date operator-(date, days);
>>> friend days operator-(date, date);
>>> friend date operator+(date, months);
>>> friend date operator+(months, date);
>>> friend date operator-(date, months);
>>> friend date operator+(date, years);
>>> friend date operator+(years, date);
>>> friend date operator-(date, years);
>>> friend bool operator==(const date &, const date &);
>>> friend bool operator<(const date &, const date &);
>>> friend bool operator!=(const date &, const date &);
>>> friend bool operator>(const date &, const date &);
>>> friend bool operator<=(const date &, const date &);
>>> friend bool operator>=(const date &, const date &);
>>>
>>> };
>> I'm not seeing the ability to get the number of days in the current month, aside from building my own table and indexing into it with the month() accessor.
> I have not written all the song. This could be added as I added is_leap. I added explicitly is_leap as some date representations could choose to store if the year is leap or not (in particular some of yours implementation do it). The class year has some useful functions that can be used by someone writing its own date class.
>
> class year : public bounded< year_tag,-32768, 32767> // defines value(), is_valid(), ...
> {
> public:
> // construct/copy/destruct
> explicit year(int); // no check
>
> // public member functions
> days days_in() const; // number of days in this year
> days days_in(month) const; // number of days in this month for year
> days days_since_epoch() const; // number of days since the epoch
> bool is_leap() const; // whether is a leap year
> };
>
> So yes we could provide a days_in_month() function defined as dt.year().days_in(dt.month()).

<nod> Or as Anurag is exploring: dt.year_month().days_in().

Howard


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