[chrono/date] date conversion and arithmetic with unvalidated dates
Hi, Note that N3344 suggest only day arithmetic for a day representation (which is always valid) and suggest only only a date representation. Could we apply arithmetic operations on a unvalidated date (without striving on UB)? e.g. day arithmetic on a ymd_date ymd_date d1(year(2013), may, day(66)); ymd_date d2 = d1 + day(1); If defined, which must be the value of d2? Could we convert a date representation to another one if the source date is unvalidated (without striving on UB)? e.g. ymd_date -> days_date ymd_date d1(year(2013), may, day(66)); days_date d2 = d1; If defined, which must be the value of d2? Best, Vicente
On May 6, 2013, at 6:30 AM, "Vicente J. Botet Escriba"
Hi,
Note that N3344 suggest only day arithmetic for a day representation (which is always valid) and suggest only only a date representation.
Could we apply arithmetic operations on a unvalidated date (without striving on UB)? e.g. day arithmetic on a ymd_date
ymd_date d1(year(2013), may, day(66)); ymd_date d2 = d1 + day(1);
If defined, which must be the value of d2?
Could we convert a date representation to another one if the source date is unvalidated (without striving on UB)? e.g. ymd_date -> days_date
ymd_date d1(year(2013), may, day(66)); days_date d2 = d1;
If defined, which must be the value of d2?
My understanding is that all of these operations (if unchecked at ymd_date construction time) are simply undefined behavior. This is what N3344 seems to be asking for. Howard
Le 06/05/13 15:22, Howard Hinnant a écrit :
On May 6, 2013, at 6:30 AM, "Vicente J. Botet Escriba"
wrote: Hi,
Note that N3344 suggest only day arithmetic for a day representation (which is always valid) and suggest only only a date representation.
Could we apply arithmetic operations on a unvalidated date (without striving on UB)? e.g. day arithmetic on a ymd_date
ymd_date d1(year(2013), may, day(66)); ymd_date d2 = d1 + day(1);
If defined, which must be the value of d2?
Could we convert a date representation to another one if the source date is unvalidated (without striving on UB)? e.g. ymd_date -> days_date
ymd_date d1(year(2013), may, day(66)); days_date d2 = d1;
If defined, which must be the value of d2? My understanding is that all of these operations (if unchecked at ymd_date construction time) are simply undefined behavior. This is what N3344 seems to be asking for.
N3344 doesn't suggest month and year arithmetic at all. They suggest to let these features for a higher layer. Should the following pass. ymd_date dt(year(2012),feb,day(29)); try { dt+=years(1); BOOST_TEST(false); } catch (...) {} Or should dt contain an invalid date? I would expect that an exception is thrown. In the same way we are proposing constructors and factories that allows the user to check/uncheck the validity of the entered date, arithmetic operators can be considered also as date factories I'd propose that arithmetic operators using operators validates the result. For those that don't want to pay for validity check we can add additional functions that don't check the validity date& date::inc(years); date& date::pre_inc(); date& date::post_inc(); date& date::dec(years); date& date::pre_dec(); date& date::post_dec(years); date sum(date, years); date diff(date, years); Or shouldn't we have two different classes, validated and unvalidated dates, as Rob has suggested? More I analyze the subject more I believe the separation is unavoidable. validated_date dt(year(2012),feb,day(29)); try { dt+=years(1); assert(false); } catch (...) {} unvalidated_date dt(year(2012),feb,day(29)); try { dt+=years(1); // the result of dt is 2013/feb/29 which is unchecked and invalid } catch (...) { assert(false); } The user can move from one date type to the other unvalidated_date dt(year(2012),feb,day(29)); try { dt=validate(dt) + years(1); } catch (...) { assert(false); } Best, Vicente
Vicente Botet wrote
Le 06/05/13 15:22, Howard Hinnant a écrit :
On May 6, 2013, at 6:30 AM, "Vicente J. Botet Escriba" <
vicente.botet@
> wrote:
Hi,
Note that N3344 suggest only day arithmetic for a day representation (which is always valid) and suggest only only a date representation.
Could we apply arithmetic operations on a unvalidated date (without striving on UB)? e.g. day arithmetic on a ymd_date
ymd_date d1(year(2013), may, day(66)); ymd_date d2 = d1 + day(1);
If defined, which must be the value of d2?
Could we convert a date representation to another one if the source date is unvalidated (without striving on UB)? e.g. ymd_date -> days_date
ymd_date d1(year(2013), may, day(66)); days_date d2 = d1;
If defined, which must be the value of d2? My understanding is that all of these operations (if unchecked at ymd_date construction time) are simply undefined behavior. This is what N3344 seems to be asking for.
N3344 doesn't suggest month and year arithmetic at all. They suggest to let these features for a higher layer.
I thought it does. It even includes a benchmark for increasing in terms of months.
Should the following pass.
ymd_date dt(year(2012),feb,day(29)); try { dt+=years(1); BOOST_TEST(false); } catch (...) {}
Or should dt contain an invalid date? I would expect that an exception is thrown.
In the same way we are proposing constructors and factories that allows the user to check/uncheck the validity of the entered date, arithmetic operators can be considered also as date factories
I'd propose that arithmetic operators using operators validates the result. For those that don't want to pay for validity check we can add additional functions that don't check the validity
date& date::inc(years); date& date::pre_inc(); date& date::post_inc(); date& date::dec(years); date& date::pre_dec(); date& date::post_dec(years); date sum(date, years); date diff(date, years);
Or shouldn't we have two different classes, validated and unvalidated dates, as Rob has suggested? More I analyze the subject more I believe the separation is unavoidable.
validated_date dt(year(2012),feb,day(29)); try { dt+=years(1); assert(false); } catch (...) {}
unvalidated_date dt(year(2012),feb,day(29)); try { dt+=years(1); // the result of dt is 2013/feb/29 which is unchecked and invalid } catch (...) { assert(false); }
The user can move from one date type to the other
unvalidated_date dt(year(2012),feb,day(29)); try { dt=validate(dt) + years(1); } catch (...) { assert(false); }
Best, Vicente
I am afraid I don't like this separation of dates. The current model as it stands simply says that the dates constructed with "raw" constructor are undefined. This doesn't mean that they are discarded and can't be used: one natural interpretation is that the dates created are assumed to be valid. The implementation essentially has faith in the programmer for feeding it the correct value. Also, dates constructed with validation check are of course valid. Combining the two, we have always valid dates when created. Thus, operations define their behaviour on valid dates only. It doesn't mean operation is always defined for valid dates. As your example suggests, "2013-02-29" + 1 year = "2013-02-29" which doesn't exist! Thus it is possible for valid dates to have no defined answer for that operation. We can either throw an error, or extend our operation to include those dates, say "29 feb" + 1 year maps to "28-feb" now. The same can be said in the cases of underflow or overflow. The path we choose for those dates can be argued and debated, but what we are not arguing is the validity of the input dates. But as you rightly ponder over is how to validate an unvalidated date. We can always create a date from the previous date using functions that validate it, say make_date(). To ease it, we can create function: make_date(date unvalidated); make_date() as before uses validation while construction of dates. The question though remains whether we can ever check a date for validation. As I said before, it is impossible for serial-based implementation. So, I am in favour scrapping any such effort and dumping any interaction with an unvalidated date in the undefined behaviour. It indicates to user to proceed with caution. If he/she does, well good luck! They can always build validation in their own system as they see fit. -- View this message in context: http://boost.2283326.n4.nabble.com/chrono-date-date-conversion-and-arithmeti... Sent from the Boost - Dev mailing list archive at Nabble.com.
Le 06/05/13 22:57, Anurag Kalia a écrit :
Vicente Botet wrote
Le 06/05/13 15:22, Howard Hinnant a écrit :
On May 6, 2013, at 6:30 AM, "Vicente J. Botet Escriba" < vicente.botet@ > wrote:
Hi,
Note that N3344 suggest only day arithmetic for a day representation (which is always valid) and suggest only only a date representation.
Could we apply arithmetic operations on a unvalidated date (without striving on UB)? e.g. day arithmetic on a ymd_date
ymd_date d1(year(2013), may, day(66)); ymd_date d2 = d1 + day(1);
If defined, which must be the value of d2?
Could we convert a date representation to another one if the source date is unvalidated (without striving on UB)? e.g. ymd_date -> days_date
ymd_date d1(year(2013), may, day(66)); days_date d2 = d1;
If defined, which must be the value of d2? My understanding is that all of these operations (if unchecked at ymd_date construction time) are simply undefined behavior. This is what N3344 seems to be asking for. N3344 doesn't suggest month and year arithmetic at all. They suggest to let these features for a higher layer. I thought it does. It even includes a benchmark for increasing in terms of months. I has a bechmarc about month arithmetic, bu the chapter 8 Recommendations exclude it and Appendix A shows an stereotype of the recommendation without any mention to month arithmetic.
Should the following pass.
ymd_date dt(year(2012),feb,day(29)); try { dt+=years(1); BOOST_TEST(false); } catch (...) {}
Or should dt contain an invalid date? I would expect that an exception is thrown.
In the same way we are proposing constructors and factories that allows the user to check/uncheck the validity of the entered date, arithmetic operators can be considered also as date factories
I'd propose that arithmetic operators using operators validates the result. For those that don't want to pay for validity check we can add additional functions that don't check the validity
date& date::inc(years); date& date::pre_inc(); date& date::post_inc(); date& date::dec(years); date& date::pre_dec(); date& date::post_dec(years); date sum(date, years); date diff(date, years);
Or shouldn't we have two different classes, validated and unvalidated dates, as Rob has suggested? More I analyze the subject more I believe the separation is unavoidable.
validated_date dt(year(2012),feb,day(29)); try { dt+=years(1); assert(false); } catch (...) {}
unvalidated_date dt(year(2012),feb,day(29)); try { dt+=years(1); // the result of dt is 2013/feb/29 which is unchecked and invalid } catch (...) { assert(false); }
The user can move from one date type to the other
unvalidated_date dt(year(2012),feb,day(29)); try { dt=validate(dt) + years(1); } catch (...) { assert(false); }
Best, Vicente I am afraid I don't like this separation of dates. The current model as it stands simply says that the dates constructed with "raw" constructor are undefined. This doesn't mean that they are discarded and can't be used: one natural interpretation is that the dates created are assumed to be valid. The implementation essentially has faith in the programmer for feeding it the correct value. Agreed. N3344 propose just a unchecked serial date without not too much features that corresponds to what I named unvalidated_date. The question is how we can extend the design to cover better with unchecked and checked dates and how they can interact. Whether we have one class or two is IMO an open point, from all the discussion we have had.
Also, dates constructed with validation check are of course valid. Combining the two, we have always valid dates when created. Thus, operations define their behaviour on valid dates only. It doesn't mean operation is always defined for valid dates. As your example suggests, "2013-02-29" + 1 year = "2013-02-29" which doesn't exist! Thus it is possible for valid dates to have no defined answer for that operation. We can either throw an error, or extend our operation to include those dates, say "29 feb" + 1 year maps to "28-feb" now.
In order to do that you need some kind of validation. This is exactly my concern. Should the year arithmetic be as efficient as possible (just increate the year) and let the user to ensure the require clauses of the function)? I would say yes, and this should be the behavior for unchecked dates. Or should the operation check if the resulting date is a valid one a throw otherwise? I would say yes, and this should be the behavior for checked dates. Having a single class don't allows us to provide both behaviors and so we need to choose one, or I have showed we need two function flavors, which start to make the interface quite complex. Once we have defined both behaviors and assign them to a specific class the user can move from checked to unchecked and vice versa depending on what she wants to do and using always the same syntax.
The same can be said in the cases of underflow or overflow. The path we choose for those dates can be argued and debated, but what we are not arguing is the validity of the input dates.
But as you rightly ponder over is how to validate an unvalidated date. We can always create a date from the previous date using functions that validate it, say make_date(). To ease it, we can create function:
make_date(date unvalidated);
make_date() as before uses validation while construction of dates. Yes, but the C++type system is not helping us to define the correct behavior.
The question though remains whether we can ever check a date for validation. As I said before, it is impossible for serial-based implementation. Except if you ensure that because the type valid_date has only checked and valid dates :)
So, I am in favour scrapping any such effort and dumping any interaction with an unvalidated date in the undefined behaviour. Well this is your right. It indicates to user to proceed with caution. If he/she does, well good luck! They can always build validation in their own system as they see fit.
As for example? How the user would be able to have a different behavior if we only provide one? Please could tell me which behavior would you want for year arithmetic? Would you need to provide two flavors, one unchecked and one checked, or not? In order to compare the different approaches and take decisions we need to explore them. H.H design was based on valid dates. N3344 is based on unchecked dates. I'm just supporting the Rob idea that maybe we need both with a close interaction if we want a clear design and easy to use/learn interface. Best, Vicente
On May 6, 2013, at 6:54 PM, "Vicente J. Botet Escriba"
Le 06/05/13 22:57, Anurag Kalia a écrit :
I am afraid I don't like this separation of dates. The current model as it stands simply says that the dates constructed with "raw" constructor are undefined.
There's no such thing as a "raw" constructor, unless you name a class "raw". These are semantics you have chosen to apply to constructors versus free functions. Note that "make_date" is practically the same as the constructor for a type named "make_date".
Also, dates constructed with validation check are of course valid. Combining the two, we have always valid dates when created. Thus, operations define their behaviour on valid dates only. It doesn't mean operation is always defined for valid dates. As your example suggests, "2013-02-29" + 1 year = "2013-02-29" which doesn't exist! Thus it is possible for valid dates to have no defined answer for that operation. We can either throw an error, or extend our operation to include those dates, say "29 feb" + 1 year maps to "28-feb" now. In order to do that you need some kind of validation. This is exactly my concern. Should the year arithmetic be as efficient as possible (just increate the year) and let the user to ensure the require clauses of the function)? I would say yes, and this should be the behavior for unchecked dates. Or should the operation check if the resulting date is a valid one a throw otherwise? I would say yes, and this should be the behavior for checked dates.
+1
Once we have defined both behaviors and assign them to a specific class the user can move from checked to unchecked and vice versa depending on what she wants to do and using always the same syntax.
+1
The same can be said in the cases of underflow or overflow. The path we choose for those dates can be argued and debated, but what we are not arguing is the validity of the input dates.
But as you rightly ponder over is how to validate an unvalidated date. We can always create a date from the previous date using functions that validate it, say make_date(). To ease it, we can create function:
make_date(date unvalidated);
make_date() as before uses validation while construction of dates. Yes, but the C++type system is not helping us to define the correct behavior.
Right.
The question though remains whether we can ever check a date for validation. As I said before, it is impossible for serial-based implementation. Except if you ensure that because the type valid_date has only checked and valid dates :)
Right.
So, I am in favour scrapping any such effort and dumping any interaction with an unvalidated date in the undefined behaviour. Well this is your right.
...or wrong (since I don't agree :).
I'm just supporting the Rob idea that maybe we need both with a close interaction if we want a clear design and easy to use/learn interface.
Remember that part of such an interface is to help the user do the right thing. ___ Rob (Sent from my portable computation engine)
participants (4)
-
Anurag Kalia
-
Howard Hinnant
-
Rob Stewart
-
Vicente J. Botet Escriba