On Tue, 2020-05-12 at 18:09 -0700, David G. Johnston wrote:
> Redirecting to -hackers for visibility.  I feel there needs to be something 
> done here, even if just documentation (a bullet in the usage notes section - 
> and a code comment update for the macro)
> pointing this out and not changing any behavior.
> 
> David J.
> 
> On Wed, May 6, 2020 at 8:12 PM David G. Johnston <david.g.johns...@gmail.com> 
> wrote:
> > ‪On Wed, May 6, 2020 at 6:31 PM ‫دار الآثار للنشر والتوزيع-صنعاء Dar 
> > Alathar-Yemen‬‎ <dar_alat...@hotmail.com> wrote:‬
> > > Any one suppose that these functions return the same:
> > > make_date(-1,1,1)
> > > to_date('-1-01-01','yyyy-mm-dd')
> > > 
> > > But make_date will give 0001-01-01 BC
> > > 
> > > And to_date will give 0002-01-01 BC
> > > 
> > > 
> > > 
> > > 
> > 
> > Interesting...and a fair point.
> > 
> > What seems to be happening here is that to_date is trying to be helpful by 
> > doing:
> > 
> > select to_date('0000','YYYY'); // 0001-01-01 BC
> > 
> > It does this seemingly by subtracting one from the year, making it 
> > positive, then (I infer) appending "BC" to the result.  Thus for the year 
> > "-1" it yields "0002-01-01 BC"
> > 
> > make_date just chooses to reject the year 0 and treat the negative as an 
> > alternative to specifying BC
> > 
> > There seems to be zero tests for to_date involving negative years, and the 
> > documentation doesn't talk of them.
> > 
> > I'll let the -hackers speak up as to how they want to go about handling 
> > to_date (research how it behaves in the other database it tries to emulate 
> > and either document or possibly change the
> > behavior in v14) but do suggest that a simple explicit description of how 
> > to_date works in the presence of negative years be back-patched.  A bullet 
> > in the usage notes section probably suffices:
> > 
> > "If a YYYY format string captures a negative year, or 0000, it will treat 
> > it as a BC year after decreasing the value by one.  So 0000 maps to 1 BC 
> > and -1 maps to 2 BC and so on."
> > 
> > So, no, make_date and to_date do not agree on this point; and they do not 
> > have to.  There is no way to specify "BC" in make_date function so using 
> > negative there makes sense.  You can specify BC
> > in the input string for to_date and indeed that is the only supported 
> > (documented) way to do so.
> > 
> > 
> 
>  
> [and the next email]
>  
> > Specifically:
> > 
> > https://github.com/postgres/postgres/blob/fb544735f11480a697fcab791c058adc166be1fa/src/backend/utils/adt/formatting.c#L236
> > 
> > /*
> >  * There is no 0 AD.  Years go from 1 BC to 1 AD, so we make it
> >  * positive and map year == -1 to year zero, and shift all negative
> >  * years up one.  For interval years, we just return the year.
> >  */
> > #define ADJUST_YEAR(year, is_interval) ((is_interval) ? (year) : ((year) <= 
> > 0 ? -((year) - 1) : (year)))
> > 
> > The code comment took me a bit to process - seems like the following would 
> > be better (if its right - I don't know why interval is a pure no-op while 
> > non-interval normalizes to a positive integer).
> > 
> > Years go from 1 BC to 1 AD, so we adjust the year zero, and all negative 
> > years, by shifting them away one year,  We then return the positive value 
> > of the result because the caller tracks the BC/AD
> > aspect of the year separately and only deals with positive year values 
> > coming out of this macro.  Intervals denote the distance away from 0 a year 
> > is so we can simply take the supplied value and
> > return it.  Interval processing code expects a negative result for 
> > intervals going into BC.
> > 
> > David J.

Since "to_date" is an Oracle compatibility function, here is what Oracle 18.4 
has to say to that:

SQL> SELECT to_date('0000', 'YYYY') FROM dual;
SELECT to_date('0000', 'YYYY') FROM dual
               *
ERROR at line 1:
ORA-01841: (full) year must be between -4713 and +9999, and not be 0


SQL> SELECT to_date('-0001', 'YYYY') FROM dual;
SELECT to_date('-0001', 'YYYY') FROM dual
               *
ERROR at line 1:
ORA-01841: (full) year must be between -4713 and +9999, and not be 0


SQL> SELECT to_date('-0001', 'SYYYY') FROM dual;

TO_DATE('-0001','SYYYY
----------------------
0001-05-01 00:00:00 BC

Yours,
Laurenz Albe



Reply via email to