[gsoc 2013] draft proposal for chrono::date
Hi, I mistakenly sent this draft as a reply to personal email of Vicente. I am sorry! Here it is again, verbatim. One related question though. I still haven't got the hang of a Mailing List. Is there a software takes the pain away from it. For example, I use outlook.com and '>' get appended in front of only the start of paragraph making it wholly unreadable. Thanks, Anurag Kalia. ----------------------------------------
Date: Sat, 27 Apr 2013 18:14:09 +0200 From: vicente.botet@wanadoo.fr CC: anurag.kalia@outlook.com Subject: Re: [boost] [gsoc 2013] draft proposal for chrono::date
Le 27/04/13 16:58, Anurag Kalia a écrit :
I understand that you want to design your own library, I did the same, but getting a coherent design would take you some time. I suggest you to start with the H. Hinnant proposal as a reference and state what you want to added, changed or removes. State clearly why do you want these changes, what would be improved/solved.
Don't forget to inspect the threads/discussion related to the H.Hinnant proposal, and the paper N3344 Toward a Standard C++ 'Date' Class http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3344.pdf Hi Vicente,
Thanks a lot for the exhaustive reply! I will get back to you after going through all the things you pointed out and also the paper you forwarded. But I have one quick question for now, from where can I start my search for the threads and discussions around this date proposal?
Hi,
There were some interesting exchanges in https://groups.google.com/forum/?fromgroups=#!forum/kona-date-lib
I guess that if you request to the administrator s/he will be able to give you read access to the existing threads (there are not too much).
Best, Vicente
I have finally implemented some representative functions in date class and calendar to get a better idea of the proposal. I have dropped the JDN storage for another implementation that I thought of in the middle of the night. I think it is a hybrid approach between serial and field-based implementations. Off the top of my head, without any alternative implementations at hand and benchmarks too, I can't think of something inherently wrong in it. I would really like to hear its gotchas, if any. I have overhauled my proposal on the basis of it. It doesn't contain other headings like about me etc for today. I have tried to incorporate all the previous points you noted before and have also tried to keep naming consistent. I did use uppercase constants in there but they are meant to be private. Is that okay? I knew little about the benchmarks before you sent me that paper. From what I got, they just iterate over a significant period of time and note the start and ending time. Is it more than that? And should I make them before or after I have multiple implementations in hand. I could get hold of your and Howard Hinnant's implementations for them, but then the APIs would have to line up, wouldn't they? Hope you find my proposal better this time around. With an open ear, Anurag Kalia.
Hi Vicente,
Before I start, I want to ask what I should submit as a proposal to Google?
i know I am to implement date class. But the problem arises that the design
and implementation are still taking time to set in.
How can I tell them details that most probably would change in one way or
another? What should I mention in the proposal beyond that I want to
implement date class.
Moreover, what am I expected to do? Should I make all the implementations of
major representations and then test them in the course of making the date
library? I think I should (and probably I would) but I am just asking about
the expectations.
And so on to the actual discussion...
I have just read the representation part. IIUC you choose a compact
representation of a YMD. It has the same liabilities than the YMD
representation (day arithmetic) and adds in addition some drawbacks as you
need to calculate the year, month and day from this representation.
Yeah it does, now that I ponder more over it. My representation (let us call
it serial YMD) has advantage in incrementing and decrementing of months and
years. Currently, its day arithmetic is inefficient but I am sure it can be
made better and mostly on the lines of YMD date only so not much difference
there either way.
Again comparison is a toss-up. Best case of serial YMD takes two comparisons
but for the majority it suffices. YMD takes only one in best cases and
restricts itself to three comparisons at most. I think, simple YMD has
advantage here.
And I/O in YMD can't be bested by any other representation in my opinion. I
thought serial YMD is absorbing advantages of both representations but it
seems to be the opposite.
I don't think that having a single representation would solve the design of
a Date library. I think that each representation could be more efficient for
day arithmetic while been less efficient for month/year arithmetic and
vice-versa. So several representation must be provided. The problem is how
to make all of them more efficient while preserving the integrity.
I was thinking of wrapping the storage fields of date into a class, e.g. ymd
class, serial_raw class, serial_leap class, ymd_cache class etc. All of them
surface the basic functions in the same way like setter and getter functions
[year(), month(), etc], comparison, copy operations etc.
Then the general date<rep> template is able to interact with any other
date<rep2>. For example, an operation of date<ymd> - date<serial> should be
possible through the general operator- function in date template only,
returning a duration in days.
I intend to code a working prototype to show it to you, but I am still in
the middle of it though and should be demonstrable by the next day.
The problem is how to make all of them more efficient while preserving the
integrity.
I don't understand what is being conveyed here. Can I ask you to explain it
better for me please? :)
date construction
I don't like the functional construction.
date d1_2 = date(1, 2, 2011, DMY);
What do you think of the ways I included on my prototype?
/**
* @Effect Constructs a @c ymd_date using the @c year, @c month, @c
day stored in the arguments as follows:
* If the value stored in @c d is outside the range of valid dates for
this month @c m and year @c y,
* throws an exception of type @c bad_date.
* Else constructs a @c ymd_date for which <c>get_year() == y &&
get_month() == m && get_day() == d</c>.
* @Throws @c bad_date if the specified @c ymd_date is invalid.
*/
I can see use of all constructors except the day_of_year constructor. Since
we are making it a p. gregorian calendar, such interface shouldn't be made
public. It should rather be used in dayofyear_claendar where concept of
months doesn't exist, shouldn't it? And I like the way you have designed the
corresponding unchecked functions. As for implementation, one only needs
combined representation month_day and the data if the year is leap or not.
//rough idea:
month_day = (month-1)*31 + (day-1)
We check this value against the seven (six, if leap) days as mentioned in my
serial YMD implementation.
Actually, this approach can be used for any representation because of its
compactness.
Having opaque types for year, month and day you can add as many permutations
as you desire.
The following would not work
//operational
date d3_1 = weekday(wd)[_1st] / may / 2011;
//functional
date d3_2 = date( wd[_1st], may, 2011, DMY);
The problem is that wd[_1st] is not really the same as a day.
Yeah, I thought about that and decided to add a WMY constant. But it would
have just added mental overload of users. It is there just to tell the
compiler about the order of arguments to both compiler and a person who
reads the code. Seeing ymd added to the definition automatically tells what
the date constructor intends to do. weekday() is anyways of a different type
than day() so we can just overload the functions. I genuinely am curious why
adding that fourth parameter might be a bad design.
About the _1st, 2nd, ... , _31th, last constantand you symmetry problem,
what about having the nth to access the one before the last
nth(-1)
or just last_1st last_2nd, ...
Again, this adds extra syntax when the current one can do it all the same.
e.g. a second-to-last date can be constructed by the current syntax itself:
date d = last / jan / 2001 - days(1);
which is also perfectly readable. Shouldn't we avoid to add needless
constants?
I don't see nothing yet about validation of dates. IMO the usual constructor
must validate the date and one less fiendly could be provided when the date
must not be validated. I included a constructor for unchecked dates in my
prototype. What do you think?
ymd_date(chrono::year y, chrono::month m, chrono::day d);
/**
* @Effect Constructs a @c ymd_date constructor from @c year, @c
month, @c day stored in the arguments as follows:
* Constructs a ymd_date so that <c>get_year() == y && get_month() = m
&& get_day() == d</c>.
*/
ymd_date(year::rep y, month::rep m, day::rep d, no_check_t)
BOOST_NOEXCEPT;
As I have said, I like this representation. Average user gets the full
error-checking without any syntactic burden. The person in need of more of
power however gets the uglier representation because it would be much less
required.
Year/Month/Day/WeekDay arithmetic
As your representation is equivalent to the YMD representation all the
algorithm in HH implementation are valid, so there is no need to extend on
this.
I/O again I don't see nothing about input.
I am not sure about this. I thought date construction is only way one can
make dates. Is it possible to change the values of an existing dates? if we
want to change the whole date, we can just use constructors and make them
assign the temporaries to the given date object.
e.g.
date d = date(1, 12, 2001);
d = date(1, 12, 2001);
If one wants to modify individual objects, the function modify may look
like:
date modify_year(int yr)
{
int diff_y = yr - year();
*this += years(diff_y);
return *this;
}
Relations with chrono::system_clock
I would say just that conversion between date and
chrono::time_point
Anurag Kalia wrote
Hi Vicente,
Before I start, I want to ask what I should submit as a proposal to Google? i know I am to implement date class. But the problem arises that the design and implementation are still taking time to set in. How can I tell them details that most probably would change in one way or another? What should I mention in the proposal beyond that I want to implement date class.
For me it is not enough to say you want to implement a date class. H.Hinnant and myself already did that. You need to mention what are the limitation on this proposal and how you plan to solve them.
Moreover, what am I expected to do? Should I make all the implementations of major representations and then test them in the course of making the date library? I think I should (and probably I would) but I am just asking about the expectations.
Again H.Hinnant and myself already do the major representations. What needs to be done is to provide the interface that satisfy most of the user expectations (don't forget that performances was one of the main issues of H.H. proposal.
And so on to the actual discussion...
I have just read the representation part. IIUC you choose a compact representation of a YMD. It has the same liabilities than the YMD representation (day arithmetic) and adds in addition some drawbacks as you need to calculate the year, month and day from this representation. Yeah it does, now that I ponder more over it. My representation (let us call it serial YMD) has advantage in incrementing and decrementing of months and years. Currently, its day arithmetic is inefficient but I am sure it can be made better and mostly on the lines of YMD date only so not much difference there either way.
Again comparison is a toss-up. Best case of serial YMD takes two comparisons but for the majority it suffices. YMD takes only one in best cases and restricts itself to three comparisons at most. I think, simple YMD has advantage here.
And I/O in YMD can't be bested by any other representation in my opinion. I thought serial YMD is absorbing advantages of both representations but it seems to be the opposite.
Right, so no need to provide an additional representation.
I don't think that having a single representation would solve the design of a Date library. I think that each representation could be more efficient for day arithmetic while been less efficient for month/year arithmetic and vice-versa. So several representation must be provided. The problem is how to make all of them more efficient while preserving the integrity. I was thinking of wrapping the storage fields of date into a class, e.g. ymd class, serial_raw class, serial_leap class, ymd_cache class etc. All of them surface the basic functions in the same way like setter and getter functions [year(), month(), etc], comparison, copy operations etc.
Then the general date <rep> template is able to interact with any other date <rep2> . For example, an operation of date <ymd> - date <serial> should be possible through the general operator- function in date template only, returning a duration in days.
I intend to code a working prototype to show it to you, but I am still in the middle of it though and should be demonstrable by the next day.
I don't mind if the different date class are provided by a date<Representation> template class or by several classes that interact each one with each other. The advantage of independent classes is that the user could add its own date class that works well with the others.
The problem is how to make all of them more efficient while preserving the integrity. I don't understand what is being conveyed here. Can I ask you to explain it better for me please? :)
You can see the project as: Given the H.Hinnant design and implementation, what needs to be changed so that the performances are improved? * date validation versus "sur parolle" valid dates, * relative dates should not reduce the performances of absolute dates, * some algorithms are more efficient with one or another representation.
date construction
I don't like the functional construction.
date d1_2 = date(1, 2, 2011, DMY);
What do you think of the ways I included on my prototype?
/** * @Effect Constructs a @c ymd_date using the @c year, @c month, @c day stored in the arguments as follows: * If the value stored in @c d is outside the range of valid dates for this month @c m and year @c y, * throws an exception of type @c bad_date. * Else constructs a @c ymd_date for which <c> get_year() == y && get_month() == m && get_day() == d </c> . * @Throws @c bad_date if the specified @c ymd_date is invalid. */ I can see use of all constructors except the day_of_year constructor.
Why? This is an alternative representation.
Since we are making it a p. gregorian calendar, such interface shouldn't be made public. It should rather be used in dayofyear_claendar where concept of months doesn't exist, shouldn't it?
I disagree here. 125th day of 2013 is a valid date.
And I like the way you have designed the corresponding unchecked functions. As for implementation, one only needs combined representation month_day and the data if the year is leap or not.
//rough idea:
month_day = (month-1)*31 + (day-1)
We check this value against the seven (six, if leap) days as mentioned in my serial YMD implementation. Actually, this approach can be used for any representation because of its compactness.
Sorry, but I don't see any added value to your compact YMD representation.
Having opaque types for year, month and day you can add as many permutations as you desire.
The following would not work //operational date d3_1 = weekday(wd)[_1st] / may / 2011; //functional date d3_2 = date( wd[_1st], may, 2011, DMY);
The problem is that wd[_1st] is not really the same as a day. Yeah, I thought about that and decided to add a WMY constant. But it would have just added mental overload of users. It is there just to tell the compiler about the order of arguments to both compiler and a person who reads the code. Seeing ymd added to the definition automatically tells what the date constructor intends to do. weekday() is anyways of a different type than day() so we can just overload the functions. I genuinely am curious why adding that fourth parameter might be a bad design.
Well, this is a taste question. I will comeback to your functional format later.
About the _1st, 2nd, ... , _31th, last constants and you symmetry problem, what about having the nth to access the one before the last
nth(-1)
or just last_1st last_2nd, ... Again, this adds extra syntax when the current one can do it all the same. e.g. a second-to-last date can be constructed by the current syntax itself:
date d = last / jan / 2001 - days(1);
which is also perfectly readable. Shouldn't we avoid to add needless constants?
Good point.
I don't see nothing yet about validation of dates. IMO the usual constructor must validate the date and one less fiendly could be provided when the date must not be validated. I included a constructor for unchecked dates in my prototype. What do you think?
ymd_date(chrono::year y, chrono::month m, chrono::day d); /** * @Effect Constructs a @c ymd_date constructor from @c year, @c month, @c day stored in the arguments as follows: * Constructs a ymd_date so that <c> get_year() == y && get_month() = m && get_day() == d </c> . */ ymd_date(year::rep y, month::rep m, day::rep d, no_check_t) BOOST_NOEXCEPT; As I have said, I like this representation. Average user gets the full error-checking without any syntactic burden. The person in need of more of power however gets the uglier representation because it would be much less required.
As your representation is equivalent to the YMD representation all the algorithm in HH implementation are valid, so there is no need to extend on this. I/O again I don't see nothing about input. I am not sure about this. I thought date construction is only way one can make dates. Is it possible to change the values of an existing dates? if we want to change the whole date, we can just use constructors and make
Year/Month/Day/WeekDay arithmetic them assign the temporaries to the given date object.
e.g. date d = date(1, 12, 2001); d = date(1, 12, 2001);
If one wants to modify individual objects, the function modify may look like:
date modify_year(int yr) { int diff_y = yr - year(); *this += years(diff_y); return *this; }
I was talking about stream Input/output
Relations with chrono::system_clock
I would say just that conversion between date and chrono::time_point<chrono::system_clock, chrono::days> can be done in both direction. If we want to convert into both directions, non-member functions can be employed:
date to_date(time_point<system_clock, days>); //and time_point<system_clock, days> to_time_point(date);
Moreover, I realized I should start putting my date classes into appropriate namespaces, eg. chrono::days.
What I mean is that a date can not inherit from a days point, this is horrible. More later, Vicente -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
My latest proposal with changes is attached. Anurag_Kalia_chrono_date_Project_Proposal.docx http://boost.2283326.n4.nabble.com/file/n4646428/Anurag_Kalia_chrono_date_Pr... Vicente Botet wrote
Hi Vicente,
Before I start, I want to ask what I should submit as a proposal to Google? i know I am to implement date class. But the problem arises that the design and implementation are still taking time to set in. How can I tell them details that most probably would change in one way or another? What should I mention in the proposal beyond that I want to implement date class. For me it is not enough to say you want to implement a date class. H.Hinnant and myself already did that. You need to mention what are the limitation on this proposal and how you
Anurag Kalia wrote plan to solve them.
Moreover, what am I expected to do? Should I make all the implementations of major representations and then test them in the course of making the date library? I think I should (and probably I would) but I am just asking about the expectations. Again H.Hinnant and myself already do the major representations. What needs to be done is to provide the interface that satisfy most of the user expectations (don't forget that performances was one of the main issues of H.H. proposal.
Thanks! Now I know which direction to take.
You can see the project as: Given the H.Hinnant design and implementation, what needs to be changed so that the performances are improved? * date validation versus "sur parolle" valid dates, * relative dates should not reduce the performances of absolute dates, * some algorithms are more efficient with one or another representation.
Okay. As I have already mentioned, an application requiring absolute performance from date class would use its operations. And nothing can beat a serial representation at comparisons, assignments and day/week arithmetic. ymd represntations, OTOH, optimize I/O. And an application requiring such optimization can simply employ a representation like: struct date { int y, m, d; }; which serves their purpose well enough. For the vast majority however, serial representation is good enough. After caching as you know, performance nears ymd in these scenarios too.
I can see use of all constructors except the day_of_year constructor. Why? This is an alternative representation. Since we are making it a p. gregorian calendar, such interface shouldn't be made public. It should rather be used in dayofyear_claendar where concept of months doesn't exist, shouldn't it? I disagree here. 125th day of 2013 is a valid date.
I agree and don't deny that such notation doesn't have use in the world. But, I am proposing that the ordinal date should be used in its own calendar. If it makes our programming easy, it should become a private constructor instead. Haven't we designed our date class to belong to a specific calendar only? I am not favouring this constructor only and only by design.
I genuinely am curious why adding that fourth parameter might be a bad design. Well, this is a taste question. I will comeback to your functional format later.
I myself have dropped this constructor. After all, it is not difficult for users to check documentation for the order of the date parameters. It just eats up the performance anyway, even if it is just a switch statement.
I was talking about stream Input/output
I didn't know much about localization before today. I get the basic understanding of basics of locale and facets now. I have to study more about them.
What I mean is that a date can not inherit from a days point, this is horrible.
In the new representation, I have tried less to keep synergies between
current chrono library and date class. I have provided explicit functions
instead to convert to/from time_point
Le 01/05/13 22:33, Anurag Kalia a écrit :
My latest proposal with changes is attached. Anurag_Kalia_chrono_date_Project_Proposal.docx http://boost.2283326.n4.nabble.com/file/n4646428/Anurag_Kalia_chrono_date_Pr...
I have tried to address all these concerns and more.
Please, apply to GSoC as soon as possible if you want to have a change to be selected. If you want to attach your doc proposal please do it in pdf format. Best, Vicente
Vicente Botet wrote
Please, apply to GSoC as soon as possible if you want to have a change to be selected. If you want to attach your doc proposal please do it in pdf format.
Done! And a big thanks for your effort. :) Here is the link to proposal anyway: http://tinyurl.com/ceedjj4 - Anurag. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
On May 1, 2013, at 4:33 PM, Anurag Kalia
Vicente Botet wrote
Anurag Kalia wrote
As I have already mentioned, an application requiring absolute performance from date class would use its operations. And nothing can beat a serial representation at comparisons, assignments and day/week arithmetic.
ymd represntations, OTOH, optimize I/O. And an application requiring such optimization can simply employ a representation like: struct date { int y, m, d; };
which serves their purpose well enough.
Optimizing I/O, which is a relatively slow process anyway, is possibly misguided. The YMD format could be extracted from the serial format to reuse, but I imagine a given object being formatted for I/O in a single function call, à la strftime(), rather than in a series of formatting calls. Still, even the latter is possible with a formatter object which extracts the YMD values once and applies various formatting operations, as desired.
For the vast majority however, serial representation is good enough. After caching as you know, performance nears ymd in these scenarios too.
Caching suggests larger objects and, possibly lower locality of reference.
I genuinely am curious why adding that fourth parameter might be a bad design. Well, this is a taste question. I will comeback to your functional format later.
I myself have dropped this constructor. After all, it is not difficult for users to check documentation for the order of the date parameters. It just eats up the performance anyway, even if it is just a switch statement.
I'm not sure of the context here, but relying on documentation to ensure correct argument order will only frustrate users, because it is error prone. The Named Constructor Idiom is beneficial in such cases. ___ Rob (Sent from my portable computation engine)
Rob Stewart-6 wrote
On May 1, 2013, at 4:33 PM, Anurag Kalia <
anurag.kalia@
> wrote:
Vicente Botet wrote
Anurag Kalia wrote
As I have already mentioned, an application requiring absolute performance from date class would use its operations. And nothing can beat a serial representation at comparisons, assignments and day/week arithmetic.
ymd represntations, OTOH, optimize I/O. And an application requiring such optimization can simply employ a representation like: struct date { int y, m, d; };
which serves their purpose well enough.
Optimizing I/O, which is a relatively slow process anyway, is possibly misguided. The YMD format could be extracted from the serial format to reuse, but I imagine a given object being formatted for I/O in a single function call, à la strftime(), rather than in a series of formatting calls. Still, even the latter is possible with a formatter object which extracts the YMD values once and applies various formatting operations, as desired.
So, you are trying to say that optimizing I/O is not really rewarding since I/O by itself is already orders of magnitude slower? It makes sense to me. But might there not be cases where we have to store the output not to some output device, but to a string in memory only?
For the vast majority however, serial representation is good enough. After caching as you know, performance nears ymd in these scenarios too.
Caching suggests larger objects and, possibly lower locality of reference.
By caching, I meant to say retrieve y,m,d values explicitly as well as some other properties of the date.
I genuinely am curious why adding that fourth parameter might be a bad design. Well, this is a taste question. I will comeback to your functional format later.
I myself have dropped this constructor. After all, it is not difficult for users to check documentation for the order of the date parameters. It just eats up the performance anyway, even if it is just a switch statement.
I'm not sure of the context here, but relying on documentation to ensure correct argument order will only frustrate users, because it is error prone. The Named Constructor Idiom is beneficial in such cases.
To recap, the constructor under discussion looks like: date(int a, int b, int c, date_format fmt); where date_format is an enum type: enum date_format { ymd, dmy, mdy }; so that following construction is possible: date dt = date(2013, 6, 5, ymd); All information required is directly in front of the eyes. But thinking more about it, y-m-d should be the only case possible in the constructor. It is because date object is streamable. Thus, such construction is possible: std::cout << date(4, 5, 2013, dmy); where output comes out to be "2013-05-04". This seems a little clumsy to me. Having never seen a precedent like this, my mind is against it a little. That is the only reason I am antagonist to otherwise completely natural construction of: date natural = day(7) / month(12) / year(2013); Now, back to your named constructor idiom. Thanks! How true that you learn something new everyday! I liked it. It actually seems very suitable to my date class since we are constructing dates also in ordinal date notation (year, day_of_year) as well as week date notation (year, week_no, weekday). Again, I have doubts still. First that I have never encountered it in standard library till now - the library that I use the most. If it exists somewhere in it or Boost, that would hearten me. Moreover, named constructor idiom allows construction of objects by the objects themselves. It has possibility to look ugly. Though, as I write, I think one could always make them first-class functions which are friends of date class. Is the latter ok approach? My concerns also extend to performance of resulting code. But people on internet say that the call is almost always optimized away. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
Rob Stewart-6 wrote
On May 1, 2013, at 4:33 PM, Anurag Kalia < anurag.kalia@ > wrote:
Vicente Botet wrote
Anurag Kalia wrote
As I have already mentioned, an application requiring absolute performance from date class would use its operations. And nothing can beat a serial representation at comparisons, assignments and day/week arithmetic.
ymd represntations, OTOH, optimize I/O. And an application requiring such optimization can simply employ a representation like: struct date { int y, m, d; };
which serves their purpose well enough. Optimizing I/O, which is a relatively slow process anyway, is possibly misguided. The YMD format could be extracted from the serial format to reuse, but I imagine a given object being formatted for I/O in a single function call, à la strftime(), rather than in a series of formatting calls. Still, even the latter is possible with a formatter object which extracts the YMD values once and applies various formatting operations, as desired. So, you are trying to say that optimizing I/O is not really rewarding since I/O by itself is already orders of magnitude slower? It makes sense to me. But might there not be cases where we have to store the output not to some output device, but to a string in memory only? I don't see how the format at construction could improve the
Le 02/05/13 23:35, Anurag Kalia a écrit : performances of I/O? COul dyou elaborate?
For the vast majority however, serial representation is good enough. After caching as you know, performance nears ymd in these scenarios too. Caching suggests larger objects and, possibly lower locality of reference. By caching, I meant to say retrieve y,m,d values explicitly as well as some other properties of the date.
Please, could you elaborate on the cache mechanism you have in mind? Could you tell us from where would you retrieve these informations? Would you use memoization? Or is the cache static and build at startup? Would the user be able to configure how many mappings could be stored? What would be the keys of the catch?
I genuinely am curious why adding that fourth parameter might be a bad design. Well, this is a taste question. I will comeback to your functional format later. I myself have dropped this constructor. After all, it is not difficult for users to check documentation for the order of the date parameters. It just eats up the performance anyway, even if it is just a switch statement. I'm not sure of the context here, but relying on documentation to ensure correct argument order will only frustrate users, because it is error prone. The Named Constructor Idiom is beneficial in such cases. To recap, the constructor under discussion looks like:
date(int a, int b, int c, date_format fmt);
where date_format is an enum type:
enum date_format { ymd, dmy, mdy };
so that following construction is possible:
date dt = date(2013, 6, 5, ymd);
All information required is directly in front of the eyes. But thinking more about it, y-m-d should be the only case possible in the constructor. It is because date object is streamable. Thus, such construction is possible:
std::cout << date(4, 5, 2013, dmy);
where output comes out to be "2013-05-04". This seems a little clumsy to me. Having never seen a precedent like this, my mind is against it a little.
As I have said several times already the date shouldn't include any I/O formatting information.
That is the only reason I am antagonist to otherwise completely natural construction of:
date natural = day(7) / month(12) / year(2013);
Now, back to your named constructor idiom. Thanks! How true that you learn something new everyday! I liked it. It actually seems very suitable to my date class since we are constructing dates also in ordinal date notation (year, day_of_year) as well as week date notation (year, week_no, weekday). What do you see wrong with
date d(day(7),month(12),year(2013)); respect to date d = day(7) / month(12) / year(2013); Take in account that this is not just about naming parameters but about defining the design space. Note that day(32) will fail and throw an exception. If you don't want the check to be done you have the possibility to use a no_check tag day(7, no_check) In the same way the library provided some constant object for month we could add constant objects days for days, e.g. d_07 which is correct by definition. So the preceding examples could become date d(d_7,dec,year(2013,no_check)); date d = d_7 / december / year(2013); If no check is desired the following could construct a date efficiently. date d(d_7,dec,2013,no_check); Note that the operator/() factory don't allows you to avoid the validity check.
Again, I have doubts still. First that I have never encountered it in standard library till now - the library that I use the most. If it exists somewhere in it or Boost, that would hearten me. Moreover, named constructor idiom allows construction of objects by the objects themselves. It has possibility to look ugly. Though, as I write, I think one could always make them first-class functions which are friends of date class.
Is the latter ok approach? My concerns also extend to performance of resulting code. But people on internet say that the call is almost always optimized away.
The best you can do is to test it, and share your experience on internet ;-) Best, Vicente
Vicente Botet wrote
Rob Stewart-6 wrote
On May 1, 2013, at 4:33 PM, Anurag Kalia < anurag.kalia@ > wrote:
Vicente Botet wrote
Anurag Kalia wrote
As I have already mentioned, an application requiring absolute performance from date class would use its operations. And nothing can beat a serial representation at comparisons, assignments and day/week arithmetic.
ymd represntations, OTOH, optimize I/O. And an application requiring such optimization can simply employ a representation like: struct date { int y, m, d; };
which serves their purpose well enough. Optimizing I/O, which is a relatively slow process anyway, is possibly misguided. The YMD format could be extracted from the serial format to reuse, but I imagine a given object being formatted for I/O in a single function call, à la strftime(), rather than in a series of formatting calls. Still, even the latter is possible with a formatter object which extracts the YMD values once and applies various formatting operations, as desired. So, you are trying to say that optimizing I/O is not really rewarding since I/O by itself is already orders of magnitude slower? It makes sense to me. But might there not be cases where we have to store the output not to some output device, but to a string in memory only? I don't see how the format at construction could improve the
Le 02/05/13 23:35, Anurag Kalia a écrit : performances of I/O? COul dyou elaborate?
I am not at all suggesting that there would be any improvement in performance in I/O; it would be chugging at its own pace. What I meant to say was that a suboptimal output function is not actually going to affect I/O in significant measures. Since our output function, even after taking two more CPU cycles, would be significantly fast than I/O which would be taking hundreds of CPU cycles. But I did come to the conclusion after the fact that optimizing I/O is a distraction. We are optimizing CPU cycles and in that scenario, efficient I/O operations are significant after all. It is mostly a big, never mind.
For the vast majority however, serial representation is good enough. After caching as you know, performance nears ymd in these scenarios too. Caching suggests larger objects and, possibly lower locality of reference. By caching, I meant to say retrieve y,m,d values explicitly as well as some other properties of the date.
Please, could you elaborate on the cache mechanism you have in mind? Could you tell us from where would you retrieve these informations? Would you use memoization? Or is the cache static and build at startup? Would the user be able to configure how many mappings could be stored? What would be the keys of the catch? I commented about it under my GSoC application; but I never hit upon some of the questions that you have asked here so here I go. My lazy, default choice is to have some fixed number of dates in the cache (they are not user-configurable). The algorithm by which they would be kept and the hashing function have not been decided. But it wouldn't give me much. My attempt is at memoization. I want to infer data from other, already-accessed dates. For example, there can be at most 14 types of year-calendars. There are a number of properties due to recurring nature of date arithmetic. Thus, I want to, say, store 1st days of every year in the table and infer y, m, d, weekday, iso-year etc of another date in the same year from the first day of year. It would be done lazily. And the key used would be the srial itself because they are the only property we store in our date objects as well as they uniquely identify any date. But I said before in those comments, I am little successful upril now. Dates already repeat their properties every 400 years. Let us see how much we can cut that period short.
I genuinely am curious why adding that fourth parameter might be a bad design. Well, this is a taste question. I will comeback to your functional format later. I myself have dropped this constructor. After all, it is not difficult for users to check documentation for the order of the date parameters. It just eats up the performance anyway, even if it is just a switch statement. I'm not sure of the context here, but relying on documentation to ensure correct argument order will only frustrate users, because it is error prone. The Named Constructor Idiom is beneficial in such cases. To recap, the constructor under discussion looks like:
date(int a, int b, int c, date_format fmt);
where date_format is an enum type:
enum date_format { ymd, dmy, mdy };
so that following construction is possible:
date dt = date(2013, 6, 5, ymd);
All information required is directly in front of the eyes. But thinking more about it, y-m-d should be the only case possible in the constructor. It is because date object is streamable. Thus, such construction is possible:
std::cout << date(4, 5, 2013, dmy);
where output comes out to be "2013-05-04". This seems a little clumsy to me. Having never seen a precedent like this, my mind is against it a little. As I have said several times already the date shouldn't include any I/O formatting information. That is the only reason I am antagonist to otherwise completely natural construction of:
date natural = day(7) / month(12) / year(2013);
Now, back to your named constructor idiom. Thanks! How true that you learn something new everyday! I liked it. It actually seems very suitable to my date class since we are constructing dates also in ordinal date notation (year, day_of_year) as well as week date notation (year, week_no, weekday). What do you see wrong with
date d(day(7),month(12),year(2013));
respect to
date d = day(7) / month(12) / year(2013);
I think the first version is wordy. Some may think the second version is "too cute" (someone in the Kona-date-lib thought so), I think it is perfectly normal to try to push forward the syntax. The "<<" operator was overloaded too, and how! Dates are already used with separators. So, it is not like this library is trying to set a trend; it is merely following the age-old conventions.
Take in account that this is not just about naming parameters but about defining the design space. Note that
day(32)
will fail and throw an exception. If you don't want the check to be done you have the possibility to use a no_check tag
day(7, no_check)
In the same way the library provided some constant object for month we could add constant objects days for days, e.g. d_07 which is correct by definition. So the preceding examples could become
date d(d_7,dec,year(2013,no_check)); date d = d_7 / december / year(2013);
If no check is desired the following could construct a date efficiently.
date d(d_7,dec,2013,no_check);
Note that the operator/() factory don't allows you to avoid the validity check.
I realize it. But there is no need for bounds to be so intricately defined. A person either wants to define a date, or not. So, I suggest to leave out the checks from day, month and year and let them be checked only when constructing the actual date. We are not expecting, after all, that these day or month classes would be used independently after all. In fact, one of the strengths of functional approach [ day(2013, 05, 03) ] is that it lets us drop the redundant usage of day, month and year in the syntax.
Again, I have doubts still. First that I have never encountered it in standard library till now - the library that I use the most. If it exists somewhere in it or Boost, that would hearten me. Moreover, named constructor idiom allows construction of objects by the objects themselves. It has possibility to look ugly. Though, as I write, I think one could always make them first-class functions which are friends of date class.
Is the latter ok approach? My concerns also extend to performance of resulting code. But people on internet say that the call is almost always optimized away.
The best you can do is to test it, and share your experience on internet ;-)
And I sure will! Regards, Anurag. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
Le 03/05/13 04:19, Anurag Kalia a écrit :
Rob Stewart-6 wrote
On May 1, 2013, at 4:33 PM, Anurag Kalia < anurag.kalia@ > wrote:
Vicente Botet wrote
Anurag Kalia wrote
As I have already mentioned, an application requiring absolute performance from date class would use its operations. And nothing can beat a serial representation at comparisons, assignments and day/week arithmetic.
ymd represntations, OTOH, optimize I/O. And an application requiring such optimization can simply employ a representation like: struct date { int y, m, d; };
which serves their purpose well enough. Optimizing I/O, which is a relatively slow process anyway, is possibly misguided. The YMD format could be extracted from the serial format to reuse, but I imagine a given object being formatted for I/O in a single function call, à la strftime(), rather than in a series of formatting calls. Still, even the latter is possible with a formatter object which extracts the YMD values once and applies various formatting operations, as desired. So, you are trying to say that optimizing I/O is not really rewarding since I/O by itself is already orders of magnitude slower? It makes sense to me. But might there not be cases where we have to store the output not to some output device, but to a string in memory only? I don't see how the format at construction could improve the
Le 02/05/13 23:35, Anurag Kalia a écrit : performances of I/O? COul dyou elaborate? I am not at all suggesting that there would be any improvement in
Vicente Botet wrote performance in I/O; it would be chugging at its own pace. What I meant to say was that a suboptimal output function is not actually going to affect I/O in significant measures. Since our output function, even after taking two more CPU cycles, would be significantly fast than I/O which would be taking hundreds of CPU cycles.
But I did come to the conclusion after the fact that optimizing I/O is a distraction. We are optimizing CPU cycles and in that scenario, efficient I/O operations are significant after all. It is mostly a big, never mind.
For the vast majority however, serial representation is good enough. After caching as you know, performance nears ymd in these scenarios too. Caching suggests larger objects and, possibly lower locality of reference. By caching, I meant to say retrieve y,m,d values explicitly as well as some other properties of the date. Please, could you elaborate on the cache mechanism you have in mind? Could you tell us from where would you retrieve these informations? Would you use memoization? Or is the cache static and build at startup? Would the user be able to configure how many mappings could be stored? What would be the keys of the catch?
I commented about it under my GSoC application; but I never hit upon some of the questions that you have asked here so here I go.
My lazy, default choice is to have some fixed number of dates in the cache (they are not user-configurable). The algorithm by which they would be kept and the hashing function have not been decided.
But it wouldn't give me much. My attempt is at memoization. I want to infer data from other, already-accessed dates. For example, there can be at most 14 types of year-calendars. There are a number of properties due to recurring nature of date arithmetic. Thus, I want to, say, store 1st days of every year in the table and infer y, m, d, weekday, iso-year etc of another date in the same year from the first day of year. It would be done lazily. And the key used would be the srial itself because they are the only property we store in our date objects as well as they uniquely identify any date. You would need at least the ymd key and the days key. The first one to build your days date class and the other to get the user land year/month/day attributes.
But I said before in those comments, I am little successful upril now. Dates already repeat their properties every 400 years. Let us see how much we can cut that period short.
What do you see wrong with
date d(day(7),month(12),year(2013));
respect to
date d = day(7) / month(12) / year(2013); I think the first version is wordy. There is just one additional character ;-) Some may think the second version is "too cute" (someone in the Kona-date-lib thought so), I think it is perfectly normal to try to push forward the syntax. The "<<" operator was overloaded too, and how! Dates are already used with separators. So, it is not like this library is trying to set a trend; it is merely following the age-old conventions. I think that for the standard this syntax must be proposed separately,
Have you explored the cache mechanism the Bloomberg implemented. It would be great if you can request them if you can have their implementation and the performances test they do for their paper " Towarda Standard C++’Date’Class" http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3344.pdf Memoization would be a bottleneck on multi-threaded systems. that is it is a SHOULD feature. For Boost I think it is a MUST so that we can experiment with it.
Take in account that this is not just about naming parameters but about defining the design space. Note that
day(32)
will fail and throw an exception. If you don't want the check to be done you have the possibility to use a no_check tag
day(7, no_check)
In the same way the library provided some constant object for month we could add constant objects days for days, e.g. d_07 which is correct by definition. So the preceding examples could become
date d(d_7,dec,year(2013,no_check)); date d = d_7 / december / year(2013);
If no check is desired the following could construct a date efficiently.
date d(d_7,dec,2013,no_check);
Note that the operator/() factory don't allows you to avoid the validity check. I realize it. But there is no need for bounds to be so intricately defined. A person either wants to define a date, or not. So, I suggest to leave out the checks from day, month and year and let them be checked only when constructing the actual date.
In my chrono::date library I check for the bounds on all the date related types allowing to don't check them with the specific no_check_t parameter.
We are not expecting, after all, that these day or month classes would be used independently after all. In fact, one of the strengths of functional approach [ day(2013, 05, 03) ] is that it lets us drop the redundant usage of day, month and year in the syntax.
The problem is the ordering of the parameters and performances. That is how you make the interface not ambiguous without decreasing the performances. Best, Vicente
My lazy, default choice is to have some fixed number of dates in the cache (they are not user-configurable). The algorithm by which they would be kept and the hashing function have not been decided.
But it wouldn't give me much. My attempt is at memoization. I want to infer data from other, already-accessed dates. For example, there can be at most 14 types of year-calendars. There are a number of properties due to recurring nature of date arithmetic. Thus, I want to, say, store 1st days of every year in the table and infer y, m, d, weekday, iso-year etc of another date in the same year from the first day of year. It would be done lazily. And the key used would be the srial itself because they are the only property we store in our date objects as well as they uniquely identify any date. You would need at least the ymd key and the days key. The first one to build your days date class and the other to get the user land year/month/day attributes.
I was talking about keys to the cache. I hope we are on the same page; ymd as key just doesn't seem right in this sense.
Have you explored the cache mechanism the Bloomberg implemented. It would be great if you can request them if you can have their implementation and the performances test they do for their paper " Towarda Standard C++’Date’Class" http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3344.pdf
We can do that? Though who would miss a chance to see an efficient implementation in action? I am quite interested in using the same performance test though. It would let me compare my class directly with other implementations in their paper. Is it okay to do so?
Memoization would be a bottleneck on multi-threaded systems.
Thanks! I didn't know about it.
What do you see wrong with
date d(day(7),month(12),year(2013));
respect to
date d = day(7) / month(12) / year(2013); I think the first version is wordy. There is just one additional character ;-)
Haha! true.
Some may think the second version is "too cute" (someone in the Kona-date-lib thought so), I think it is perfectly normal to try to push forward the syntax. The "<<" operator was overloaded too, and how! Dates are already used with separators. So, it is not like this library is trying to set a trend; it is merely following the age-old conventions. I think that for the standard this syntax must be proposed separately, that is it is a SHOULD feature. For Boost I think it is a MUST so that we can experiment with it.
Point. Thanks for everything! Anurag. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
Le 04/05/13 01:13, Anurag Kalia a écrit :
My lazy, default choice is to have some fixed number of dates in the cache (they are not user-configurable). The algorithm by which they would be kept and the hashing function have not been decided.
But it wouldn't give me much. My attempt is at memoization. I want to infer data from other, already-accessed dates. For example, there can be at most 14 types of year-calendars. There are a number of properties due to recurring nature of date arithmetic. Thus, I want to, say, store 1st days of every year in the table and infer y, m, d, weekday, iso-year etc of another date in the same year from the first day of year. It would be done lazily. And the key used would be the srial itself because they are the only property we store in our date objects as well as they uniquely identify any date. You would need at least the ymd key and the days key. The first one to build your days date class and the other to get the user land year/month/day attributes. I was talking about keys to the cache. I hope we are on the same page; ymd as key just doesn't seem right in this sense. Why? You need the cache to access the ymd from days and another to validate a date when construction a days date from ymd. Couldn't you have a cache indexed by year/month that gives you the number of days since an epoch (for some years of course)? Have you explored the cache mechanism the Bloomberg implemented. It would be great if you can request them if you can have their implementation and the performances test they do for their paper " Towarda Standard C++’Date’Class" http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3344.pdf We can do that? Though who would miss a chance to see an efficient implementation in action? I am quite interested in using the same performance test though. It would let me compare my class directly with other implementations in their paper.
Is it okay to do so? The paper states clearly that interested people could request them. I would expect there is no problem for GSoC.
Best, Vicente
Why? You need the cache to access the ymd from days and another to validate a date when construction a days date from ymd. Couldn't you have a cache indexed by year/month that gives you the number of days since an epoch (for some years of course)?
That means that I need to still calculate year and month. I want to cut this step too. I want to directly access y,m,d from the serial field. That's why. It doesn't mean that the array that you are suggesting wouldn't be present; both have different uses.
Is it okay to do so? The paper states clearly that interested people could request them. I would expect there is no problem for GSoC.
And now I see it too. Thanks, I would surely contact them. More the input, better the output! - Anurag. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
Le 04/05/13 19:14, Anurag Kalia a écrit :
Why? You need the cache to access the ymd from days and another to validate a date when construction a days date from ymd. Couldn't you have a cache indexed by year/month that gives you the number of days since an epoch (for some years of course)? That means that I need to still calculate year and month. I want to cut this step too. I want to directly access y,m,d from the serial field. That's why. It doesn't mean that the array that you are suggesting wouldn't be present; both have different uses. How would you calculate the days from year/month/day?
Best, Vicente
On May 2, 2013, at 10:19 PM, Anurag Kalia
Vicente Botet wrote
Take in account that this is not just about naming parameters but about defining the design space. Note that
day(32)
will fail and throw an exception. If you don't want the check to be done you have the possibility to use a no_check tag
day(7, no_check)
I don't see the value of this. Yes, you can ensure the value is < 31, but you can't ensure that it doesn't exceed the range for a given month in the day type. Therefore, the validation should be done by the date constructor. IOW, I see two classes: year and day that are trivial value types holding an unsigned type with no validation. They exist solely to apply type information.
In the same way the library provided some constant object for month we could add constant objects days for days, e.g. d_07 which is correct by definition. So the preceding examples could become
date d(d_7,dec,year(2013,no_check)); date d = d_7 / december / year(2013);
At the very least, the names would need to be dayN or day_N. "d" seems insufficient. At that point, "day()" is only one or two characters longer. I don't like the day-of-month constants.
If no check is desired the following could construct a date efficiently.
date d(d_7,dec,2013,no_check);
Note that the operator/() factory don't allows you to avoid the validity check.
We're agreed here, aside from the spelling. That is, I get the same from this: date(day(7), dec, 2013, no_check);
I realize it. But there is no need for bounds to be so intricately defined. A person either wants to define a date, or not. So, I suggest to leave out the checks from day, month and year and let them be checked only when constructing the actual date.
Ah, right. You said the same thing as I.
We are not expecting, after all, that these day or month classes would be used independently after all. In fact, one of the strengths of functional approach [ day(2013, 05, 03) ] is that it lets us drop the redundant usage of day, month and year in the syntax.
That form isn't clear. ___ Rob (Sent from my portable computation engine)
Le 03/05/13 13:39, Rob Stewart a écrit :
On May 2, 2013, at 10:19 PM, Anurag Kalia
wrote: Vicente Botet wrote
Take in account that this is not just about naming parameters but about defining the design space. Note that
day(32)
will fail and throw an exception. If you don't want the check to be done you have the possibility to use a no_check tag
day(7, no_check) I don't see the value of this. Yes, you can ensure the value is < 31, but you can't ensure that it doesn't exceed the range for a given month in the day type. Therefore, the validation should be done by the date constructor.
IOW, I see two classes: year and day that are trivial value types holding an unsigned type with no validation. They exist solely to apply type information. I could admit that the independent validation for the day could be redundant, but the year can be validated independently, so providing validated year don't need any further validation.
In the same way the library provided some constant object for month we could add constant objects days for days, e.g. d_07 which is correct by definition. So the preceding examples could become
date d(d_7,dec,year(2013,no_check)); date d = d_7 / december / year(2013); At the very least, the names would need to be dayN or day_N. "d" seems insufficient. At that point, "day()" is only one or two characters longer.
I don't like the day-of-month constants. I have posted another thread for date suffixes.
Best, Vicente
On May 2, 2013, at 5:35 PM, Anurag Kalia
Rob Stewart-6 wrote
On May 1, 2013, at 4:33 PM, Anurag Kalia wrote:
Optimizing I/O, which is a relatively slow process anyway, is possibly misguided. The YMD format could be extracted from the serial format to reuse, but I imagine a given object being formatted for I/O in a single function call, à la strftime(), rather than in a series of formatting calls. Still, even the latter is possible with a formatter object which extracts the YMD values once and applies various formatting operations, as desired.
So, you are trying to say that optimizing I/O is not really rewarding since I/O by itself is already orders of magnitude slower? It makes sense to me.
Yes
But might there not be cases where we have to store the output not to some output device, but to a string in memory only?
I don't imagine there would be much call for doing formatted output of many strings to a buffer. Even then, each date of interest must be cast into the I/O friendly form, so there's no room for optimizing that case, is there?
For the vast majority however, serial representation is good enough. After caching as you know, performance nears ymd in these scenarios too.
Caching suggests larger objects and, possibly lower locality of reference.
By caching, I meant to say retrieve y,m,d values explicitly as well as some other properties of the date.
That sounds like conversion to another date type.
I myself have dropped this constructor. After all, it is not difficult for users to check documentation for the order of the date parameters. It just eats up the performance anyway, even if it is just a switch statement.
I'm not sure of the context here, but relying on documentation to ensure correct argument order will only frustrate users, because it is error prone. The Named Constructor Idiom is beneficial in such cases.
To recap, the constructor under discussion looks like:
date(int a, int b, int c, date_format fmt);
where date_format is an enum type:
enum date_format { ymd, dmy, mdy };
so that following construction is possible:
date dt = date(2013, 6, 5, ymd);
All information required is directly in front of the eyes. But thinking more about it, y-m-d should be the only case possible in the constructor.
I comment on this further down, but that does address the documentation dependency problem.
It is because date object is streamable. Thus, such construction is possible:
std::cout << date(4, 5, 2013, dmy);
where output comes out to be "2013-05-04". This seems a little clumsy to me.
If there is I/O support, I'd hope for other formats. Regardless, those in different regions will want the arguments ordered in their locale's order. You could require them in magnitude order -- YMD -- but that's less friendly and isn't checked by the compiler.
That is the only reason I am antagonist to otherwise completely natural construction of:
date natural = day(7) / month(12) / year(2013);
That will be harder to optimize. I'm not saying that it wouldn't be optimized, just harder, so less likely. It's also unlike anything in the standard. I don't think that's any clearer than this: date d(7, dec, year(2013)) (The months can be from an enumerated type.) One can specify a day or year object and avoid ambiguity: date(unsigned, month, year); date(day, month, unsigned); You can even handle other orders that way: date(month, unsigned, year); date(month, day, unsigned); date(unsigned, day, month); date(year, unsigned, month); One can also be explicit for both day and year: date(year, month, day); date(month, day, year); date(day, month, year); date(day, year, month); (That last seems particularly odd, but could be supported.) I'm assuming explicit constructors for day and year, of course.
Now, back to your named constructor idiom. Thanks! How true that you learn something new everyday! I liked it. It actually seems very suitable to my date class since we are constructing dates also in ordinal date notation (year, day_of_year) as well as week date notation (year, week_no, weekday).
Again, I have doubts still. First that I have never encountered it in standard library till now - the library that I use the most. If it exists somewhere in it or Boost, that would hearten me.
make_pair, make_shared are something like it. However, the reason you've not seen it used is that no standardized type has needed it.
Moreover, named constructor idiom allows construction of objects by the objects themselves. It has possibility to look ugly. Though, as I write, I think one could always make them first-class functions which are friends of date class.
date::ymd(2013, 5, 3) make_ymd(2013, 5, 3) I don't think this is right, though: ymd(2013, 5, 3)
Is the latter ok approach? My concerns also extend to performance of resulting code. But people on internet say that the call is almost always optimized away.
RVO should take care of things. ___ Rob (Sent from my portable computation engine)
Le 03/05/13 05:46, Rob Stewart a écrit :
On May 2, 2013, at 5:35 PM, Anurag Kalia
wrote: Rob Stewart-6 wrote That is the only reason I am antagonist to otherwise completely natural construction of:
date natural = day(7) / month(12) / year(2013); That will be harder to optimize. I'm not saying that it wouldn't be optimized, just harder, so less likely. It's also unlike anything in the standard. You are right. This expression should be included a s SHOULD feature,not a MUST feature. The constructor of a date must provide almost the same functionality and even more. E.g. my days_date has the following constructors
days_date(); days_date(year, month, day); days_date(year::rep, month::rep, day::rep, no_check_t); days_date(year, month_day); days_date(year::rep, month_day, no_check_t); days_date(year, day_of_year); days_date(year::rep, day_of_year::rep, no_check_t); explicit days_date(days); days_date(days, no_check_t); days_date(days::rep, no_check_t); days_date(year::rep, month::rep, day::rep, bool, no_check_t); And all my date classes have the same constructors.
I don't think that's any clearer than this:
date d(7, dec, year(2013))
(The months can be from an enumerated type.)
One can specify a day or year object and avoid ambiguity:
date(unsigned, month, year); date(day, month, unsigned);
I would add the following constructors days_date(year, month, day::rep); days_date(year, month::rep, day); days_date(year::rep, month, day); days_date(year::rep, month_day); days_date(year, day_of_year); days_date(year::rep, day_of_year); days_date(year, day_of_year::rep);
You can even handle other orders that way:
date(month, unsigned, year); date(month, day, unsigned); date(unsigned, day, month); date(year, unsigned, month);
One can also be explicit for both day and year:
date(year, month, day); date(month, day, year); date(day, month, year); date(day, year, month);
I don't think that the constructor should support different orderings.
(That last seems particularly odd, but could be supported.)
I'm assuming explicit constructors for day and year, of course. Of course. And implicit conversion to his representation, so that
date(2013, may, 3, no_check) is yet valid. We could have a factory make_date function that doesn't checks the validity of the date and avoids the no_check parameter make_ymd_date(2013, may, 3) Best, Vicente
On May 3, 2013, at 3:47 AM, "Vicente J. Botet Escriba"
You can even handle other orders that way:
date(month, unsigned, year); date(month, day, unsigned); date(unsigned, day, month); date(year, unsigned, month);
One can also be explicit for both day and year:
date(year, month, day); date(month, day, year); date(day, month, year); date(day, year, month); I don't think that the constructor should support different orderings.
It could, easily, so why not? Different locales have different preferred orderings. All can learn to use the descending magnitude order, but a little flexibility, without ambiguity, would be nice.
I'm assuming explicit constructors for day and year, of course. Of course. And implicit conversion to his representation, so that
date(2013, may, 3, no_check)
is yet valid.
That's where I disagree. If you have explicit constructors for year and day, and no constructor accepting two ::rep arguments, then your example won't compile. That's also what enables support for other argument orders.
We could have a factory make_date function that doesn't checks the validity of the date and avoids the no_check parameter
make_ymd_date(2013, may, 3)
I don't understand what no_check has to do with such a function. Add an overload that accepts no_check_t to get that. ___ Rob (Sent from my portable computation engine)
Le 03/05/13 13:52, Rob Stewart a écrit :
On May 3, 2013, at 3:47 AM, "Vicente J. Botet Escriba"
wrote: You can even handle other orders that way:
date(month, unsigned, year); date(month, day, unsigned); date(unsigned, day, month); date(year, unsigned, month);
One can also be explicit for both day and year:
date(year, month, day); date(month, day, year); date(day, month, year); date(day, year, month); I don't think that the constructor should support different orderings. It could, easily, so why not? Different locales have different preferred orderings. All can learn to use the descending magnitude order, but a little flexibility, without ambiguity, would be nice.
I'm assuming explicit constructors for day and year, of course. Of course. And implicit conversion to his representation, so that
date(2013, may, 3, no_check)
is yet valid. That's where I disagree. If you have explicit constructors for year and day, and no constructor accepting two ::rep arguments, then your example won't compile.
That's also what enables support for other argument orders. The no_check constructor allows only year,month,day order of arguments. If the user know that the date is valid it can know the order. This is a low level function.
We could have a factory make_date function that doesn't checks the validity of the date and avoids the no_check parameter
make_ymd_date(2013, may, 3) I don't understand what no_check has to do with such a function. Add an overload that accepts no_check_t to get that.
The name is not the good one. make_unchecked_date(2013, may, 3); or make_valid_date(2013, may, 3); Vicente
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
Le 03/05/13 16:58, Howard Hinnant a écrit :
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). Hi Howard,
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. Again I proposed these functions for the no_check date construction. Nothing to be with the checked one. Anyway I don't like them more that
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. I would expect that month and year could be check its validity so no check is needed when using constant objects. BTW, Howard, could you comment on my post about date literals for day and year?
-----------------
Two date types:
1. Serial:
typedef duration
>> days; typedef time_point 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. This is a reasonable argument. I have taken the option to provide on all
my guess with no_check was to make it more difficult to build an unvalidated date than a validated one. Wouldn't an beginner user start using the unchecked constructors before the factory functions? the no_check constructor. I like the factories for the check interface. the date classes the same interface. Maybe both can be conciliated. * First level: provides only the operations that are efficient with the used representation. The user must do the needed conversions to obtain whatever is needed. * Second level: build an adaptor that models a Date concept independently of the underlying type. The adaptor need to make internally the conversions to provide the Date functions. * If a Date concept is proposed, we (the user/boost/or the standard) can provide other alternative dates that could be more efficient than the preceding adpator.
-------------------------
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!
So the cached serial functionality would be hidden on the field-serial conversions. As the and so on the Field date-type, isn't it? ------------------------- BTW, what about the relative date functionalities? Where should them be implemented? On the field based date? Or on a specific relative_date class that is convertible to ymd_date? I 'm all for a relative_date class, so that the additional functionality doesn't incur on performances penalties of the ymd date. Thanks Howard for your comments, Vicente
Le 03/05/13 18:06, Vicente J. Botet Escriba a écrit :
Le 03/05/13 16:58, Howard Hinnant a écrit :
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). Hi Howard,
my guess with no_check was to make it more difficult to build an unvalidated date than a validated one. Wouldn't an beginner user start using the unchecked constructors before the factory functions?
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. Again I proposed these functions for the no_check date construction. Nothing to be with the checked one. Anyway I don't like them more that the no_check constructor. I like the factories for the check interface. 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. I would expect that month and year could be check its validity so no check is needed when using constant objects. BTW, Howard, could you comment on my post about date literals for day and year?
Howard, I believe I start to understand why you don't want day/month/year to validate its input. I suspect that the rationale is quite simple, you are relaying on undefined behavior when the value of these parameters is out of range, isn't it? Best, Vicente
On May 3, 2013, at 1:46 PM, "Vicente J. Botet Escriba"
Howard, I believe I start to understand why you don't want day/month/year to validate its input. I suspect that the rationale is quite simple, you are relaying on undefined behavior when the value of these parameters is out of range, isn't it?
class day { int d_; public: constexpr explicit day(int d) noexcept : d_(d) {} constexpr operator int() const noexcept {return d_;} }; day d(623); // no undefined behavior The rationale for not checking if day is within a valid range (like I did for http://home.roadrunner.com/~hinnant/bloomington/date.html), is simply performance. One can not completely validate a day without the knowledge of the year and month. There are two design spaces to explore: 1. Validate as much as you can as early as you can. This was the approach taken by my 2011 paper and which was criticized for low performance. 2. Validate only once when you have the complete information. 1. didn't work. So now I'm proposing 2. I'm flexible. :-) There is no undefined behavior involved. Howard
Le 03/05/13 19:53, Howard Hinnant a écrit :
On May 3, 2013, at 1:46 PM, "Vicente J. Botet Escriba"
wrote: Howard, I believe I start to understand why you don't want day/month/year to validate its input. I suspect that the rationale is quite simple, you are relaying on undefined behavior when the value of these parameters is out of range, isn't it? class day { int d_; public: constexpr explicit day(int d) noexcept : d_(d) {}
constexpr operator int() const noexcept {return d_;} };
day d(623); // no undefined behavior
The UB is when you need to build a date if the day is not on the range. E.g. the implementation could use some arrays indexed with the day and access memory that has no sense. If we are sure that a day is in the range 1..31, this kind of assumptions can be done on the implementation. The implementation could not do it if the day value has not been validated previously or behavior would be undefined.
The rationale for not checking if day is within a valid range (like I did for http://home.roadrunner.com/~hinnant/bloomington/date.html), is simply performance.
One can not completely validate a day without the knowledge of the year and month. There are two design spaces to explore:
1. Validate as much as you can as early as you can. This was the approach taken by my 2011 paper and which was criticized for low performance.
2. Validate only once when you have the complete information.
1. didn't work. So now I'm proposing 2. I'm flexible. :-) There is no undefined behavior involved.
Are you saying that you would check the day is in range 1..31 before accessing such an array when using the value to build a valid date? I'm also flexible, and this was the reason I moved to no_check. Maybe this is ugly, but covers the needs. * No validation if not desired so no loss of performances. * Validation is easier than no validation, so the user code is more robust. Best, Vicente
Le 04/05/13 01:05, Vicente J. Botet Escriba a écrit :
Le 03/05/13 19:53, Howard Hinnant a écrit :
On May 3, 2013, at 1:46 PM, "Vicente J. Botet Escriba"
wrote: Howard, I believe I start to understand why you don't want day/month/year to validate its input. I suspect that the rationale is quite simple, you are relaying on undefined behavior when the value of these parameters is out of range, isn't it? class day { int d_; public: constexpr explicit day(int d) noexcept : d_(d) {}
constexpr operator int() const noexcept {return d_;} };
day d(623); // no undefined behavior
The UB is when you need to build a date if the day is not on the range. E.g. the implementation could use some arrays indexed with the day and access memory that has no sense. If we are sure that a day is in the range 1..31, this kind of assumptions can be done on the implementation. The implementation could not do it if the day value has not been validated previously or behavior would be undefined.
The rationale for not checking if day is within a valid range (like I did for http://home.roadrunner.com/~hinnant/bloomington/date.html), is simply performance.
One can not completely validate a day without the knowledge of the year and month. There are two design spaces to explore:
1. Validate as much as you can as early as you can. This was the approach taken by my 2011 paper and which was criticized for low performance.
2. Validate only once when you have the complete information.
1. didn't work. So now I'm proposing 2. I'm flexible. :-) There is no undefined behavior involved.
Are you saying that you would check the day is in range 1..31 before accessing such an array when using the value to build a valid date?
I'm also flexible, and this was the reason I moved to no_check. Maybe this is ugly, but covers the needs. * No validation if not desired so no loss of performances. * Validation is easier than no validation, so the user code is more robust.
To be concrete: In my implementation I have the following tables (some of them based on your implementation Howard) day::rep days_in_month_[2][13]; day_of_year::rep days_in_year_before_month_[2][13]; month::rep day_of_year_to_month_[2][366]; day::rep day_of_year_to_day_of_month_[2][366]; day_of_year::rep month_day_to_day_of_year_[2][12][31]; Access to these tables needs that the month is in range 1..12, day(_of_month) in 1..31, day_of_year 1..366. Vicente
On May 3, 2013, at 7:19 PM, "Vicente J. Botet Escriba"
Le 04/05/13 01:05, Vicente J. Botet Escriba a écrit :
Le 03/05/13 19:53, Howard Hinnant a écrit :
On May 3, 2013, at 1:46 PM, "Vicente J. Botet Escriba"
wrote: Howard, I believe I start to understand why you don't want day/month/year to validate its input. I suspect that the rationale is quite simple, you are relaying on undefined behavior when the value of these parameters is out of range, isn't it? class day { int d_; public: constexpr explicit day(int d) noexcept : d_(d) {}
constexpr operator int() const noexcept {return d_;} };
day d(623); // no undefined behavior
The UB is when you need to build a date if the day is not on the range. E.g. the implementation could use some arrays indexed with the day and access memory that has no sense. If we are sure that a day is in the range 1..31, this kind of assumptions can be done on the implementation. The implementation could not do it if the day value has not been validated previously or behavior would be undefined.
The rationale for not checking if day is within a valid range (like I did for http://home.roadrunner.com/~hinnant/bloomington/date.html), is simply performance.
One can not completely validate a day without the knowledge of the year and month. There are two design spaces to explore:
1. Validate as much as you can as early as you can. This was the approach taken by my 2011 paper and which was criticized for low performance.
2. Validate only once when you have the complete information.
1. didn't work. So now I'm proposing 2. I'm flexible. :-) There is no undefined behavior involved.
Are you saying that you would check the day is in range 1..31 before accessing such an array when using the value to build a valid date?
I'm also flexible, and this was the reason I moved to no_check. Maybe this is ugly, but covers the needs. * No validation if not desired so no loss of performances. * Validation is easier than no validation, so the user code is more robust.
To be concrete: In my implementation I have the following tables (some of them based on your implementation Howard)
day::rep days_in_month_[2][13]; day_of_year::rep days_in_year_before_month_[2][13]; month::rep day_of_year_to_month_[2][366]; day::rep day_of_year_to_day_of_month_[2][366]; day_of_year::rep month_day_to_day_of_year_[2][12][31];
Access to these tables needs that the month is in range 1..12, day(_of_month) in 1..31, day_of_year 1..366.
In: Unchecked interface: -------------------- date(year y, month m, day d); If any of the y, m or d are out of range, it would index into those tables upon field->serial conversion (if asked for) and result in undefined behavior. In: Checked interface: ------------------ 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; There would be a single point of validation at the point where all of y, m and d are known, and there would be an error. Presumably that error would be an exception if any of the inputs are not constexpr, or a compile-time failure if all of the inputs are constexpr. Howard
Le 04/05/13 01:36, Howard Hinnant a écrit :
On May 3, 2013, at 7:19 PM, "Vicente J. Botet Escriba"
wrote: Le 04/05/13 01:05, Vicente J. Botet Escriba a écrit :
Le 03/05/13 19:53, Howard Hinnant a écrit :
On May 3, 2013, at 1:46 PM, "Vicente J. Botet Escriba"
wrote: Howard, I believe I start to understand why you don't want day/month/year to validate its input. I suspect that the rationale is quite simple, you are relaying on undefined behavior when the value of these parameters is out of range, isn't it? class day { int d_; public: constexpr explicit day(int d) noexcept : d_(d) {}
constexpr operator int() const noexcept {return d_;} };
day d(623); // no undefined behavior
The UB is when you need to build a date if the day is not on the range. E.g. the implementation could use some arrays indexed with the day and access memory that has no sense. If we are sure that a day is in the range 1..31, this kind of assumptions can be done on the implementation. The implementation could not do it if the day value has not been validated previously or behavior would be undefined.
The rationale for not checking if day is within a valid range (like I did for http://home.roadrunner.com/~hinnant/bloomington/date.html), is simply performance.
One can not completely validate a day without the knowledge of the year and month. There are two design spaces to explore:
1. Validate as much as you can as early as you can. This was the approach taken by my 2011 paper and which was criticized for low performance.
2. Validate only once when you have the complete information.
1. didn't work. So now I'm proposing 2. I'm flexible. :-) There is no undefined behavior involved.
Are you saying that you would check the day is in range 1..31 before accessing such an array when using the value to build a valid date?
I'm also flexible, and this was the reason I moved to no_check. Maybe this is ugly, but covers the needs. * No validation if not desired so no loss of performances. * Validation is easier than no validation, so the user code is more robust.
To be concrete: In my implementation I have the following tables (some of them based on your implementation Howard)
day::rep days_in_month_[2][13]; day_of_year::rep days_in_year_before_month_[2][13]; month::rep day_of_year_to_month_[2][366]; day::rep day_of_year_to_day_of_month_[2][366]; day_of_year::rep month_day_to_day_of_year_[2][12][31];
Access to these tables needs that the month is in range 1..12, day(_of_month) in 1..31, day_of_year 1..366.
In:
Unchecked interface: --------------------
date(year y, month m, day d);
If any of the y, m or d are out of range, it would index into those tables upon field->serial conversion (if asked for) and result in undefined behavior.
In:
Checked interface: ------------------
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;
There would be a single point of validation at the point where all of y, m and d are known, and there would be an error. Presumably that error would be an exception if any of the inputs are not constexpr, or a compile-time failure if all of the inputs are constexpr.
Just to be sure I'm not trying to solve an issue that doesn't needs to be solved: Given date d = day(33) / month(5) / 2013; Note that I has a typo on the day. This is equivalent on my implementation to date d(year(2013), month(5), day(33)); If I want to throw a bad date exception I would need to check that the day/month and year are in range. Next follows the code: days_date::days_date(year y, month m, day d) { if (set_if_valid_date(y, m, d)) return; throw bad_date("day " + to_string(d) + " is out of range for " + to_string(y) + '-' + to_string(m)); } bool days_date::set_if_valid_date(year y, month m, day d) noexcept { // [0] bool leap = is_leap(y.value()); // [1] const day_of_year::rep* year_data = days_in_year_before(leap); if (!(d.value() <= year_data[m.value()] - year_data[m.value()-1])) // [2] { return false; } year::rep by = y.value() + 32799; // [3] x_ = days_before_year(by) + year_data[m.value()-1] + d.value(); // [4] return true; } [1] and [3] presumes the year is in range. [2] and [4] presumes the day and month are in range. If some of 3 values are not in range the function set_if_valid_date could return true or false arbitrarily. So if the day/month and year are just names and don't ensure range I would need to start by validating their range in [0] so that if some of them is out of range the function return false; This mean that the user is not able, using the type system, to help the implementation to reduce needed check. I have no measures of the impact of these 3 range checks (6 comparisons). I just wanted to avoid them. Even if we use other kind of cache, I don't see how the cached data can be accessed without checking the range are correct. E.g. An alternative implementation using a cache for the year_month_cache bool days_date::set_if_valid_date(year y, month m, day d) noexcept { // [0] auto c = year_month_cache(y, m); // [1] if (d.value() >= c.days_in_month) return false; x_ = c.days_before + d.value(); // [2] return true; } This needs that y and m anre in range at [1] and d in in range at [2]. Howard, could you tell me if you find something wrong on my analysis? I want to be too purist? Best, Vicente
Le 04/05/13 08:56, Vicente J. Botet Escriba a écrit :
Le 04/05/13 01:36, Howard Hinnant a écrit :
On May 3, 2013, at 7:19 PM, "Vicente J. Botet Escriba"
wrote: Le 04/05/13 01:05, Vicente J. Botet Escriba a écrit :
Le 03/05/13 19:53, Howard Hinnant a écrit :
On May 3, 2013, at 1:46 PM, "Vicente J. Botet Escriba"
wrote: In:
Unchecked interface: --------------------
date(year y, month m, day d);
If any of the y, m or d are out of range, it would index into those tables upon field->serial conversion (if asked for) and result in undefined behavior.
In:
Checked interface: ------------------
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;
There would be a single point of validation at the point where all of y, m and d are known, and there would be an error. Presumably that error would be an exception if any of the inputs are not constexpr, or a compile-time failure if all of the inputs are constexpr.
Just to be sure I'm not trying to solve an issue that doesn't needs to be solved: Given
date d = day(33) / month(5) / 2013;
Note that I has a typo on the day. This is equivalent on my implementation to
date d(year(2013), month(5), day(33));
If I want to throw a bad date exception I would need to check that the day/month and year are in range. Next follows the code:
days_date::days_date(year y, month m, day d) { if (set_if_valid_date(y, m, d)) return; throw bad_date("day " + to_string(d) + " is out of range for " + to_string(y) + '-' + to_string(m)); }
bool days_date::set_if_valid_date(year y, month m, day d) noexcept { // [0] bool leap = is_leap(y.value()); // [1] const day_of_year::rep* year_data = days_in_year_before(leap);
if (!(d.value() <= year_data[m.value()] - year_data[m.value()-1])) // [2] { return false; } year::rep by = y.value() + 32799; // [3] x_ = days_before_year(by) + year_data[m.value()-1] + d.value(); // [4] return true; }
[1] and [3] presumes the year is in range. [2] and [4] presumes the day and month are in range.
If some of 3 values are not in range the function set_if_valid_date could return true or false arbitrarily. So if the day/month and year are just names and don't ensure range I would need to start by validating their range in [0] so that if some of them is out of range the function return false;
This mean that the user is not able, using the type system, to help the implementation to reduce needed check. I have no measures of the impact of these 3 range checks (6 comparisons). I just wanted to avoid them.
Even if we use other kind of cache, I don't see how the cached data can be accessed without checking the range are correct.
E.g. An alternative implementation using a cache for the year_month_cache
bool days_date::set_if_valid_date(year y, month m, day d) noexcept { // [0] auto c = year_month_cache(y, m); // [1] if (d.value() >= c.days_in_month) return false; x_ = c.days_before + d.value(); // [2] return true; }
This needs that y and m anre in range at [1] and d in in range at [2].
Howard, could you tell me if you find something wrong on my analysis? I want to be too purist?
Rob, all, you can also comment also :-) Vicente
On May 4, 2013, at 2:56 AM, Vicente J. Botet Escriba
Just to be sure I'm not trying to solve an issue that doesn't needs to be solved: Given
date d = day(33) / month(5) / 2013;
Note that I has a typo on the day. This is equivalent on my implementation to
date d(year(2013), month(5), day(33));
If I want to throw a bad date exception I would need to check that the day/month and year are in range. Next follows the code:
days_date::days_date(year y, month m, day d) { if (set_if_valid_date(y, m, d)) return; throw bad_date("day " + to_string(d) + " is out of range for " + to_string(y) + '-' + to_string(m)); }
bool days_date::set_if_valid_date(year y, month m, day d) noexcept { // [0] bool leap = is_leap(y.value()); // [1] const day_of_year::rep* year_data = days_in_year_before(leap);
if (!(d.value() <= year_data[m.value()] - year_data[m.value()-1])) // [2] { return false; } year::rep by = y.value() + 32799; // [3] x_ = days_before_year(by) + year_data[m.value()-1] + d.value(); // [4] return true; }
[1] and [3] presumes the year is in range. [2] and [4] presumes the day and month are in range.
If some of 3 values are not in range the function set_if_valid_date could return true or false arbitrarily. So if the day/month and year are just names and don't ensure range I would need to start by validating their range in [0] so that if some of them is out of range the function return false;
This mean that the user is not able, using the type system, to help the implementation to reduce needed check. I have no measures of the impact of these 3 range checks (6 comparisons). I just wanted to avoid them.
Even if we use other kind of cache, I don't see how the cached data can be accessed without checking the range are correct.
E.g. An alternative implementation using a cache for the year_month_cache
bool days_date::set_if_valid_date(year y, month m, day d) noexcept { // [0] auto c = year_month_cache(y, m); // [1] if (d.value() >= c.days_in_month) return false; x_ = c.days_before + d.value(); // [2] return true; }
This needs that y and m anre in range at [1] and d in in range at [2].
Howard, could you tell me if you find something wrong on my analysis? I want to be too purist?
I think I see the disconnect. Simplifying as much as possible (hopefully not too much), you prefer: days_date(year::rep, month::rep, day::rep, no_check_t); whereas I prefer for the same functionality: days_date(year, month, day); or for the sake of removing part of the debate: days_date(year, month, day, no_check_t); Your design doesn't require that year, month and day objects be "validation free", but mine does. And then I'm also seeing constructors like this running around (which I also experimented with): month::month(int m, no_check_t); Exploring design decisions like these would make a pretty good topic for a gsoc. :-) Howard
Le 04/05/13 17:26, Howard Hinnant a écrit :
On May 4, 2013, at 2:56 AM, Vicente J. Botet Escriba
wrote: So if the day/month and year are just names and don't ensure range I would need to start by validating their range in [0] so that if some of them is out of range the function return false;
This mean that the user is not able, using the type system, to help the implementation to reduce needed check. I have no measures of the impact of these 3 range checks (6 comparisons). I just wanted to avoid them.
Even if we use other kind of cache, I don't see how the cached data can be accessed without checking the range are correct.
E.g. An alternative implementation using a cache for the year_month_cache
bool days_date::set_if_valid_date(year y, month m, day d) noexcept { // [0] auto c = year_month_cache(y, m); // [1] if (d.value() >= c.days_in_month) return false; x_ = c.days_before + d.value(); // [2] return true; }
This needs that y and m anre in range at [1] and d in in range at [2].
Howard, could you tell me if you find something wrong on my analysis? I want to be too purist? I think I see the disconnect. Simplifying as much as possible (hopefully not too much), you prefer:
days_date(year::rep, month::rep, day::rep, no_check_t);
whereas I prefer for the same functionality:
days_date(year, month, day);
or for the sake of removing part of the debate:
days_date(year, month, day, no_check_t);
Your design doesn't require that year, month and day objects be "validation free", but mine does. Could you tell me why your is "validation free"? I'm sure we all will learn something interesting.
And then I'm also seeing constructors like this running around (which I also experimented with):
month::month(int m, no_check_t); I ind it ugly, but it does what is requested.
Exploring design decisions like these would make a pretty good topic for a gsoc. :-)
I don't know if Anurag will be for. Best, Vicente
I think I see the disconnect. Simplifying as much as possible (hopefully not too much), you prefer:
days_date(year::rep, month::rep, day::rep, no_check_t);
whereas I prefer for the same functionality:
days_date(year, month, day);
or for the sake of removing part of the debate:
days_date(year, month, day, no_check_t);
Your design doesn't require that year, month and day objects be "validation free", but mine does.
Howard, I am curious why are we not trying to make the default date type beginner-proof? Is it because we already have the "slash" form that validates the date? -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
On May 4, 2013, at 12:01 PM, Anurag Kalia
I think I see the disconnect. Simplifying as much as possible (hopefully not too much), you prefer:
days_date(year::rep, month::rep, day::rep, no_check_t);
whereas I prefer for the same functionality:
days_date(year, month, day);
or for the sake of removing part of the debate:
days_date(year, month, day, no_check_t);
Your design doesn't require that year, month and day objects be "validation free", but mine does.
Howard, I am curious why are we not trying to make the default date type beginner-proof? Is it because we already have the "slash" form that validates the date?
I think Vicente put it best:
On May 4, 2013, at 10:25 AM, Vicente J. Botet Escriba
H.H. approach is a little bit different * date constructors build unchecked dates * date factories build checked dates
I found this separation of behaviors to be something easily learned, remembered, and does not require the no_check_t syntax. Howard
Howard Hinnant-3 wrote
On May 4, 2013, at 12:01 PM, Anurag Kalia <
anurag.kalia@
> wrote:
I think I see the disconnect. Simplifying as much as possible (hopefully not too much), you prefer:
days_date(year::rep, month::rep, day::rep, no_check_t);
whereas I prefer for the same functionality:
days_date(year, month, day);
or for the sake of removing part of the debate:
days_date(year, month, day, no_check_t);
Your design doesn't require that year, month and day objects be "validation free", but mine does.
Howard, I am curious why are we not trying to make the default date type beginner-proof? Is it because we already have the "slash" form that validates the date?
I think Vicente put it best:
On May 4, 2013, at 10:25 AM, Vicente J. Botet Escriba <
vicente.botet@
> wrote:
H.H. approach is a little bit different * date constructors build unchecked dates * date factories build checked dates
I found this separation of behaviors to be something easily learned, remembered, and does not require the no_check_t syntax.
It makes sense when said like that! - Anurag. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
On May 4, 2013, at 12:10 PM, Howard Hinnant
On May 4, 2013, at 12:01 PM, Anurag Kalia
wrote: I think I see the disconnect. Simplifying as much as possible (hopefully not too much), you prefer:
days_date(year::rep, month::rep, day::rep, no_check_t);
whereas I prefer for the same functionality:
days_date(year, month, day);
or for the sake of removing part of the debate:
days_date(year, month, day, no_check_t);
Your design doesn't require that year, month and day objects be "validation free", but mine does.
Howard, I am curious why are we not trying to make the default date type beginner-proof? Is it because we already have the "slash" form that validates the date?
I think Vicente put it best:
On May 4, 2013, at 10:25 AM, Vicente J. Botet Escriba
wrote: H.H. approach is a little bit different * date constructors build unchecked dates * date factories build checked dates
I found this separation of behaviors to be something easily learned, remembered, and does not require the no_check_t syntax.
Both the constructors and make_date() can be overloaded with and without unchecked_t or no_check_t. That means the choice of syntax can be left to the user and context, rather than based upon checking or not. Nevertheless, I recognize there's value in the distinction you're making. ___ Rob (Sent from my portable computation engine)
Just to be sure I'm not trying to solve an issue that doesn't needs to be solved: Given
date d = day(33) / month(5) / 2013;
Note that I has a typo on the day. This is equivalent on my implementation to
date d(year(2013), month(5), day(33));
If I want to throw a bad date exception I would need to check that the day/month and year are in range. Next follows the code:
days_date::days_date(year y, month m, day d) { if (set_if_valid_date(y, m, d)) return; throw bad_date("day " + to_string(d) + " is out of range for " + to_string(y) + '-' + to_string(m)); }
bool days_date::set_if_valid_date(year y, month m, day d) noexcept { // [0] bool leap = is_leap(y.value()); // [1] const day_of_year::rep* year_data = days_in_year_before(leap);
if (!(d.value() <= year_data[m.value()] - year_data[m.value()-1])) // [2] { return false; } year::rep by = y.value() + 32799; // [3] x_ = days_before_year(by) + year_data[m.value()-1] + d.value(); // [4] return true; }
I have a question here. Why are we not throwing the exception from set_if_valid_date() itself? Why did we include noexcept in its definition? -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
Le 04/05/13 17:57, Anurag Kalia a écrit :
Just to be sure I'm not trying to solve an issue that doesn't needs to be solved: Given
date d = day(33) / month(5) / 2013;
Note that I has a typo on the day. This is equivalent on my implementation to
date d(year(2013), month(5), day(33));
If I want to throw a bad date exception I would need to check that the day/month and year are in range. Next follows the code:
days_date::days_date(year y, month m, day d) { if (set_if_valid_date(y, m, d)) return; throw bad_date("day " + to_string(d) + " is out of range for " + to_string(y) + '-' + to_string(m)); }
bool days_date::set_if_valid_date(year y, month m, day d) noexcept; I have a question here. Why are we not throwing the exception from set_if_valid_date() itself? Why did we include noexcept in its definition?
set_if_valid_date allows the user make changes without using exceptions. This is useful on some contexts. Best, Vicente
On May 4, 2013, at 12:16 PM, "Vicente J. Botet Escriba"
Le 04/05/13 17:57, Anurag Kalia a écrit :
I have a question here. Why are we not throwing the exception from set_if_valid_date() itself? Why did we include noexcept in its definition?
set_if_valid_date allows the user make changes without using exceptions. This is useful on some contexts.
You could add a no_throw_t overload, or an error_code overload. ___ Rob (Sent from my portable computation engine)
Le 04/05/13 20:50, Rob Stewart a écrit :
On May 4, 2013, at 12:16 PM, "Vicente J. Botet Escriba"
wrote: Le 04/05/13 17:57, Anurag Kalia a écrit :
I have a question here. Why are we not throwing the exception from set_if_valid_date() itself? Why did we include noexcept in its definition?
set_if_valid_date allows the user make changes without using exceptions. This is useful on some contexts. You could add a no_throw_t overload, or an error_code overload.
Yes, there are a lot of alternatives to this simple function. For the time been I have set_if_valid_date. This doesn't mean that I could not change,but this is not the major problem I have to fix. Best, Vicente
On May 3, 2013, at 7:05 PM, "Vicente J. Botet Escriba"
Le 03/05/13 19:53, Howard Hinnant a écrit :
On May 3, 2013, at 1:46 PM, "Vicente J. Botet Escriba"
wrote: Howard, I believe I start to understand why you don't want day/month/year to validate its input. I suspect that the rationale is quite simple, you are relaying on undefined behavior when the value of these parameters is out of range, isn't it? class day { int d_; public: constexpr explicit day(int d) noexcept : d_(d) {}
constexpr operator int() const noexcept {return d_;} };
day d(623); // no undefined behavior The UB is when you need to build a date if the day is not on the range. E.g. the implementation could use some arrays indexed with the day and access memory that has no sense. If we are sure that a day is in the range 1..31, this kind of assumptions can be done on the implementation. The implementation could not do it if the day value has not been validated previously or behavior would be undefined.
You can apply the same range checking in the date constructor as you intended for the day, month, and year constructors. There is no difference in performance, since the same checking is done. ___ Rob (Sent from my portable computation engine)
Le 04/05/13 14:32, Rob Stewart a écrit :
On May 3, 2013, at 7:05 PM, "Vicente J. Botet Escriba"
wrote: Le 03/05/13 19:53, Howard Hinnant a écrit :
On May 3, 2013, at 1:46 PM, "Vicente J. Botet Escriba"
wrote: Howard, I believe I start to understand why you don't want day/month/year to validate its input. I suspect that the rationale is quite simple, you are relaying on undefined behavior when the value of these parameters is out of range, isn't it? class day { int d_; public: constexpr explicit day(int d) noexcept : d_(d) {}
constexpr operator int() const noexcept {return d_;} };
day d(623); // no undefined behavior The UB is when you need to build a date if the day is not on the range. E.g. the implementation could use some arrays indexed with the day and access memory that has no sense. If we are sure that a day is in the range 1..31, this kind of assumptions can be done on the implementation. The implementation could not do it if the day value has not been validated previously or behavior would be undefined. You can apply the same range checking in the date constructor as you intended for the day, month, and year constructors. There is no difference in performance, since the same checking is done.
Yes, there is one. When using constant/constexpr objects there is no need to validate at runtime its validity, they are valid by definition at compile time. Best, Vicente
On May 4, 2013, at 8:39 AM, "Vicente J. Botet Escriba"
Le 04/05/13 14:32, Rob Stewart a écrit :
On May 3, 2013, at 7:05 PM, "Vicente J. Botet Escriba"
wrote: Le 03/05/13 19:53, Howard Hinnant a écrit :
On May 3, 2013, at 1:46 PM, "Vicente J. Botet Escriba"
wrote: The UB is when you need to build a date if the day is not on the range. E.g. the implementation could use some arrays indexed with the day and access memory that has no sense. If we are sure that a day is in the range 1..31, this kind of assumptions can be done on the implementation. The implementation could not do it if the day value has not been validated previously or behavior would be undefined. You can apply the same range checking in the date constructor as you intended for the day, month, and year constructors. There is no difference in performance, since the same checking is done.
Yes, there is one. When using constant/constexpr objects there is no need to validate at runtime its validity, they are valid by definition at compile time.
OK, but is that level of performance worth worrying about? ___ Rob (Sent from my portable computation engine)
Le 04/05/13 15:29, Rob Stewart a écrit :
On May 4, 2013, at 8:39 AM, "Vicente J. Botet Escriba"
wrote: Le 04/05/13 14:32, Rob Stewart a écrit :
On May 3, 2013, at 7:05 PM, "Vicente J. Botet Escriba"
wrote: Le 03/05/13 19:53, Howard Hinnant a écrit :
On May 3, 2013, at 1:46 PM, "Vicente J. Botet Escriba"
wrote: The UB is when you need to build a date if the day is not on the range. E.g. the implementation could use some arrays indexed with the day and access memory that has no sense. If we are sure that a day is in the range 1..31, this kind of assumptions can be done on the implementation. The implementation could not do it if the day value has not been validated previously or behavior would be undefined. You can apply the same range checking in the date constructor as you intended for the day, month, and year constructors. There is no difference in performance, since the same checking is done. Yes, there is one. When using constant/constexpr objects there is no need to validate at runtime its validity, they are valid by definition at compile time. OK, but is that level of performance worth worrying about?
This is the question. In the best case (all the date parts have already validated they are in range) we can make 6 comparison less. Accessing a year_month cache would mean two comparison plus the access itself (an two dimension array). We need an additional comparison to check if the day is a valid day for the year/month before adding it to the days since the year/month obtained from the cache. I suspect that this could take around the twice the 6 comparison time (but I'm not good on performance estimation), which would mean a loss of 30%. Of course, I see what I need to do, check what the compiler generates :( If as a result of this analysis the gain is less than 30%, I would move to let the validation of the date parts to the validated date build. This would mean no need for the no_check overloads for day(3) and year(2015). Best, Vicente
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
Why are we not using following constructor? date(int year, int month, int day); To clarify my position, benefits are not apparent to me. Since the order is strictly-defined, the writer of code has to remember the ordering in both cases. OTOH an year is typically in thousands: date(2013, 5, 3); It should ring a bell towards the ymd notation. The fact that constructor is named date already gives away its purpose. But then I haven't read any code that isn't written by me, or when the docs are not at hand, so I can always use second opinion.
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.
No questions there. I agree. And the fact that they can't be faster than constructor variants, I think it is okay to make it optimized for usability then.
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.
Again, no bone of contention. And, orthogonal to the topic, i want to say that I am still waving for functions like: make_ordinal_date(2013, 45); make_week_date(2013, w7, 5); Because mixing them with other constructors would be confusing. Keeping the notation to make them seprately, but part of same date class at the end, is something like their relation with actual gregorian calendar. At which point I wonder, why can't we be symmetrical and allow a function like: make_date(2013, 2, 27); As Rob says compiler will optimize this call due to RVO. we can make it explicit using move constructor too.
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.
I don't think we should mirror the behaviour of tm and time_t here. Somebody who needs only thin wrappers around actual functions can already use them. The fact that they originated in C doesn't make them unusable. The need for date class exists only because there isn't one around that behaves like it. That means that we should provide a solid abstraction in this case. Ultimately, passing objects around would lead to more sub-optimal (and confusing) code. Why would we forego the chance that C++ has of gaining a proper date type?
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!
I will not negate that I have opinions. But I am grateful for the invaluable feedback here. And we can't forget that my proposal rips off of yours. :D constexpr conversions are a case in point. They might have never struck me on my own. Maybe they might have, but who knows. I will try it on my own and wouldn't hesitate to contact you if I have problems. It would be much more rewarding that way. :-) Thanks everybody! Anurag. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
On May 3, 2013, at 6:24 PM, Anurag Kalia
Why are we not using following constructor?
date(int year, int month, int day);
The compiler cannot prevent a user from mistakenly writing date(5, 4, 2013) when the intent was date(2013, 5, 4).
To clarify my position, benefits are not apparent to me. Since the order is strictly-defined, the writer of code has to remember the ordering in both cases. OTOH an year is typically in thousands:
date(2013, 5, 3);
It should ring a bell towards the ymd notation. The fact that constructor is named date already gives away its purpose.
You're presuming familiarity and thoughtful use. Programmers are lazy and frequently hurried, so it's better to prevent errors by forcing date(may, 5, year(2013)). Your argument WRT the year being larger than any valid day or month is interesting, but applies only if you support years greater than, say, 100.
I am still waving for functions like:
make_ordinal_date(2013, 45); make_week_date(2013, w7, 5);
Is 5, in the latter, the day of the week? If so, I recommend a constant (or enumeration): make_week_date(2013, w7, fri);
Because mixing them with other constructors would be confusing. Keeping the notation to make them seprately, but part of same date class at the end, is something like their relation with actual gregorian calendar.
At which point I wonder, why can't we be symmetrical and allow a function like:
make_date(2013, 2, 27);
You could have these, instead, for more consistency: make_date(2013, 45); make_date(2013, w7, fri); ___ Rob (Sent from my portable computation engine)
Le 04/05/13 14:28, Rob Stewart a écrit :
On May 3, 2013, at 6:24 PM, Anurag Kalia
wrote: Why are we not using following constructor?
date(int year, int month, int day); The compiler cannot prevent a user from mistakenly writing date(5, 4, 2013) when the intent was date(2013, 5, 4).
To clarify my position, benefits are not apparent to me. Since the order is strictly-defined, the writer of code has to remember the ordering in both cases. OTOH an year is typically in thousands:
date(2013, 5, 3);
It should ring a bell towards the ymd notation. The fact that constructor is named date already gives away its purpose. You're presuming familiarity and thoughtful use. Programmers are lazy and frequently hurried, so it's better to prevent errors by forcing date(may, 5, year(2013)).
Your argument WRT the year being larger than any valid day or month is interesting, but applies only if you support years greater than, say, 100.
I am still waving for functions like:
make_ordinal_date(2013, 45); make_week_date(2013, w7, 5); Is 5, in the latter, the day of the week? If so, I recommend a constant (or enumeration):
make_week_date(2013, w7, fri); HH and mine have already these cosntant objects extern const weekday sun; extern const weekday mon; extern const weekday tue; extern const weekday wed; extern const weekday thu; extern const weekday fri; extern const weekday sat;
Because mixing them with other constructors would be confusing. Keeping the notation to make them seprately, but part of same date class at the end, is something like their relation with actual gregorian calendar.
At which point I wonder, why can't we be symmetrical and allow a function like:
make_date(2013, 2, 27); You could have these, instead, for more consistency:
make_date(2013, 45); make_date(2013, w7, fri);
The later overload is Ok. The following is ambiguous make_date(2013, 45); and need make_date(year(2013), 45); BTW, do you prefer w7 or w_7? Or a literal 7_w instead of a constant object? Vicente
On May 4, 2013, at 8:46 AM, "Vicente J. Botet Escriba"
Le 04/05/13 14:28, Rob Stewart a écrit :
On May 3, 2013, at 6:24 PM, Anurag Kalia
wrote: At which point I wonder, why can't we be symmetrical and allow a function like:
make_date(2013, 2, 27);
You could have these, instead, for more consistency:
make_date(2013, 45); make_date(2013, w7, fri);
The later overload is Ok. The following is ambiguous
make_date(2013, 45);
and need
make_date(year(2013), 45);
Right
BTW, do you prefer w7 or w_7? Or a literal 7_w instead of a constant object?
I'd prefer at least "wk" if not "week" in the name. I don't think an underscore is nice there, and I don't like the literal ordering. Therefore, I'd like to see week7 or wk7. ___ Rob (Sent from my portable computation engine)
On 2013-05-04 15:26, Rob Stewart wrote:
On May 4, 2013, at 8:46 AM, "Vicente J. Botet Escriba"
wrote: Le 04/05/13 14:28, Rob Stewart a écrit :
On May 3, 2013, at 6:24 PM, Anurag Kalia
wrote: At which point I wonder, why can't we be symmetrical and allow a function like:
make_date(2013, 2, 27); You could have these, instead, for more consistency:
make_date(2013, 45); make_date(2013, w7, fri); The later overload is Ok. The following is ambiguous
make_date(2013, 45);
and need
make_date(year(2013), 45); Right
BTW, do you prefer w7 or w_7? Or a literal 7_w instead of a constant object? I'd prefer at least "wk" if not "week" in the name. I don't think an underscore is nice there, and I don't like the literal ordering. Therefore, I'd like to see week7 or wk7.
Weeks? According to what way of counting weeks? Monday-weeks (Europe, ISO 8601), Saturday-weeks (Middle East), or Sunday-weeks (Canada, US, Mexico)? If boost should adhere to ISO 8601, which I think it should, why not go all in and mandate ordering of YMD as well? Regards, Anders Dalvander
Anders Dalvander-3 wrote
On 2013-05-04 15:26, Rob Stewart wrote:
On May 4, 2013, at 8:46 AM, "Vicente J. Botet Escriba" <
vicente.botet@
> wrote:
Le 04/05/13 14:28, Rob Stewart a écrit :
On May 3, 2013, at 6:24 PM, Anurag Kalia <
anurag.kalia@
> wrote:
At which point I wonder, why can't we be symmetrical and allow a function like:
make_date(2013, 2, 27); You could have these, instead, for more consistency:
make_date(2013, 45); make_date(2013, w7, fri); The later overload is Ok. The following is ambiguous
make_date(2013, 45);
and need
make_date(year(2013), 45); Right
BTW, do you prefer w7 or w_7? Or a literal 7_w instead of a constant object? I'd prefer at least "wk" if not "week" in the name. I don't think an underscore is nice there, and I don't like the literal ordering. Therefore, I'd like to see week7 or wk7.
Weeks? According to what way of counting weeks? Monday-weeks (Europe, ISO 8601), Saturday-weeks (Middle East), or Sunday-weeks (Canada, US, Mexico)?
If boost should adhere to ISO 8601, which I think it should, why not go all in and mandate ordering of YMD as well?
Regards, Anders Dalvander
My opinions: 1) ISO week date says week as W07. So my suggestion was to not deviate too much from it: w7, W7 and W07 are all acceptable to me. w7 is the easiest to write among them so the first choice of mine. That is, if we mandate use of enums at all. Otherwise, sticking to week(n) is the cleanest option. 2) We should stick to ISO 8601 week standard, i.e. Monday weeks. They are not commonplace enough to warrant multiple notations. OTOH, the different day formats are actually followed in different countries, according to that useful link by Howard. My suggestion is to use ISo ordering only in functions like date() or make_date(). But when using slash, as in: date d = day(25) / dec / 2013; It is very natural to me. Here in my part of world, d-m-y is the dominant format. Otherwise use of '/' itself is of no much use. Regards, Anurag. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
On May 4, 2013, at 6:15 PM, Anurag Kalia
Anders Dalvander-3 wrote
On 2013-05-04 15:26, Rob Stewart wrote:
On May 4, 2013, at 8:46 AM, "Vicente J. Botet Escriba" <
vicente.botet@
> wrote:
Le 04/05/13 14:28, Rob Stewart a écrit :
On May 3, 2013, at 6:24 PM, Anurag Kalia <
anurag.kalia@
> wrote:
At which point I wonder, why can't we be symmetrical and allow a function like:
make_date(2013, 2, 27); You could have these, instead, for more consistency:
make_date(2013, 45); make_date(2013, w7, fri); The later overload is Ok. The following is ambiguous
make_date(2013, 45);
and need
make_date(year(2013), 45); Right
BTW, do you prefer w7 or w_7? Or a literal 7_w instead of a constant object? I'd prefer at least "wk" if not "week" in the name. I don't think an underscore is nice there, and I don't like the literal ordering. Therefore, I'd like to see week7 or wk7.
Weeks? According to what way of counting weeks? Monday-weeks (Europe, ISO 8601), Saturday-weeks (Middle East), or Sunday-weeks (Canada, US, Mexico)?
If boost should adhere to ISO 8601, which I think it should, why not go all in and mandate ordering of YMD as well?
Regards, Anders Dalvander
My opinions:
1) ISO week date says week as W07. So my suggestion was to not deviate too much from it: w7, W7 and W07 are all acceptable to me. w7 is the easiest to write among them so the first choice of mine.
That is, if we mandate use of enums at all. Otherwise, sticking to week(n) is the cleanest option.
Or is iso_year_week another field type, which could be converted to and from the serial type?
2) We should stick to ISO 8601 week standard, i.e. Monday weeks. They are not commonplace enough to warrant multiple notations.
OTOH, the different day formats are actually followed in different countries, according to that useful link by Howard.
Recall that the C and C++ standards already define in struct tm: int tm_wday; // days since Sunday — [0, 6] It might get awkward to ignore this convention. It is most unfortunate that we have this conflict between C and ISO. We have to conflict with either C or ISO on this point, and I have no problem with either choice. Slight preference with conflicting with ISO just because I think C++ programmers are more likely to know about struct tm than ISO-8601. But I wouldn't loose sleep over either choice.
My suggestion is to use ISo ordering only in functions like date() or make_date().
But when using slash, as in:
date d = day(25) / dec / 2013;
It is very natural to me. Here in my part of world, d-m-y is the dominant format. Otherwise use of '/' itself is of no much use.
Lately I've been recalling that earlier drafts of my 2011 allowed one to specify just the first unit explicitly, and then if you know the 3 date orders, then the other two units are set and can be unambiguously implicit. Furthermore if the middle unit is specified as day, then the first and last units have to be month and year respectively. An early review found that confusing, but it did allow for very compact notation. And perhaps that decision should be revisited too. Taking your example: date d = day(25) / dec / 2013; date d = day(25) / 12 / 2013; date d = dec / 25 / 2013; date d = 12 / day(25) / 2013; date d = year(2013) / dec / 25; date d = year(2013) / 12 / 25; All of the above could be unambiguous. Furthermore: date d = 12 / last / 2013; In practice I have found that I don't mind specifying the first two units that much. And I have also found that despite the fact that my "natural/cultural" ordering is mdy, I tend to use the other orders quite freely depending upon the context. For example, if I have a compile-time month and compile time day, or I already have units of day and month, but an int for the year, I'll always use one of mdy or dmy: for (date d = jan/last/y; ... or: for (date d = mon[2]/jan/y; ... And when I already have a year type, I tend to use that as the first unit: for (date d = other_date.year() / jan / 1; ... I explored the use of operator-() for the date separator, but the operator precedence is problematic for expressions like: date d = days(30) + day(25)-dec-2013; Otherwise that would have lined up very nicely with ISO. Howard
On May 4, 2013, at 7:33 PM, Howard Hinnant
On May 4, 2013, at 6:15 PM, Anurag Kalia
wrote: Recall that the C and C++ standards already define in struct tm:
int tm_wday; // days since Sunday — [0, 6]
It might get awkward to ignore this convention. It is most unfortunate that we have this conflict between C and ISO. We have to conflict with either C or ISO on this point, and I have no problem with either choice. Slight preference with conflicting with ISO just because I think C++ programmers are more likely to know about struct tm than ISO-8601. But I wouldn't loose sleep over either choice.
I don't know what the ISO convention is, but I know the C convention from struct tm and tools.
My suggestion is to use ISo ordering only in functions like date() or make_date().
But when using slash, as in:
date d = day(25) / dec / 2013;
It is very natural to me. Here in my part of world, d-m-y is the dominant format. Otherwise use of '/' itself is of no much use.
Lately I've been recalling that earlier drafts of my 2011 allowed one to specify just the first unit explicitly, and then if you know the 3 date orders, then the other two units are set and can be unambiguously implicit. Furthermore if the middle unit is specified as day, then the first and last units have to be month and year respectively. An early review found that confusing, but it did allow for very compact notation. And perhaps that decision should be revisited too. Taking your example:
date d = day(25) / dec / 2013; date d = day(25) / 12 / 2013; date d = dec / 25 / 2013; date d = 12 / day(25) / 2013; date d = year(2013) / dec / 25; date d = year(2013) / 12 / 25;
Interesting. ___ Rob (Sent from my portable computation engine)
Le 05/05/13 00:15, Anurag Kalia a écrit :
OTOH, the different day formats are actually followed in different countries, according to that useful link by Howard. My suggestion is to use ISo ordering only in functions like date() or make_date(). But when using slash, as in: date d = day(25) / dec / 2013; It is very natural to me. Here in my part of world, d-m-y is the dominant format. Otherwise use of '/' itself is of no much use. I guess that make_date would be less conflicting than operator/() expressions on a C++ proposal to the standard. So the proposal should include make_date. I like however the operator/() expressions also.
Best, Vicente
Rob Stewart-6 wrote
On May 3, 2013, at 6:24 PM, Anurag Kalia <
anurag.kalia@
> wrote:
Why are we not using following constructor?
date(int year, int month, int day);
The compiler cannot prevent a user from mistakenly writing date(5, 4, 2013) when the intent was date(2013, 5, 4).
To clarify my position, benefits are not apparent to me. Since the order is strictly-defined, the writer of code has to remember the ordering in both cases. OTOH an year is typically in thousands:
date(2013, 5, 3);
It should ring a bell towards the ymd notation. The fact that constructor is named date already gives away its purpose.
You're presuming familiarity and thoughtful use. Programmers are lazy and frequently hurried, so it's better to prevent errors by forcing date(may, 5, year(2013)).
Your argument WRT the year being larger than any valid day or month is interesting, but applies only if you support years greater than, say, 100.
The vast majority of dates are supposed to be greater than year 100. Actually, vast majority are supposed to be greater than 1900. So that is not a problem. But any bug should be avoided. So, I am not really against the initial constructor. It is just that it doesn't seem to hit the right spot between natural and explicit.
I am still waving for functions like:
make_ordinal_date(2013, 45); make_week_date(2013, w7, 5);
Is 5, in the latter, the day of the week? If so, I recommend a constant (or enumeration):
make_week_date(2013, w7, fri);
That looks good. I like that syntax; i hope nobody else minds it.
Because mixing them with other constructors would be confusing. Keeping the notation to make them seprately, but part of same date class at the end, is something like their relation with actual gregorian calendar.
At which point I wonder, why can't we be symmetrical and allow a function like:
make_date(2013, 2, 27);
You could have these, instead, for more consistency:
make_date(2013, 45); make_date(2013, w7, fri);
___ Rob
Good suggestion. Now to consolidate my view up till now, I have following types of date construction in mind: 1) Unchecked: simple ymd constructor - date(year, month, day); date(day, month, year); date(month, day, year); 2) Checked: "natural" factory function - (the last argument also has an int for succinctness) year / month / day; day / month / year; month / day / year; This notation extends as - weekday[n] / month / year; and so on. 3) Again, checked: But named factory functions, follows ISO conventions - make_date(year, month, day); make_date(year, week, weekday); make_date(year, day_of_year); To be noted: 1) I have dropped no_check option. It is verbose and I never liked that. The constructor has a 'raw' feeling to it that lends itself well to an unchecked constructor. We have the other two categories of functions that check the validity. 2) I include the named factory functions because there seems no other way to make a date with so myriad options. The consistent naming indicates that the same object is being produced; which is exactly what needs to be said. 3) The "natural" construction (really, we need some name for them) is actually natural. I particularly like the dmy notation in this case, with last argument as int: day(25) / dec / 2013; As for the constants, the jan, feb, ..., dec as well as sun, ..., sat are there. Moreover, there are also _1st, _2nd, ... , _31st and last. There is nothing for these : day(1) and week(4). It is because the are not widespread and this notation is much more clear than any enumeration. Thoughts? There should be many. Regards, Anurag. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
Good suggestion. Now to consolidate my view up till now, I have following types of date construction in mind:
1) Unchecked:
simple ymd constructor -
date(year, month, day); date(day, month, year); date(month, day, year);
2) Checked:
"natural" factory function -
(the last argument also has an int for succinctness)
year / month / day; day / month / year; month / day / year;
This notation extends as -
weekday[n] / month / year; Does the preceding build a contextual or non-contextual date?
To be noted: 1) I have dropped no_check option. It is verbose and I never liked that. The constructor has a 'raw' feeling to it that lends itself well to an unchecked constructor. We have the other two categories of functions that check the validity. I have not yet a clear position on this concern. It is clear to me that /() factories must be checked as we can not state
Le 04/05/13 23:16, Anurag Kalia a écrit : the opposite.
2) I include the named factory functions because there seems no other way to make a date with so myriad options. The consistent naming indicates that the same object is being produced; which is exactly what needs to be said.
Do you mean that make_date would provide more services than the expression template using operator/()?
3) The "natural" construction (really, we need some name for them) is actually natural. I particularly like the dmy notation in this case, with last argument as int:
day(25) / dec / 2013;
As for the constants, the jan, feb, ..., dec as well as sun, ..., sat are there. Moreover, there are also _1st, _2nd, ... , _31st and last.
There is nothing for these : day(1) and week(4). It is because the are not widespread and this notation is much more clear than any enumeration.
Sorry I missed your position. Could you clarify what are you proposing? Best, Vicente
On May 4, 2013, at 5:16 PM, Anurag Kalia
"natural" factory function -
(the last argument also has an int for succinctness)
year / month / day; day / month / year; month / day / year;
This notation extends as -
weekday[n] / month / year;
and so on.
Considering Vicente's concept plan, a user-defined date type can be extended readily via make_date(), but not via this notation. I'd consider that another argument against this unless the design provided specific customization points to enable new date types.
1) I have dropped no_check option. It is verbose and I never liked that. The constructor has a 'raw' feeling to it that lends itself well to an unchecked constructor. We have the other two categories of functions that check the validity.
Why not simply offer, say, date and raw_date? Pick any two names, but one is checked and the other not. Derivation or composition can simplify the implementation if the checked version in terms of the unchecked.
2) I include the named factory functions because there seems no other way to make a date with so myriad options. The consistent naming indicates that the same object is being produced; which is exactly what needs to be said.
I like that, of course.
3) The "natural" construction (really, we need some name for them) is actually natural. I particularly like the dmy notation in this case, with last argument as int:
day(25) / dec / 2013;
Any one unit can be int, since the others are typed.
As for the constants, the jan, feb, ..., dec as well as sun, ..., sat are there.
OK
Moreover, there are also _1st, _2nd, ... , _31st and last.
Why not use (and extend) the placeholders used for bind? date(_1, dec, 2013); Those are shorter and less prone to sounding odd. By the latter I mean that I'd never say "first December 2013", but would s/first/one/. OTOH, I would say "December first, 2013", so perhaps we could offer ordinals, too. Given your preference to provide support only for the YMD order, which is not an order I would speak otherwise, I don't know whether "first" or "one" is more natural, so maybe that also argues for both. ___ Rob (Sent from my portable computation engine)
On 2013-05-05 07:28, Rob Stewart wrote:
On May 4, 2013, at 5:16 PM, Anurag Kalia
wrote: As for the constants, the jan, feb, ..., dec as well as sun, ..., sat are there.
OK
Moreover, there are also _1st, _2nd, ... , _31st and last.
Why not use (and extend) the placeholders used for bind?
Which bind? std? boost? The Boost.Bind placeholders should be avoided because their position in the global namespace has caused lots of grief. I'd say overloading any of these placeholders is confusing and dangerous.
date(_1, dec, 2013);
Those are shorter and less prone to sounding odd. By the latter I mean that I'd never say "first December 2013", but would s/first/one/. OTOH, I would say "December first, 2013", so perhaps we could offer ordinals, too.
Given your preference to provide support only for the YMD order, which is not an order I would speak otherwise, I don't know whether "first" or "one" is more natural, so maybe that also argues for both.
FWIW, I would say "first December", not "one December" (although I'd be more likely to say "December first" or even "December the first" than either of those). I'm a native British English speaker. John Bytheway
On May 5, 2013, at 10:52 AM, John Bytheway
On 2013-05-05 07:28, Rob Stewart wrote:
On May 4, 2013, at 5:16 PM, Anurag Kalia
wrote: Moreover, there are also _1st, _2nd, ... , _31st and last.
Why not use (and extend) the placeholders used for bind?
Which bind? std? boost? The Boost.Bind placeholders should be avoided because their position in the global namespace has caused lots of grief.
I was speaking more abstractly of using _1, etc.
I'd say overloading any of these placeholders is confusing and dangerous.
Surely you don't mean using *some* _1 is confusing or dangerous.
date(_1, dec, 2013);
Those are shorter and less prone to sounding odd. By the latter I mean that I'd never say "first December 2013", but would s/first/one/. OTOH, I would say "December first, 2013", so perhaps we could offer ordinals, too.
Given your preference to provide support only for the YMD order, which is not an order I would speak otherwise, I don't know whether "first" or "one" is more natural, so maybe that also argues for both.
FWIW, I would say "first December", not "one December"
I was in the USAF, where 1 DEC 2013 is the vernacular, pronounced "one December twenty thirteen."
(although I'd be more likely to say "December first" or even "December the first" than either of those). I'm a native British English speaker.
I, too would be more likely, now, to say "December first," but that order isn't in the offing. ___ Rob (Sent from my portable computation engine)
On 2013-05-05 19:36, Rob Stewart wrote:
On May 5, 2013, at 10:52 AM, John Bytheway
wrote: On 2013-05-05 07:28, Rob Stewart wrote:
On May 4, 2013, at 5:16 PM, Anurag Kalia
wrote: Moreover, there are also _1st, _2nd, ... , _31st and last.
Why not use (and extend) the placeholders used for bind?
Which bind? std? boost? The Boost.Bind placeholders should be avoided because their position in the global namespace has caused lots of grief.
I was speaking more abstractly of using _1, etc.
I'd say overloading any of these placeholders is confusing and dangerous.
Surely you don't mean using *some* _1 is confusing or dangerous.
I mean that if you use a _1, then it shouldn't be a _1 which is also a placeholder. It should be from a different namespace. John
Le 05/05/13 13:28, Rob Stewart a écrit :
On May 4, 2013, at 5:16 PM, Anurag Kalia
wrote: "natural" factory function -
(the last argument also has an int for succinctness)
year / month / day; day / month / year; month / day / year;
This notation extends as -
weekday[n] / month / year;
and so on. Considering Vicente's concept plan, a user-defined date type can be extended readily via make_date(), but not via this notation. I'd consider that another argument against this unless the design provided specific customization points to enable new date types. Rob, please could you clarify the preceding paragraph?
1) I have dropped no_check option. It is verbose and I never liked that. The constructor has a 'raw' feeling to it that lends itself well to an unchecked constructor. We have the other two categories of functions that check the validity. Why not simply offer, say, date and raw_date? Pick any two names, but one is checked and the other not. Derivation or composition can simplify the implementation if the checked version in terms of the unchecked. I like when the C++ type system is used to mean different invariants.
raw_date d1 ; date d2 = d1; // throws if d1 is not valid This moves the play from looking a better name for no_check to one for raw_date. Independently I was exploring a is_validated() and validate() functions. See the specific post. With your approach these two functions have no sense of course as it is the type system that plays the role: raw_date::is_validated() ==false date::is_validated() == true and the validate function is the conversion from raw_date to date. I like it. Best, Vicente Best, Vicente
On May 5, 2013, at 4:11 PM, Vicente J. Botet Escriba
Le 05/05/13 13:28, Rob Stewart a écrit :
On May 4, 2013, at 5:16 PM, Anurag Kalia
wrote: "natural" factory function -
(the last argument also has an int for succinctness)
year / month / day; day / month / year; month / day / year;
This notation extends as -
weekday[n] / month / year;
and so on. Considering Vicente's concept plan, a user-defined date type can be extended readily via make_date(), but not via this notation. I'd consider that another argument against this unless the design provided specific customization points to enable new date types. Rob, please could you clarify the preceding paragraph?
1) I have dropped no_check option. It is verbose and I never liked that. The constructor has a 'raw' feeling to it that lends itself well to an unchecked constructor. We have the other two categories of functions that check the validity. Why not simply offer, say, date and raw_date? Pick any two names, but one is checked and the other not. Derivation or composition can simplify the implementation if the checked version in terms of the unchecked. I like when the C++ type system is used to mean different invariants.
raw_date d1 ; date d2 = d1; // throws if d1 is not valid
This moves the play from looking a better name for no_check to one for raw_date.
Independently I was exploring a is_validated() and validate() functions. See the specific post. With your approach these two functions have no sense of course as it is the type system that plays the role: raw_date::is_validated() ==false date::is_validated() == true and the validate function is the conversion from raw_date to date.
Is this being considered for field types, serial types, or both? The reason I ask is that all serial values are valid dates, at least those in range, and we can make the range as big as we want. It is only the field types that are problematic when day, month, or week is out of range. Howard
Is this being considered for field types, serial types, or both?
The reason I ask is that all serial values are valid dates, at least those in range, and we can make the range as big as we want. It is only the field types that are problematic when day, month, or week is out of range.
Howard
The problem with this function is that it can give us a false sense of security. If we ask it to make an unchecked date "31-feb-2013", it would gladly do it for us. With field representation, we can catch it. But with serial representation, it would make a valid but wrong date! I don't think a valid date that the user didn't ask for is really a valid date, is it? Anurag. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
On May 5, 2013, at 8:12 PM, Anurag Kalia
Is this being considered for field types, serial types, or both?
The reason I ask is that all serial values are valid dates, at least those in range, and we can make the range as big as we want. It is only the field types that are problematic when day, month, or week is out of range.
Howard
The problem with this function is that it can give us a false sense of security. If we ask it to make an unchecked date "31-feb-2013", it would gladly do it for us. With field representation, we can catch it. But with serial representation, it would make a valid but wrong date! I don't think a valid date that the user didn't ask for is really a valid date, is it?
You have stated well my concern. In order to spread this to serial_date, serial_date would need to carry an extra bool, or at least an extra bit, which means: came from untrusted source or not. And that would compromise the goal of the serial_date day arithmetic being no more expensive than integral arithmetic. It would also mean that serial_date is no longer just a simple chrono::time_point. If validity checking is strictly confined to field types, that seems reasonable. But extending it to serial types seems problematic. Once serial, the value is a valid date unless metadata says otherwise. And metadata in a date type has a less-than stellar history. ;-) Howard
Le 06/05/13 02:12, Anurag Kalia a écrit :
Is this being considered for field types, serial types, or both?
The reason I ask is that all serial values are valid dates, at least those in range, and we can make the range as big as we want. It is only the field types that are problematic when day, month, or week is out of range.
Howard The problem with this function is that it can give us a false sense of security. If we ask it to make an unchecked date "31-feb-2013", it would gladly do it for us. With field representation, we can catch it. But with serial representation, it would make a valid but wrong date! I don't think a valid date that the user didn't ask for is really a valid date, is it?
Good point Anarag. Vicente
Le 06/05/13 00:21, Howard Hinnant a écrit :
On May 5, 2013, at 4:11 PM, Vicente J. Botet Escriba
wrote: Le 05/05/13 13:28, Rob Stewart a écrit :
On May 4, 2013, at 5:16 PM, Anurag Kalia
wrote: "natural" factory function -
(the last argument also has an int for succinctness)
year / month / day; day / month / year; month / day / year;
This notation extends as -
weekday[n] / month / year;
and so on. Considering Vicente's concept plan, a user-defined date type can be extended readily via make_date(), but not via this notation. I'd consider that another argument against this unless the design provided specific customization points to enable new date types. Rob, please could you clarify the preceding paragraph? 1) I have dropped no_check option. It is verbose and I never liked that. The constructor has a 'raw' feeling to it that lends itself well to an unchecked constructor. We have the other two categories of functions that check the validity. Why not simply offer, say, date and raw_date? Pick any two names, but one is checked and the other not. Derivation or composition can simplify the implementation if the checked version in terms of the unchecked. I like when the C++ type system is used to mean different invariants.
raw_date d1 ; date d2 = d1; // throws if d1 is not valid
This moves the play from looking a better name for no_check to one for raw_date.
Independently I was exploring a is_validated() and validate() functions. See the specific post. With your approach these two functions have no sense of course as it is the type system that plays the role: raw_date::is_validated() ==false date::is_validated() == true and the validate function is the conversion from raw_date to date. Is this being considered for field types, serial types, or both? I would like for all. If we want to provide unchecked date construction it is normal to provide if it is a valid date later. But, ...
The reason I ask is that all serial values are valid dates, at least those in range, and we can make the range as big as we want. It is only the field types that are problematic when day, month, or week is out of range.
I see. This means that the serial date can not provide the is_valid() function as we don't know, as Anaurag pointed out in the next post, if the stored date corresponds to maybe a valid date or not. is_valid can return a tribool in this case, and if the serial_date is validated at construction, the function return true, else return undefined. But this is not too useful if is_validated. BTW, N3344 suggest only ymd validation. Best, Vicente
On May 5, 2013, at 4:11 PM, "Vicente J. Botet Escriba"
Le 05/05/13 13:28, Rob Stewart a écrit :
On May 4, 2013, at 5:16 PM, Anurag Kalia
wrote: "natural" factory function -
(the last argument also has an int for succinctness)
year / month / day; day / month / year; month / day / year;
This notation extends as -
weekday[n] / month / year;
and so on. Considering Vicente's concept plan, a user-defined date type can be extended readily via make_date(), but not via this notation. I'd consider that another argument against this unless the design provided specific customization points to enable new date types. Rob, please could you clarify the preceding paragraph?
Try this: I think your idea of date concepts has merit. Given such concepts, a user-defined date type could be used by various algorithms if they satisfy an appropriate concept. With that support for extensibility, thought should be given to how such types can fit into the factory interfaces, too. make_date() can be extended trivially in the user's namespace. The same is not true of the /-based interface. ___ Rob (Sent from my portable computation engine)
On May 3, 2013, at 10:58 AM, Howard Hinnant
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 see we agree on the use of day, month, year, but I don't agree with a single order. More on that below.
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).
The use of an extra tag argument, creating a separate overload set for use when checking isn't needed, doesn't seem a bad approach.
Checked interface: ------------------
I like the use of factory functions here. I also like an easy-to-remember, convenience, forgiving, type-safety.
We agree thus far.
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;
To implement those, you must either introduce intermediate types, like year_and_month, with and overloaded operator /, or you need expression templates to shuffle the argument order before forwarding to a single, checked, date constructor. The same is possible with overloaded date constructors: date(year, month, day); date(month, day, year); date(day, month, year); plus overloads permitting 1 of 3 to be and integer for each supported order.
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
That's a good rationale.
The first two units have to explicit. The last unit can be implicit.
Overloaded constructors permit the first or second unit to 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.
My version is even easier, while still safe.
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.
Surely you'll remember the constructor spelling as easily. Given that, my suggestion is one overloaded factory function: make_date(year, month, day); etc. This would have the same set of overloads as the date constructors, including the no_check_t (or unchecked_t parameter). The result is easy to use and recall, efficient, and safe.
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.
Right
-----------------
Two date types:
1. Serial:
typedef duration
>> days; typedef time_point 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.
While I understand the motivation, I do hope you're implying more encapsulation than for the C types. ___ Rob (Sent from my portable computation engine)
On May 4, 2013, at 7:57 AM, Rob Stewart
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;
To implement those, you must either introduce intermediate types, like year_and_month, with and overloaded operator /, or you need expression templates to shuffle the argument order before forwarding to a single, checked, date constructor.
Correct, I was strongly leaning towards the former: intermediate types like year_and_month. And an open question is whether or not year_and_month would be a useful type to the client outside of this role. E.g. would it be useful to perform month arithmetic on a year_month object? Would it be helpful to get the number of days in a month out of a year_month object? Or is year_month simply an implementation detail that the client never sees? Personally I am still exploring these questions. Howard
Correct, I was strongly leaning towards the former: intermediate types like year_and_month. And an open question is whether or not year_and_month would be a useful type to the client outside of this role. E.g. would it be useful to perform month arithmetic on a year_month object? Would it be helpful to get the number of days in a month out of a year_month object? Or is year_month simply an implementation detail that the client never sees?
Personally I am still exploring these questions.
IMO they should be implementation. day_month is ambiguous so it shouldn't have much use. I am confused about year_month though. It does have some uses. But if we publicize it, it would be clear it has its main use in the factory function. And then we would have to make day_month public too, which I am strongly leaning against. So overall, I would like to keep it hidden only. I don't think it has uses enough to make it public. Moreover, its existence completely relies on our factory function; nobody would have meant to include it in API had that function not been in use. So, that gives us a hint too. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
On May 4, 2013, at 12:24 PM, Howard Hinnant
On May 4, 2013, at 7:57 AM, Rob Stewart
wrote: 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;
To implement those, you must either introduce intermediate types, like year_and_month, with an overloaded operator /, or you need expression templates to shuffle the argument order before forwarding to a single, checked, date constructor.
Correct, I was strongly leaning towards the former: intermediate types like year_and_month. And an open question is whether or not year_and_month would be a useful type to the client outside of this role. E.g. would it be useful to perform month arithmetic on a year_month object? Would it be helpful to get the number of days in a month out of a year_month object? Or is year_month simply an implementation detail that the client never sees?
Personally I am still exploring these questions.
I suppose there could be some limited use for such types, but I suspect it would be better that they be implementation details. One thing to consider in all of this, is the effect on debugging. The more objects created, the more stack frames to step through. That shouldn't be a primary consideration, but it can be useful to break ties. ___ Rob (Sent from my portable computation engine)
On 5/3/2013 8:30 AM, Vicente J. Botet Escriba wrote:
Le 03/05/13 13:52, Rob Stewart a écrit :
On May 3, 2013, at 3:47 AM, "Vicente J. Botet Escriba"
wrote: You can even handle other orders that way:
date(month, unsigned, year); date(month, day, unsigned); date(unsigned, day, month); date(year, unsigned, month);
One can also be explicit for both day and year:
date(year, month, day); date(month, day, year); date(day, month, year); date(day, year, month); I don't think that the constructor should support different orderings. It could, easily, so why not? Different locales have different preferred orderings. All can learn to use the descending magnitude order, but a little flexibility, without ambiguity, would be nice.
I'm assuming explicit constructors for day and year, of course. Of course. And implicit conversion to his representation, so that
date(2013, may, 3, no_check)
is yet valid. That's where I disagree. If you have explicit constructors for year and day, and no constructor accepting two ::rep arguments, then your example won't compile.
That's also what enables support for other argument orders. The no_check constructor allows only year,month,day order of arguments. If the user know that the date is valid it can know the order. This is a low level function.
We could have a factory make_date function that doesn't checks the validity of the date and avoids the no_check parameter
make_ymd_date(2013, may, 3) I don't understand what no_check has to do with such a function. Add an overload that accepts no_check_t to get that.
The name is not the good one.
make_unchecked_date(2013, may, 3); or
make_valid_date(2013, may, 3);
This reminds me of the constexpr date constructor sketched out at: http://akrzemi1.wordpress.com/2011/05/06/compile-time-computations/
On May 3, 2013, at 9:30 AM, "Vicente J. Botet Escriba"
Le 03/05/13 13:52, Rob Stewart a écrit :
On May 3, 2013, at 3:47 AM, "Vicente J. Botet Escriba"
wrote: You can even handle other orders that way:
date(month, unsigned, year); date(month, day, unsigned); date(unsigned, day, month); date(year, unsigned, month);
One can also be explicit for both day and year:
date(year, month, day); date(month, day, year); date(day, month, year); date(day, year, month); I don't think that the constructor should support different orderings. It could, easily, so why not? Different locales have different preferred orderings. All can learn to use the descending magnitude order, but a little flexibility, without ambiguity, would be nice.
I'm assuming explicit constructors for day and year, of course. Of course. And implicit conversion to his representation, so that
date(2013, may, 3, no_check)
is yet valid. That's where I disagree. If you have explicit constructors for year and day, and no constructor accepting two ::rep arguments, then your example won't compile.
That's also what enables support for other argument orders. The no_check constructor allows only year,month,day order of arguments. If the user know that the date is valid it can know the order. This is a low level function.
I'm talking of possibilities, not about an existing design. In the US, we write dates in a way that everyone else thinks is weird: month/day/year. A constructor taking three integral arguments will be a stumbling block for US users. We can, of course, learn the right order, but I guarantee that many -- me, certainly -- will find it necessary to consult the docs every time, if there's more than a fortnight between uses of that constructor. Even if it were validated, not all misuses can be detected. Using typed arguments, despite the added verbosity, solves that, though probably at the expense of all of those who only think of YMD order. That's also why I suggested all of the constructor possibilities above.
We could have a factory make_date function that doesn't checks the validity of the date and avoids the no_check parameter
make_ymd_date(2013, may, 3) I don't understand what no_check has to do with such a function. Add an overload that accepts no_check_t to get that. The name is not the good one.
make_unchecked_date(2013, may, 3); or
make_valid_date(2013, may, 3);
make_date(year(2013), may, 3) works just as well and can be overloaded for other orders. You could add overloads that take no_check_t (or, maybe, unvalidated_t), or just create a similar set of overloads of your make_unvalidate_date(). ___ Rob (Sent from my portable computation engine)
Le 04/05/13 03:30, Rob Stewart a écrit :
On May 3, 2013, at 9:30 AM, "Vicente J. Botet Escriba"
wrote: Le 03/05/13 13:52, Rob Stewart a écrit :
On May 3, 2013, at 3:47 AM, "Vicente J. Botet Escriba"
wrote: You can even handle other orders that way:
date(month, unsigned, year); date(month, day, unsigned); date(unsigned, day, month); date(year, unsigned, month);
One can also be explicit for both day and year:
date(year, month, day); date(month, day, year); date(day, month, year); date(day, year, month); I don't think that the constructor should support different orderings. It could, easily, so why not? Different locales have different preferred orderings. All can learn to use the descending magnitude order, but a little flexibility, without ambiguity, would be nice.
I'm assuming explicit constructors for day and year, of course. Of course. And implicit conversion to his representation, so that
date(2013, may, 3, no_check)
is yet valid. That's where I disagree. If you have explicit constructors for year and day, and no constructor accepting two ::rep arguments, then your example won't compile.
That's also what enables support for other argument orders. The no_check constructor allows only year,month,day order of arguments. If the user know that the date is valid it can know the order. This is a low level function. I'm talking of possibilities, not about an existing design. In the US, we write dates in a way that everyone else thinks is weird: month/day/year. A constructor taking three integral arguments will be a stumbling block for US users. We can, of course, learn the right order, but I guarantee that many -- me, certainly -- will find it necessary to consult the docs every time, if there's more than a fortnight between uses of that constructor. Even if it were validated, not all misuses can be detected.
Using typed arguments, despite the added verbosity, solves that, though probably at the expense of all of those who only think of YMD order. That's also why I suggested all of the constructor possibilities above.
I understand your concern. There are some that are requesting a constructor as simple as date(int y, int m, int d); that presumes the values given stand for a valid date. Others want the C++ type system helps them as much as possible. Would the following be enough satisfactory Taking in account only the ymd constructors // date validaty check date(year, month, day); date(year, month, int); date(year, int, day); date(int, month, day); // no date validity check date(year, month, day, no_check_t); date(year, month, int, no_check_t); date(year, int, day, no_check_t); date(int, month, day, no_check_t); // no date validity check parameters are in ymd orther. date(int y, int m, int d, ymd_t); Note the last one has the no_check before to avoid ambiguity as all year, month and day are convertible to int. I'm of course open to better names for no_check_t and ymd_t.
We could have a factory make_date function that doesn't checks the validity of the date and avoids the no_check parameter
make_ymd_date(2013, may, 3) I don't understand what no_check has to do with such a function. Add an overload that accepts no_check_t to get that. The name is not the good one.
make_unchecked_date(2013, may, 3); or
make_valid_date(2013, may, 3); make_date(year(2013), may, 3) works just as well and can be overloaded for other orders.
You could add overloads that take no_check_t (or, maybe, unvalidated_t), or just create a similar set of overloads of your make_unvalidate_date().
If I understand you make_date would behave as the / factory and is useful for those that don't like the / factory syntax. make_unvalidate_date() would use the no_check_t overloads. Both would have the same set of of overload orders. I would prefer to let pending these factory discussion. We could come back once we agree on the date constructors. Best, Vicente
On May 4, 2013, at 3:29 AM, "Vicente J. Botet Escriba"
Le 04/05/13 03:30, Rob Stewart a écrit :
On May 3, 2013, at 9:30 AM, "Vicente J. Botet Escriba"
wrote: Le 03/05/13 13:52, Rob Stewart a écrit :
On May 3, 2013, at 3:47 AM, "Vicente J. Botet Escriba"
wrote: You can even handle other orders that way:
date(month, unsigned, year); date(month, day, unsigned); date(unsigned, day, month); date(year, unsigned, month);
One can also be explicit for both day and year:
date(year, month, day); date(month, day, year); date(day, month, year); date(day, year, month); I don't think that the constructor should support different orderings. It could, easily, so why not? Different locales have different preferred orderings. All can learn to use the descending magnitude order, but a little flexibility, without ambiguity, would be nice.
I'm assuming explicit constructors for day and year, of course. Of course. And implicit conversion to his representation, so that
date(2013, may, 3, no_check)
is yet valid. That's where I disagree. If you have explicit constructors for year and day, and no constructor accepting two ::rep arguments, then your example won't compile.
That's also what enables support for other argument orders. The no_check constructor allows only year,month,day order of arguments. If the user know that the date is valid it can know the order. This is a low level function. I'm talking of possibilities, not about an existing design. In the US, we write dates in a way that everyone else thinks is weird: month/day/year. A constructor taking three integral arguments will be a stumbling block for US users. We can, of course, learn the right order, but I guarantee that many -- me, certainly -- will find it necessary to consult the docs every time, if there's more than a fortnight between uses of that constructor. Even if it were validated, not all misuses can be detected.
Using typed arguments, despite the added verbosity, solves that, though probably at the expense of all of those who only think of YMD order. That's also why I suggested all of the constructor possibilities above.
I understand your concern.
There are some that are requesting a constructor as simple as
date(int y, int m, int d);
that presumes the values given stand for a valid date.
Others want the C++ type system helps them as much as possible.
Would the following be enough satisfactory
Taking in account only the ymd constructors
// date validaty check date(year, month, day); date(year, month, int); date(year, int, day); date(int, month, day);
// no date validity check date(year, month, day, no_check_t); date(year, month, int, no_check_t); date(year, int, day, no_check_t); date(int, month, day, no_check_t);
// no date validity check parameters are in ymd orther. date(int y, int m, int d, ymd_t);
Note the last one has the no_check before to avoid ambiguity as all year, month and day are convertible to int.
I'm of course open to better names for no_check_t and ymd_t.
That isn't nearly as good as my suggestion. First, your ymd_t overload doesn't actually enforce that order of arguments. Second, your version doesn't permit other natural orders. It certainly forces YMD, except for the ymd_t overload, but it's less flexible. I agree with the addition of the no_check_t (or unchecked_t) parameter since using the dangerous unvalidated constructors should require extra effort and should be visible in the calling code.
make_unchecked_date(2013, may, 3); or
make_valid_date(2013, may, 3); make_date(year(2013), may, 3) works just as well and can be overloaded for other orders.
You could add overloads that take no_check_t (or, maybe, unvalidated_t), or just create a similar set of overloads of your make_unvalidate_date().
s/unvalidated_t/unchecked_t/ would be better.
If I understand you make_date would behave as the / factory and is useful for those that don't like the / factory syntax. make_unvalidate_date() would use the no_check_t overloads. Both would have the same set of of overload orders.
I'm not entirely against the /-based factories, but they really don't offer an advantage over an ordinary make_date() function. The latter is also in keeping with make_shared(), make_unique(), and make_pair(). Furthermore, as I've noted in other posts, make_date() can be overloaded for unchecked, day of year, and weekday date construction use cases. One name to rule them all, so to speak.
I would prefer to let pending these factory discussion. We could come back once we agree on the date constructors.
OK, but allow me to note that the date constructors and make_date() can have the same set of overloads, making them play together very nicely. ___ Rob (Sent from my portable computation engine)
On May 4, 2013, at 3:29 AM, "Vicente J. Botet Escriba"
wrote: Le 04/05/13 03:30, Rob Stewart a écrit :
On May 3, 2013, at 9:30 AM, "Vicente J. Botet Escriba"
wrote: Le 03/05/13 13:52, Rob Stewart a écrit :
On May 3, 2013, at 3:47 AM, "Vicente J. Botet Escriba"
wrote: > You can even handle other orders that way: > > date(month, unsigned, year); > date(month, day, unsigned); > date(unsigned, day, month); > date(year, unsigned, month); > > One can also be explicit for both day and year: > > date(year, month, day); > date(month, day, year); > date(day, month, year); > date(day, year, month); I don't think that the constructor should support different orderings. It could, easily, so why not? Different locales have different preferred orderings. All can learn to use the descending magnitude order, but a little flexibility, without ambiguity, would be nice.
> I'm assuming explicit constructors for day and year, of course. Of course. And implicit conversion to his representation, so that
date(2013, may, 3, no_check)
is yet valid. That's where I disagree. If you have explicit constructors for year and day, and no constructor accepting two ::rep arguments, then your example won't compile.
That's also what enables support for other argument orders. The no_check constructor allows only year,month,day order of arguments. If the user know that the date is valid it can know the order. This is a low level function. I'm talking of possibilities, not about an existing design. In the US, we write dates in a way that everyone else thinks is weird: month/day/year. A constructor taking three integral arguments will be a stumbling block for US users. We can, of course, learn the right order, but I guarantee that many -- me, certainly -- will find it necessary to consult the docs every time, if there's more than a fortnight between uses of that constructor. Even if it were validated, not all misuses can be detected.
Using typed arguments, despite the added verbosity, solves that, though probably at the expense of all of those who only think of YMD order. That's also why I suggested all of the constructor possibilities above.
I understand your concern.
There are some that are requesting a constructor as simple as
date(int y, int m, int d);
that presumes the values given stand for a valid date.
Others want the C++ type system helps them as much as possible.
Would the following be enough satisfactory
Taking in account only the ymd constructors
// date validaty check date(year, month, day); date(year, month, int); date(year, int, day); date(int, month, day);
// no date validity check date(year, month, day, no_check_t); date(year, month, int, no_check_t); date(year, int, day, no_check_t); date(int, month, day, no_check_t);
// no date validity check parameters are in ymd orther. date(int y, int m, int d, ymd_t);
Note the last one has the no_check before to avoid ambiguity as all year, month and day are convertible to int.
I'm of course open to better names for no_check_t and ymd_t. That isn't nearly as good as my suggestion. First, your ymd_t overload doesn't actually enforce that order of arguments. This function is needed as there are people that are requesting a int
Le 04/05/13 14:50, Rob Stewart a écrit : based interface without named types. You can always use the other constructors.
Second, your version doesn't permit other natural orders. It certainly forces YMD, except for the ymd_t overload, but it's less flexible.
I agree with the addition of the no_check_t (or unchecked_t) parameter since using the dangerous unvalidated constructors should require extra effort and should be visible in the calling code.
make_unchecked_date(2013, may, 3); or
make_valid_date(2013, may, 3); make_date(year(2013), may, 3) works just as well and can be overloaded for other orders.
You could add overloads that take no_check_t (or, maybe, unvalidated_t), or just create a similar set of overloads of your make_unvalidate_date(). s/unvalidated_t/unchecked_t/ would be better.
I wonder if no_throw couldn't be adopted.
If I understand you make_date would behave as the / factory and is useful for those that don't like the / factory syntax. make_unvalidate_date() would use the no_check_t overloads. Both would have the same set of of overload orders. I'm not entirely against the /-based factories, but they really don't offer an advantage over an ordinary make_date() function. The latter is also in keeping with make_shared(), make_unique(), and make_pair(). Furthermore, as I've noted in other posts, make_date() can be overloaded for unchecked, day of year, and weekday date construction use cases. One name to rule them all, so to speak.
Agreed. I suspect the /-based syntax was proposed as it is already used on the date domain. IMO, is a _nice_ to have feature.
I would prefer to let pending these factory discussion. We could come back once we agree on the date constructors. OK, but allow me to note that the date constructors and make_date() can have the same set of overloads, making them play together very nicely.
H.H. approach is a little bit different * date constructors build unchecked dates * date factories build checked dates What do you think of this separation? Best, Vicenet
On May 4, 2013, at 10:25 AM, "Vicente J. Botet Escriba"
Le 04/05/13 14:50, Rob Stewart a écrit :
On May 4, 2013, at 3:29 AM, "Vicente J. Botet Escriba"
wrote: Le 04/05/13 03:30, Rob Stewart a écrit :
On May 3, 2013, at 9:30 AM, "Vicente J. Botet Escriba"
wrote: // no date validity check parameters are in ymd orther. date(int y, int m, int d, ymd_t);
Note the last one has the no_check before to avoid ambiguity as all year, month and day are convertible to int.
I'm of course open to better names for no_check_t and ymd_t. That isn't nearly as good as my suggestion. First, your ymd_t overload doesn't actually enforce that order of arguments. This function is needed as there are people that are requesting a int based interface without named types. You can always use the other constructors.
People often want things that aren't good for them, so that's not a great argument. Still, my opinion isn't the only one that matters.
make_unvalidate_date(). s/unvalidated_t/unchecked_t/ would be better. I wonder if no_throw couldn't be adopted.
That could work.
If I understand you make_date would behave as the / factory and is useful for those that don't like the / factory syntax. make_unvalidate_date() would use the no_check_t overloads. Both would have the same set of of overload orders. I'm not entirely against the /-based factories, but they really don't offer an advantage over an ordinary make_date() function. The latter is also in keeping with make_shared(), make_unique(), and make_pair(). Furthermore, as I've noted in other posts, make_date() can be overloaded for unchecked, day of year, and weekday date construction use cases. One name to rule them all, so to speak. Agreed. I suspect the /-based syntax was proposed as it is already used on the date domain. IMO, is a _nice_ to have feature.
I realize that / is common in writing dates, but a great many natural notations map poorly to C++. Howard's notation seems too cute to me, but we each have different sensibilities.
I would prefer to let pending these factory discussion. We could come back once we agree on the date constructors. OK, but allow me to note that the date constructors and make_date() can have the same set of overloads, making them play together very nicely.
H.H. approach is a little bit different * date constructors build unchecked dates * date factories build checked dates
What do you think of this separation?
There's some value in it. I replied separately that the choice of syntax should not dictate whether checking occurs. However, it has just occurred to me that the make_date() I've been discussing is just another spelling of the constructor name unless there are different return types based upon the argument list. If there are different types, then make_date() provides a consistent interface. Otherwise, I'd be most interested in a full featured date class. That leaves the / factories as pure syntactic sugar. ___ Rob (Sent from my portable computation engine)
Le 04/05/13 21:20, Rob Stewart a écrit :
On May 4, 2013, at 10:25 AM, "Vicente J. Botet Escriba"
wrote: Le 04/05/13 14:50, Rob Stewart a écrit :
On May 4, 2013, at 3:29 AM, "Vicente J. Botet Escriba"
wrote: Le 04/05/13 03:30, Rob Stewart a écrit :
On May 3, 2013, at 9:30 AM, "Vicente J. Botet Escriba"
wrote: // no date validity check parameters are in ymd orther. date(int y, int m, int d, ymd_t);
Note the last one has the no_check before to avoid ambiguity as all year, month and day are convertible to int.
I'm of course open to better names for no_check_t and ymd_t. That isn't nearly as good as my suggestion. First, your ymd_t overload doesn't actually enforce that order of arguments. This function is needed as there are people that are requesting a int based interface without named types. You can always use the other constructors. People often want things that aren't good for them, so that's not a great argument. Still, my opinion isn't the only one that matters.
make_unvalidate_date(). s/unvalidated_t/unchecked_t/ would be better. I wonder if no_throw couldn't be adopted. That could work.
If I understand you make_date would behave as the / factory and is useful for those that don't like the / factory syntax. make_unvalidate_date() would use the no_check_t overloads. Both would have the same set of of overload orders. I'm not entirely against the /-based factories, but they really don't offer an advantage over an ordinary make_date() function. The latter is also in keeping with make_shared(), make_unique(), and make_pair(). Furthermore, as I've noted in other posts, make_date() can be overloaded for unchecked, day of year, and weekday date construction use cases. One name to rule them all, so to speak. Agreed. I suspect the /-based syntax was proposed as it is already used on the date domain. IMO, is a _nice_ to have feature. I realize that / is common in writing dates, but a great many natural notations map poorly to C++. Howard's notation seems too cute to me, but we each have different sensibilities.
I would prefer to let pending these factory discussion. We could come back once we agree on the date constructors. OK, but allow me to note that the date constructors and make_date() can have the same set of overloads, making them play together very nicely.
H.H. approach is a little bit different * date constructors build unchecked dates * date factories build checked dates
What do you think of this separation? There's some value in it. I replied separately that the choice of syntax should not dictate whether checking occurs.
However, it has just occurred to me that the make_date() I've been discussing is just another spelling of the constructor name unless there are different return types based upon the argument list. I expect there will be several classes. If there are different types, then make_date() provides a consistent interface. Otherwise, I'd be most interested in a full featured date class. That leaves the / factories as pure syntactic sugar.
What do you expect from a "full featured date class"? Would this correspond what I have called a Date concept? I have just extracted from my current interface some of the functions that we could have in a full featured date class. class date; date today(); class date { public: // construct/copy/destruct date(); // |year(0)/jan/1| date(year, month, day); date(year, month, day, no_check_t); // and all the usual combinations bool set_if_valid_date(year, month, day); day day() const; // or explict operator day(); the same for the other accessors. 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() explicit date(system_clock::time_point); operator system_clock::time_point() const; 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); date & operator+=(years); date & operator-=(years); // 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 &); }; Best, Vicente
Thanks. I'm curious about some of your semantics...
On May 4, 2013, at 4:15 PM, "Vicente J. Botet Escriba"
I have just extracted from my current interface some of the functions that we could have in a full featured date class.
class date;
date today();
In what timezone?
class date {
What is the range of validity? Proleptic gregorian or auto-coverting to julian? What is the internal representation?
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? Is the conversion from int to year implicit or explicit? Is there a conversion from year to int? And if so, explicit or implicit?
// and all the usual combinations bool set_if_valid_date(year, month, day);
Return true if was set?
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.
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?
explicit date(system_clock::time_point); operator system_clock::time_point() const;
Do the above two assume the UTC timezone?
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?
date & operator+=(years); date & operator-=(years);
What semantics do you use for year arithmetic?
// 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. Howard
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"
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.
class date;
date today(); In what timezone? UTC
class date { What is the range of validity? Proleptic gregorian or auto-coverting to julian? [year(-32767)/jan/1 thru year(32767)/dec/31]
Proleptic gregorian
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.
// 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
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()). Best, Vicente
On May 4, 2013, at 7:33 PM, "Vicente J. Botet Escriba"
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"
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
On 2013-05-05 01:33, Vicente J. Botet Escriba wrote:
Le 04/05/13 23:09, Howard Hinnant a écrit :
On May 4, 2013, at 4:15 PM, "Vicente J. Botet Escriba"
wrote: class date;
date today(); In what timezone? UTC I thought that `chrono::date` was an abstract representation of a date, and not at all connected to a timezone. And that in order to convert to and from a `chrono::system_clock` or any other clock an explicit timezone would be needed.
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.
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|. Same here, the date May 5th 2013 represent the time range [2013-05-05 00:00, 2013-05-05 24:00), but that time range is different for different
Exactly, we should discuss the Date concept, and not focus on the concrete *classes*. people around the world. It would be, in my humble opinion, better to keep `chrono::date` abstract, without any connection to a timezone. Same with `chrono::time` (or whatever it would be called), it could be the time 12:34:56, but that cannot be represented as an instant in time (i.e. `chrono::time_point`). Neither can the combined `chrono::date` and `chrono::time`, it is still without any connection to a timezone. Perhaps it's better to explain myself with code: chrono::date d = year(2013) / may / day(5); chrono::time t = hours(12) + minutes(34) + seconds(56); chrono::date_time dt(d, t); // Here `dt` should be the representation of May 5th 2013 at 12:34:56, but that in turn occurred at different instants for different people around the world. // In order to convert it to a `system_time` a timezone would be needed: chono::system_time st = dt.in_timezone(chrono::timezones::utc); // or perhaps `dt.in_utc()` for short. I believe this is one of the few ways to resolve the many ambiguities of dates and times once and for all. Below is a rant, but first a disclaimer: I like the Boost.DateTime library very much, it has helped me greatly. The Boost.DateTime library has some flaws, one of them is the ambiguity of the `boost::date_time::ptime` class regarding timezones. It can represent time in different timezones, and you never know which. It can be local or UTC, or whatever. It's easy to use it incorrectly, it even have two factory functions (`second_clock::universal_time` and `second_clock::local_time`) which both create a `ptime`, but in different timezones, and after it has been created you can never know which. It can easily be resolved, as described above, and the compiler will tell you if you try to do something wrong. Not even the old C `time_t` had this issue, you are always explicit when converting it to local time (`localtime`) or UTC time (`gmtime`), but here the `tm` struct was used for both. Let's not make the same mistakes once again with `chrono::date`. Best regards, Anders Dalvander
Anders Dalvander-3 wrote
On 2013-05-05 01:33, Vicente J. Botet Escriba wrote:
Le 04/05/13 23:09, Howard Hinnant a écrit :
On May 4, 2013, at 4:15 PM, "Vicente J. Botet Escriba" <
vicente.botet@
> wrote:
class date;
date today(); In what timezone? UTC I thought that `chrono::date` was an abstract representation of a date, and not at all connected to a timezone. And that in order to convert to and from a `chrono::system_clock` or any other clock an explicit timezone would be needed.
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.
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|. Same here, the date May 5th 2013 represent the time range [2013-05-05 00:00, 2013-05-05 24:00), but that time range is different for different
Exactly, we should discuss the Date concept, and not focus on the concrete *classes*. people around the world. It would be, in my humble opinion, better to keep `chrono::date` abstract, without any connection to a timezone.
Same with `chrono::time` (or whatever it would be called), it could be the time 12:34:56, but that cannot be represented as an instant in time (i.e. `chrono::time_point`). Neither can the combined `chrono::date` and `chrono::time`, it is still without any connection to a timezone. Perhaps it's better to explain myself with code:
chrono::date d = year(2013) / may / day(5); chrono::time t = hours(12) + minutes(34) + seconds(56); chrono::date_time dt(d, t); // Here `dt` should be the representation of May 5th 2013 at 12:34:56, but that in turn occurred at different instants for different people around the world. // In order to convert it to a `system_time` a timezone would be needed: chono::system_time st = dt.in_timezone(chrono::timezones::utc); // or perhaps `dt.in_utc()` for short.
I believe this is one of the few ways to resolve the many ambiguities of dates and times once and for all.
Below is a rant, but first a disclaimer: I like the Boost.DateTime library very much, it has helped me greatly. The Boost.DateTime library has some flaws, one of them is the ambiguity of the `boost::date_time::ptime` class regarding timezones. It can represent time in different timezones, and you never know which. It can be local or UTC, or whatever. It's easy to use it incorrectly, it even have two factory functions (`second_clock::universal_time` and `second_clock::local_time`) which both create a `ptime`, but in different timezones, and after it has been created you can never know which. It can easily be resolved, as described above, and the compiler will tell you if you try to do something wrong. Not even the old C `time_t` had this issue, you are always explicit when converting it to local time (`localtime`) or UTC time (`gmtime`), but here the `tm` struct was used for both.
Let's not make the same mistakes once again with `chrono::date`.
Best regards, Anders Dalvander
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
I support you completely. But I feel like we can't even if we wanted to. Timezones are separated at intervals of 30 minutes (is it not always so?). But our dates have precision in terms of days. That creates a dilemma as to how do we store this information. Say, a user enters a date and we store its timezone separately. Then, we always want to be able to convert it to UTC for general comparison. But what is that date in UTC timezone? It depends on the time at which the date was entered. But since our library is date and not datetime, we refused to record timepoints in precision below days. Comparisons etc work in UTC timezone by convention, but user does I/O in his/her local timezone. We have demonstrated that converting from one timezone to another is not possible with dates only. Thus, our date must be naive and be oblivious to timezones at all. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
Anders Dalvander-3 wrote
Same here, the date May 5th 2013 represent the time range [2013-05-05 00:00, 2013-05-05 24:00), but that time range is different for different people around the world. It would be, in my humble opinion, better to keep `chrono::date` abstract, without any connection to a timezone.
Same with `chrono::time` (or whatever it would be called), it could be the time 12:34:56, but that cannot be represented as an instant in time (i.e. `chrono::time_point`). Neither can the combined `chrono::date` and `chrono::time`, it is still without any connection to a timezone. Perhaps it's better to explain myself with code:
chrono::date d =ear(2013) / may / day(5); chrono::time t =ours(12) + minutes(34) + seconds(56); chrono::date_time dt(d, t); // Here `dt` should be the representation of May 5th 2013 at 12:34:56, but that in turn occurred at different instants for different people around the world. // In order to convert it to a `system_time` a timezone would be needed: chono::system_time st =t.in_timezone(chrono::timezones::utc); // or perhaps `dt.in_utc()` for short.
I believe this is one of the few ways to resolve the many ambiguities of dates and times once and for all.
Below is a rant, but first a disclaimer: I like the Boost.DateTime library very much, it has helped me greatly. The Boost.DateTime library has some flaws, one of them is the ambiguity of the `boost::date_time::ptime` class regarding timezones. It can represent time in different timezones, and you never know which. It can be local or UTC, or whatever. It's easy to use it incorrectly, it even have two factory functions (`second_clock::universal_time` and `second_clock::local_time`) which both create a `ptime`, but in different timezones, and after it has been created you can never know which. It can easily be resolved, as described above, and the compiler will tell you if you try to do something wrong. Not even the old C `time_t` had this issue, you are always explicit when converting it to local time (`localtime`) or UTC time (`gmtime`), but here the `tm` struct was used for both.
Let's not make the same mistakes once again with `chrono::date`. I support you completely. But I feel like we can't even if we wanted to. Timezones are separated at intervals of 30 minutes (is it not always so?). There are timezones at 15 minutes too. Chatham Island Daylight Time (CHADT), Chatham Island Standard Time (CHAST) and Nepal Time (NPT). Not
On 2013-05-05 17:37, Anurag Kalia wrote: perhaps the most common, but they should be supported non the less.
But our dates have precision in terms of days. That creates a dilemma as to how do we store this information. Still the precision of a day is too imprecise to store any timezones, even if we only had 1 hour timezones. Say, a user enters a date and we store its timezone separately. Then, we always want to be able to convert it to UTC for general comparison. But what is that date in UTC timezone? I think you are mixing two concepts: TimePoints (a.k.a. Instants) and Dates. It depends on the time at which the date was entered. But since our library is date and not datetime, we refused to record timepoints in precision below days.
Comparisons etc work in UTC timezone by convention, but user does I/O in his/her local timezone. We have demonstrated that converting from one timezone to another is not possible with dates only. Thus, our date must be naive and be oblivious to timezones at all. That is easy, enter date in an abstract `chrono::date`, convert i to an `chrono::system_clock::time_point` using an explicit timezone (your current one, that is). Then you can compare them using the comparison functions of `time_point`.
Best regards, Anders Dalvander
Timezones are separated at intervals of 30 minutes (is it not always so?).
There are timezones at 15 minutes too. Chatham Island Daylight Time (CHADT), Chatham Island Standard Time (CHAST) and Nepal Time (NPT). Not perhaps the most common, but they should be supported non the less.
Great, I didn't know that!
But our dates have precision in terms of days. That creates a dilemma as to how do we store this information. Still the precision of a day is too imprecise to store any timezones, even if we only had 1 hour timezones.
Say, a user enters a date and we store its timezone separately. Then, we always want to be able to convert it to UTC for general comparison. But what is that date in UTC timezone? I think you are mixing two concepts: TimePoints (a.k.a. Instants) and Dates.
It depends on the time at which the date was entered. But since our library is date and not datetime, we refused to record timepoints in precision below days.
Comparisons etc work in UTC timezone by convention, but user does I/O in his/her local timezone. We have demonstrated that converting from one timezone to another is not possible with dates only. Thus, our date must be naive and be oblivious to timezones at all. That is easy, enter date in an abstract `chrono::date`, convert i to an `chrono::system_clock::time_point` using an explicit timezone (your current one, that is). Then you can compare them using the comparison functions of `time_point`.
Best regards, Anders Dalvander
No, I am not. When we store a time, we have precision of seconds. When computers got too fast, we made it more and more precise so that we have a precision of nanoseconds now. A day then should be treated as a highly imprecise instant. In datetime library, every timepoint is converted into UTC timezone for direct comparison. 3.00 am in India is still earlier than 1.00 am in Greenwich, in the same day. So we subtract 5 hours 30 minutes from Indian time to get time in UTC: then we get that 3.00 am IST is actually 9.30 pm of previous day. Causality is thus not violated. But the implicit and important point is that 3.00 am IST is actually 3.00 am +/- 1 second, IST. And 1.00 am GMT is 1.00 am +/- 1 second, GMT. So, in any case, any instant in the given range of IST is less than any instant in the GMT. Thus, we can say with surity that the event happened at IST timepoint happened before than the event at GMT timepoint. But if we are talking about days, we record only till the day information. So, if I note date right now as 6 May 2013 IST, we can't verifiably convert it to UTC. If the time at which I recorded date (which I failed to note down) was 1.00 am, the date in UTC would be 5 May, 2013. If I record the same date at 6.00 am, date in UTC would be 6 May, 2013. Thus, there is an ambiguity due to the lack of precision of our dates. Now we can assume that the time at which date was recorded is noon, say. I make a timeline that illustrates my point: 2013-05-06T01:00:00+5:30 I sent an email from India. 2013-05-05T21:00:00Z My friend received his email. Converting to UTC, 2013-05-05T19:30:00Z I sent an email from India. 2013-05-05T21:00:00Z My friend received his email. Okay! Causality is not violated. Now, if we record only the date, 2013-05-06 I sent an email from India. 2013-05-05 My friend received his email. Wrong! Causality is violated! Now, if we assume that time is noted at noon and we have recorded timezones too, 2013-05-06T12:00:00+5:30 I sent an email from India. 2013-05-05T12:00:00Z My friend received his email. Converting the values to UTC, 2013-05-06T06:30:00Z I sent an email from India. 2013-05-05T12:00:00Z My friend received his email. Wrong! Causality is violated! Now people may think that maybe noon is the problem. We could choose midnight. But we can always construct examples against it too. The problem is that timezones inherently require a precision in at least 15 minutes, or to make it simpler, 1 minute. This is a domain where dates by themselves are completely unsuitable. If there is deep flaw in my reasoning, I don't know. But the talk of using or not using time zones because they are unsuitable for maintenance seems to miss a big point. I am quite sure that my analysis is correct. If there is one, I am really interested in that. And if there is not, then I would just like to channelize the energy of the whole ML away from time zones. Regards, Anurag. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
Le 05/05/13 20:36, Anurag Kalia a écrit :
This is a domain where dates by themselves are completely unsuitable. If there is deep flaw in my reasoning, I don't know. But the talk of using or not using time zones because they are unsuitable for maintenance seems to miss a big point. Anurag, if we were to include timezones in Boost.Chrono/Date we wil call it Boost.Chrono/DateTime ;-) And the precision of a date_time would be whatever we need it to be.
I'm not interested in this possible Boost.Chrono/DateTime. Best, Vicente
On 2013-05-05 13:41, Anders Dalvander wrote:
Same here, the date May 5th 2013 represent the time range [2013-05-05 00:00, 2013-05-05 24:00), but that time range is different for different people around the world. It would be, in my humble opinion, better to keep `chrono::date` abstract, without any connection to a timezone.
Same with `chrono::time` (or whatever it would be called), it could be the time 12:34:56, but that cannot be represented as an instant in time (i.e. `chrono::time_point`). Neither can the combined `chrono::date` and `chrono::time`, it is still without any connection to a timezone. Perhaps it's better to explain myself with code:
chrono::date d = year(2013) / may / day(5); chrono::time t = hours(12) + minutes(34) + seconds(56); chrono::date_time dt(d, t); // Here `dt` should be the representation of May 5th 2013 at 12:34:56, but that in turn occurred at different instants for different people around the world. // In order to convert it to a `system_time` a timezone would be needed: chono::system_time st = dt.in_timezone(chrono::timezones::utc); // or perhaps `dt.in_utc()` for short.
Sorry about that, should be: chrono::date d = year(2013) / may / day(5); chrono::time t = hours(12) + minutes(34) + seconds(56); chrono::date_time dt(d, t); // Here `dt` should be the representation of May 5th 2013 at 12:34:56, but that in turn occurred at different instants for different people around the world. // In order to convert it to a `chono::system_clock::time_point` a timezone would be needed: chono::system_clock::time_point instant = dt.in_timezone(chrono::timezones::utc); // or perhaps `dt.in_utc()` for short. Best regards, Anders Dalvander
On May 5, 2013, at 1:09 PM, Anders Dalvander
Sorry about that, should be:
chrono::date d = year(2013) / may / day(5); chrono::time t = hours(12) + minutes(34) + seconds(56); chrono::date_time dt(d, t); // Here `dt` should be the representation of May 5th 2013 at 12:34:56, but that in turn occurred at different instants for different people around the world. // In order to convert it to a `chono::system_clock::time_point` a timezone would be needed: chono::system_clock::time_point instant = dt.in_timezone(chrono::timezones::utc); // or perhaps `dt.in_utc()` for short.
Fwiw, I just ran this program fragment: day_point d = year(2013) / may / day(5); auto t = hours(13) + minutes(34) + seconds(30); auto dt = d + t; auto EDT = hours(-4); dt -= EDT; auto dt_now = system_clock::now(); auto diff = dt_now - dt; std::cout << duration_cast<seconds>(diff).count() << " seconds\n"; And it output: 1 seconds Howard
On May 5, 2013, at 1:37 PM, Howard Hinnant
On May 5, 2013, at 1:09 PM, Anders Dalvander
wrote: Sorry about that, should be:
chrono::date d = year(2013) / may / day(5); chrono::time t = hours(12) + minutes(34) + seconds(56); chrono::date_time dt(d, t); // Here `dt` should be the representation of May 5th 2013 at 12:34:56, but that in turn occurred at different instants for different people around the world. // In order to convert it to a `chono::system_clock::time_point` a timezone would be needed: chono::system_clock::time_point instant = dt.in_timezone(chrono::timezones::utc); // or perhaps `dt.in_utc()` for short.
Fwiw, I just ran this program fragment:
day_point d = year(2013) / may / day(5); auto t = hours(13) + minutes(34) + seconds(30); auto dt = d + t; auto EDT = hours(-4); dt -= EDT; auto dt_now = system_clock::now(); auto diff = dt_now - dt; std::cout << duration_cast<seconds>(diff).count() << " seconds\n";
And it output:
1 seconds
Btw, I'm getting better at this (speaking of manual dexterity more than coding):
day_point d = year(2013) / may / day(5);
auto t = hours(22) + minutes(55) + seconds(55);
auto dt = d + t;
auto EDT = -hours(4);
dt -= EDT;
auto dt_now = system_clock::now();
auto diff = dt_now - dt;
std::cout << duration_cast<milliseconds>(diff).count() << " ms\n";
Output:
318 ms
-------------------------
My point in this post is that we have an existing std::chrono date_time facility and it would be good to interoperate with it. Dissecting this code line by line:
day_point d = year(2013) / may / day(5);
d is a std::chrono::time_point with the same epoch as std::chrono::system_clock::time_point, but has a much coarser resolution (technically day_point::period): std::ratio<86400>.
Because d has the same epoch as system_clock::time_point, the two types can, by C++11 rules, have arithmetic together. You can subtract one from the other and it just works. The result is a std::chrono::duration with the precision of the common_type of the two precisions (typically the finer).
So, the above code is proposed. But much of the following code is simply existing C++11, showing interoperability with the proposed code with C++11.
auto t = hours(22) + minutes(55) + seconds(55);
Nothing new about the above code. This is just a std::chrono::duration with a resolution of seconds. It exists today.
auto dt = d + t;
The proposal is that d has type time_point
On 2013-05-06 05:29, Howard Hinnant wrote:
Btw, I'm getting better at this (speaking of manual dexterity more than coding):
day_point d = year(2013) / may / day(5); auto t = hours(22) + minutes(55) + seconds(55); auto dt = d + t; auto EDT = -hours(4); dt -= EDT; auto dt_now = system_clock::now(); auto diff = dt_now - dt; std::cout << duration_cast<milliseconds>(diff).count() << " ms\n";
Output:
318 ms
-------------------------
My point in this post is that we have an existing std::chrono date_time facility and it would be good to interoperate with it. Dissecting this code line by line:
day_point d = year(2013) / may / day(5);
d is a std::chrono::time_point with the same epoch as std::chrono::system_clock::time_point, but has a much coarser resolution (technically day_point::period): std::ratio<86400>.
Because d has the same epoch as system_clock::time_point, the two types can, by C++11 rules, have arithmetic together. You can subtract one from the other and it just works. The result is a std::chrono::duration with the precision of the common_type of the two precisions (typically the finer).
So, the above code is proposed. But much of the following code is simply existing C++11, showing interoperability with the proposed code with C++11.
auto t = hours(22) + minutes(55) + seconds(55);
Nothing new about the above code. This is just a std::chrono::duration with a resolution of seconds. It exists today.
auto dt = d + t;
The proposal is that d has type time_point
>>. If that is so, then it is, by C++11 rules, already legal to add such a time_point to a duration of seconds. The result is a time_point
>> I.e. the result is a time_point with the same epoch as system_clock. A rep capable of holding the union of values from both d and t, and a precision capable of holding both all values from d and all values from t. In English: dt is a count of seconds from the system_clock epoch ... all by C++11 specification. Nothing invented here except day_point in the first line.
auto EDT = -hours(4);
This is just C++11. A duration type. It could have any units: hours, minutes, seconds, whatever.
dt -= EDT;
The above statement is the one which troubles me. Before executing the
statement `dt` is supposedly in EDT, and after the statement has
executed `dt` is in UTC (or is it the other way around). Anyway `dt` is
of type `time_point
As dt is a timePoint with precision seconds, as long as the UTC offset does not have finer precision than seconds, this just works according to existing C++11 time_point arithmetic.
auto dt_now = system_clock::now();
The above line is straight out of C++11. Nothing new here.
auto diff = dt_now - dt;
Since dt_now and dt are both time_points based on system_clock, this is also straight out of C++11. Arithmetic between the two time_points is well defined by C++11, and results in a duration with a precision of the finer of the two time_points (typically that of system_clock::time_point, which for me has a precision of microseconds).
std::cout << duration_cast<milliseconds>(diff).count() << " ms\n";
For me this is just a simple C++11 cast of microseconds to milliseconds.
-------------------------
In summary: If the serial date type is nothing more than a time_point
>>, then we get a great deal of interoperability with the C++11 time_point/duration facilities for free. And with absolutely no loss in performance. We get at no cost serial date types and serial date_time types. And the only invention needed is the field types, and the interaction between the invented field types, and the already standardized serial types.
Yes, we get a great deal of interoperability for free, for sure, but I worry that all the implicit casting will be an even larger debt. Regards, Anders Dalvander
On May 7, 2013, at 5:32 PM, Anders Dalvander
On 2013-05-06 05:29, Howard Hinnant wrote:
Btw, I'm getting better at this (speaking of manual dexterity more than coding):
day_point d = year(2013) / may / day(5); auto t = hours(22) + minutes(55) + seconds(55); auto dt = d + t; auto EDT = -hours(4); dt -= EDT; auto dt_now = system_clock::now(); auto diff = dt_now - dt; std::cout << duration_cast<milliseconds>(diff).count() << " ms\n";
Output:
318 ms
-------------------------
My point in this post is that we have an existing std::chrono date_time facility and it would be good to interoperate with it. Dissecting this code line by line:
day_point d = year(2013) / may / day(5);
d is a std::chrono::time_point with the same epoch as std::chrono::system_clock::time_point, but has a much coarser resolution (technically day_point::period): std::ratio<86400>.
Because d has the same epoch as system_clock::time_point, the two types can, by C++11 rules, have arithmetic together. You can subtract one from the other and it just works. The result is a std::chrono::duration with the precision of the common_type of the two precisions (typically the finer).
So, the above code is proposed. But much of the following code is simply existing C++11, showing interoperability with the proposed code with C++11.
auto t = hours(22) + minutes(55) + seconds(55);
Nothing new about the above code. This is just a std::chrono::duration with a resolution of seconds. It exists today.
auto dt = d + t;
Technically this time_point is in UTC, and means 22::55::55 after midnight. However that obviously was not the intent of the code. However I could have written: auto dt = d + (t - EDT); And then the time_point would have no point in which it doesn't point to the desired time.
The proposal is that d has type time_point
>>. If that is so, then it is, by C++11 rules, already legal to add such a time_point to a duration of seconds. The result is a time_point
>> I.e. the result is a time_point with the same epoch as system_clock. A rep capable of holding the union of values from both d and t, and a precision capable of holding both all values from d and all values from t. In English: dt is a count of seconds from the system_clock epoch ... all by C++11 specification. Nothing invented here except day_point in the first line.
auto EDT = -hours(4);
This is just C++11. A duration type. It could have any units: hours, minutes, seconds, whatever.
dt -= EDT;
The above statement is the one which troubles me. Before executing the statement `dt` is supposedly in EDT, and after the statement has executed `dt` is in UTC (or is it the other way around). Anyway `dt` is of type `time_point
>>` as you wrote, but that type has a defined meaning. Each `time_point` is a single point in time, an instant. If we start to make exceptions to this rule, then we're back with the problems of `boost::date_time::ptime`. And what about the `system_clock::to_time_t` and `time_point::time_since_epoch` functions? Shouldn't it be possible to call them at any given time and not worry if someone has adjusted the time_point to be in the "correct" timezone?
This is one of the things that I hoped the chrono library would solve by requiring the user to explicit.
The C-library is this explicit, why shouldn't the chrono library be able to be explicit as well?
Perhaps it can be. The intent of the above snippet is to show that: 1. chrono::time_point has a critical role here. It is a serial type. When it has precision finer than a day, it is a serial date_time type. When it has precision of a day, it is a serial date_days type. And if time_points of different precision share a clock, you can do time_point arithmetic with them, even though they have different precisions. The result will be a time_point (or duration) with a precision that can represent the result with no loss of precision. 2. 1. is useful! We can use 1 to effortlessly handle timezone differences, even ridiculous timezone offsets in minutes or seconds. 3. There is a direct correspondence with std::system_clock. The above doesn't have to be the final or everyday syntax that the client uses. But (imho) it demonstrates a solid foundation which we should build upon. Howard
On May 4, 2013, at 4:15 PM, "Vicente J. Botet Escriba"
What do you expect from a "full featured date class"?
In context, I was just referring to the full complement of constructor overloads.
Would this correspond what I have called a Date concept?
Having a Date concept is likely s good thing.
I have just extracted from my current interface some of the functions that we could have in a full featured date class.
class date;
date today();
I'd expect to be able to influence the time zone.
class date { public: // construct/copy/destruct date(); // |year(0)/jan/1| date(year, month, day); date(year, month, day, no_check_t); // and all the usual combinations
I'm warming up to the checked/unchecked dichotomy between date and make_date(), though the latter can be used for multiple date types and all must be checked or unchecked alike.
bool set_if_valid_date(year, month, day);
This seems odd. I'd prefer set(), with an exception and, maybe, try_set(), returning bool.
day day() const; // or explict operator day(); the same for the other accessors.
I'd expect one or the other. day() is probably better, since day can offer an explicit, converting constructor to get the effect of the operator. That simplifies the date I/F a little.
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()
explicit date(system_clock::time_point); operator system_clock::time_point() const;
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); date & operator+=(years); date & operator-=(years);
// 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 &);
};
It seems pretty complete. I haven't given this as much thought as you and Howard, so I'll assume the rest is appropriate. ___ Rob (Sent from my portable computation engine)
Le 05/05/13 04:19, Rob Stewart a écrit :
On May 4, 2013, at 4:15 PM, "Vicente J. Botet Escriba"
wrote: I have just extracted from my current interface some of the functions that we could have in a full featured date class.
class date;
date today(); I'd expect to be able to influence the time zone. Boost.DateTime provides time zone management, but as far as I know it has been a nightmare to maintain it. If someone knows of some portable 3pp library that we could include in Boost helping on time zone management I could include it, but developing it is not on my priorities. Maybe someone wants to do it or mentor a GSoC for the next year.
The best I can do for the time been is to provide UTC and local timezones. This doesn't mean that the standard could not provide more.
class date { public: // construct/copy/destruct date(); // |year(0)/jan/1| date(year, month, day); date(year, month, day, no_check_t); // and all the usual combinations I'm warming up to the checked/unchecked dichotomy between date and make_date(), though the latter can be used for multiple date types and all must be checked or unchecked alike.
bool set_if_valid_date(year, month, day); This seems odd. I'd prefer set(), with an exception and, maybe, try_set(), returning bool. Yes, the name is not the best. try_set would be better.
day day() const; // or explict operator day(); the same for the other accessors. I'd expect one or the other. day() is probably better, since day can offer an explicit, converting constructor to get the effect of the operator. That simplifies the date I/F a little.
Let me show the usage: dt.day() or day(dt) What do you prefer? Couldn't both be provided? I have one possible non-member addition. I'm trying to see how to compare two dates of different date types. Should the user convert explicitly before comparing ? days_date d1; ymd_date d2; if (d1 == days_date(d2)) versus if (ymd_date(d1) == d2) or should the operator choose the conversion? if (d1 == d2) This is surely not too important as the user would use the same date type while comparing comparing dates. Best, Vicente
On May 5, 2013, at 4:36 AM, "Vicente J. Botet Escriba"
Le 05/05/13 04:19, Rob Stewart a écrit :
On May 4, 2013, at 4:15 PM, "Vicente J. Botet Escriba"
wrote: date today();
I'd expect to be able to influence the time zone. Boost.DateTime provides time zone management, but as far as I know it has been a nightmare to maintain it.
I wouldn't call it a nightmare, but tools are needed to convert from the published data to a format needed for use by a time zone class, if it doesn't do the parsing directly.
If someone knows of some portable 3pp library that we could include in Boost helping on time zone management I could include it, but developing it is not on my priorities.
Date support is not complete without it. You could define a time_zone class, put the appropriate interface on it, and the implement only UTC and local for now. That would leave room for later support of the rest of the needed things, like DST transitions and historical alterations.
Maybe someone wants to do it or mentor a GSoC for the next year.
That would be good.
day day() const; // or explict operator day(); the same for the other accessors. I'd expect one or the other. day() is probably better, since day can offer an explicit, converting constructor to get the effect of the operator. That simplifies the date I/F a little. Let me show the usage:
dt.day()
or
day(dt)
What do you prefer? Couldn't both be provided?
I was presuming both but using the converting constructor for the latter.
I have one possible non-member addition. I'm trying to see how to compare two dates of different date types. Should the user convert explicitly before comparing ?
days_date d1; ymd_date d2;
if (d1 == days_date(d2))
versus
if (ymd_date(d1) == d2)
or should the operator choose the conversion?
if (d1 == d2)
This is surely not too important as the user would use the same date type while comparing comparing dates.
Ideally, all date types should be directly comparable. That is, the operator should decide the best means of doing the comparison. This is also cleanly extensible to new date types and is the cleanest notation. ___ Rob (Sent from my portable computation engine)
On 2013-05-05 07:48, Rob Stewart wrote:
On May 5, 2013, at 4:36 AM, "Vicente J. Botet Escriba"
wrote: Le 05/05/13 04:19, Rob Stewart a écrit :
On May 4, 2013, at 4:15 PM, "Vicente J. Botet Escriba"
wrote: date today();
I'd expect to be able to influence the time zone. Boost.DateTime provides time zone management, but as far as I know it has been a nightmare to maintain it.
I wouldn't call it a nightmare, but tools are needed to convert from the published data to a format needed for use by a time zone class, if it doesn't do the parsing directly.
If someone knows of some portable 3pp library that we could include in Boost helping on time zone management I could include it, but developing it is not on my priorities.
Date support is not complete without it. You could define a time_zone class, put the appropriate interface on it, and the implement only UTC and local for now. That would leave room for later support of the rest of the needed things, like DST transitions and historical alterations.
Don't forget Boost.Locale also has date/time/timezone support. http://www.boost.org/doc/libs/1_53_0/libs/locale/doc/html/dates_times_timezo... The docs suggest that ICU is necessary and sufficient for correctly handling timezones. John Bytheway
Le 05/05/13 17:08, John Bytheway a écrit :
Don't forget Boost.Locale also has date/time/timezone support.
http://www.boost.org/doc/libs/1_53_0/libs/locale/doc/html/dates_times_timezo...
The docs suggest that ICU is necessary and sufficient for correctly handling timezones.
You are right. I've forgotten this. Thanks for the recall, Vicente
But might there not be cases where we have to store the output not to some output device, but to a string in memory only?
I don't imagine there would be much call for doing formatted output of many strings to a buffer. Even then, each date of interest must be cast into the I/O friendly form, so there's no room for optimizing that case, is there?
The fact is I really don't know much about I/O formatter functions to already pick a direction. So i can just say I will come back here when I have more knowledge of what you want to say. :-)
For the vast majority however, serial representation is good enough. After caching as you know, performance nears ymd in these scenarios too.
Caching suggests larger objects and, possibly lower locality of reference.
By caching, I meant to say retrieve y,m,d values explicitly as well as some other properties of the date.
That sounds like conversion to another date type.
Maybe logically, yes.
It is because date object is streamable. Thus, such construction is possible:
std::cout << date(4, 5, 2013, dmy);
where output comes out to be "2013-05-04". This seems a little clumsy to me.
If there is I/O support, I'd hope for other formats. Regardless, those in different regions will want the arguments ordered in their locale's order. You could require them in magnitude order -- YMD -- but that's less friendly and isn't checked by the compiler.
I was acquainted with locales a couple of days back only. So, forgive me if I neglect to take them into account. You are absolutely right.
Now, back to your named constructor idiom. Thanks! How true that you learn something new everyday! I liked it. It actually seems very suitable to my date class since we are constructing dates also in ordinal date notation (year, day_of_year) as well as week date notation (year, week_no, weekday).
Again, I have doubts still. First that I have never encountered it in standard library till now - the library that I use the most. If it exists somewhere in it or Boost, that would hearten me.
make_pair, make_shared are something like it. However, the reason you've not seen it used is that no standardized type has needed it.
Thanks for the examples. They were just what I needed.
Moreover, named constructor idiom allows construction of objects by the objects themselves. It has possibility to look ugly. Though, as I write, I think one could always make them first-class functions which are friends of date class.
date::ymd(2013, 5, 3) make_ymd(2013, 5, 3)
I don't think this is right, though:
ymd(2013, 5, 3)
I will keep it in mind.
Is the latter ok approach? My concerns also extend to performance of resulting code. But people on internet say that the call is almost always optimized away.
RVO should take care of things.
We can even make it explicit by giving a move constructor. Thanks Rob for input Anurag. -- View this message in context: http://boost.2283326.n4.nabble.com/gsoc-2013-draft-proposal-for-chrono-date-... Sent from the Boost - Dev mailing list archive at Nabble.com.
participants (9)
-
Anders Dalvander
-
Anurag Kalia
-
Anurag Kalia
-
Howard Hinnant
-
John Bytheway
-
Michael Marcin
-
Rob Stewart
-
Vicente Botet
-
Vicente J. Botet Escriba