Thread: [BUGS] BUG #14632: Plus and minus operators inconsistency with leap yearsand year intervals.
[BUGS] BUG #14632: Plus and minus operators inconsistency with leap yearsand year intervals.
The following bug has been logged on the website: Bug reference: 14632 Logged by: Pietro Pugni Email address: pietro.pugni@gmail.com PostgreSQL version: 9.6.2 Operating system: Ubuntu 16.04.2 LTS Description: The following query:SELECT '1912-02-29'::date + '10 years'::interval returns:'1922-02-28 00:00:00' while the reverse operation:SELECT '1922-02-28'::date - '10 years'::interval returns:'1912-02-28 00:00:00' instead of '1912-02-29 00:00:00'. -- Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-bugs
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
The following bug has been logged on the website:
Bug reference: 14632
Logged by: Pietro Pugni
Email address: pietro.pugni@gmail.com
PostgreSQL version: 9.6.2
Operating system: Ubuntu 16.04.2 LTS
Description:
The following query:
SELECT '1912-02-29'::date + '10 years'::interval
returns:
'1922-02-28 00:00:00'
while the reverse operation:
SELECT '1922-02-28'::date - '10 years'::interval
returns:
'1912-02-28 00:00:00'
instead of '1912-02-29 00:00:00'.
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency with leap years and year intervals.
Il giorno 26 apr 2017, alle ore 20:29, David G. Johnston <david.g.johnston@gmail.com> ha scritto:The following bug has been logged on the website:
Bug reference: 14632
Logged by: Pietro Pugni
Email address: pietro.pugni@gmail.com
PostgreSQL version: 9.6.2
Operating system: Ubuntu 16.04.2 LTS
Description:
The following query:
SELECT '1912-02-29'::date + '10 years'::interval
returns:
'1922-02-28 00:00:00'
while the reverse operation:
SELECT '1922-02-28'::date - '10 years'::interval
returns:
'1912-02-28 00:00:00'
instead of '1912-02-29 00:00:00'.What part of this do you consider to be a bug - and what should it do instead?David J.
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
Adding 10 years to 1912-02-29 returns 1922-02-29, as expected.I would like to apply the reverse operation. To do so, I subtract 10 years from 1922-02-29 but I obtain 1912-02-28, so the math is actually wrong.
assuming 1922 was a leap year, 1912 is NOT a leap year, so therefore there is no 1912-02-29, that is an invalid date.
-- john r pierce, recycling bits in santa cruz
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
On 4/26/2017 1:30 PM, Pietro Pugni wrote:Adding 10 years to 1912-02-29 returns 1922-02-29, as expected.I would like to apply the reverse operation. To do so, I subtract 10 years from 1922-02-29 but I obtain 1912-02-28, so the math is actually wrong.
assuming 1922 was a leap year, 1912 is NOT a leap year, so therefore there is no 1912-02-29, that is an invalid date.
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
The following bug has been logged on the website:
Bug reference: 14632
Logged by: Pietro Pugni
Email address: pietro.pugni@gmail.com
PostgreSQL version: 9.6.2
Operating system: Ubuntu 16.04.2 LTS
Description:
The following query:
SELECT '1912-02-29'::date + '10 years'::interval
returns:
'1922-02-28 00:00:00'
while the reverse operation:
SELECT '1922-02-28'::date - '10 years'::interval
returns:
'1912-02-28 00:00:00'
instead of '1912-02-29 00:00:00'.
?column?
---------------------
1912-02-29 00:00:00
(1 row)
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
On 4/26/2017 1:30 PM, Pietro Pugni wrote:Adding 10 years to 1912-02-29 returns 1922-02-29, as expected.I would like to apply the reverse operation. To do so, I subtract 10 years from 1922-02-29 but I obtain 1912-02-28, so the math is actually wrong.
assuming 1922 was a leap year, 1912 is NOT a leap year, so therefore there is no 1912-02-29, that is an invalid date.
ok, I got that backwards. 1912 is the leap year.
date arithmetic is not guaranteed to be associative or commutative due to the irregular units involved.
pierce=# SELECT date '1912-02-29';
date
------------
1912-02-29
(1 row)
pierce=# SELECT date '1922-02-29';
ERROR: date/time field value out of range: "1922-02-29"
LINE 1: SELECT date '1922-02-29';
^
pierce=# SELECT date '1912-02-29' - interval '10 years';
?column?
---------------------
1902-02-28 00:00:00
(1 row)
pierce=# SELECT date '1912-02-29' + interval '10 years';
?column?
---------------------
1922-02-28 00:00:00
(1 row)
as an even more extreme case...
pierce=# SELECT date '2017-04-30' - interval '2 months';
?column?
---------------------
2017-02-28 00:00:00
(1 row)
-- john r pierce, recycling bits in santa cruz
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
On Wed, Apr 26, 2017 at 8:23 PM, <pietro.pugni@gmail.com> wrote:The problem is that after the subtraction the date '1912-02-28' doesn't know it's supposed to be the last date of the month instead of the 28th, specifically.
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
I’ll try to reformulate better.Adding 10 years to 1912-02-29 returns 1922-02-29, as expected.I would like to apply the reverse operation. To do so, I subtract 10 years from 1922-02-29 but I obtain 1912-02-28, so the math is actually wrong.
The “logical” bug can be seen also by adding and subtracting the same quantity:SELECT '1912-02-29'::date - '10 years'::interval + '10 years'::intervalIt returns '1912-02-28 00:00:00' instead of '1912-02-29 00:00:00'.
It’s an issue because there’s no other way to obtain the original date (reversing the add operation).
Also:SELECT age('1922-02-28'::date, '1912-02-29'::date) = '10 years'::intervalreturns false, while:SELECT ('1922-02-29'::date + '10 years'::interval) = '1922-02-28'::datereturns true.The inconsistency (or bug) resides in the non unique meaning of the interval handling with respect of summing and subtracting the same quantity.
Again, the issue can be seen this way: adding the interval returned bySELECT age('1922-02-28'::date, '1912-02-29'::date) to the original date. So:SELECT '1912-02-29'::date + '9 years 11 mons 28 days'::intervalreturns:(c) 1922-02-26 00:00:00Here we loose 2 days even if we add the same interval used in (b)!
The bug basically consists of the vague meaning of “years” applied to leap years. It should be revised in order to be consistent and correct.
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency with leap years and year intervals.
Pietro Pugni <pietro.pugni@gmail.com> writes: > Adding 10 years to 1912-02-29 returns 1922-02-29, as expected. No, it doesn't, and it would be wrong to do so because there is no such date; 1922 wasn't a leap year. There are basically three things the addition operator could do here: throw an error, return 1922-02-28, or return 1922-03-01. The first choice seems rather unhelpful. The second choice is more in keeping with what we do for some other similar cases, such as regression=# select '2017-01-31'::date + '1 month'::interval; ?column? ---------------------2017-02-28 00:00:00 (1 row) regression=# select '2017-01-31'::date + '3 months'::interval; ?column? ---------------------2017-04-30 00:00:00 (1 row) Basically the thought is that the last day of the month is more likely to be the answer the user wants for such cases than the first day of the next month. If you want the less-fuzzy semantics of, say, "plus 30 days", you can have that too but you need to say it that way. The really short answer is that civil calendars were not invented by mathematicians and expecting them to obey mathematical laws is doomed to be an exercise in frustration. > The bug basically consists of the vague meaning of “years” applied to > leap years. It should be revised in order to be consistent and correct. As remarked somewhere in our documentation, you'd need to take that up with the Pope, not with us database hackers. We didn't invent the calendar rules. regards, tom lane -- Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-bugs
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
The problem is that after the subtraction the date '1912-02-28' doesn't know it's supposed to be the last date of the month instead of the 28th, specifically.
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency with leap years and year intervals.
Pietro Pugni <pietro.pugni@gmail.com> writes:Adding 10 years to 1912-02-29 returns 1922-02-29, as expected.
No, it doesn't, and it would be wrong to do so because there is no
such date; 1922 wasn't a leap year.
The bug basically consists of the vague meaning of “years” applied to
leap years. It should be revised in order to be consistent and correct.
As remarked somewhere in our documentation, you'd need to take that up
with the Pope, not with us database hackers. We didn't invent the
calendar rules.
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency with leap years and year intervals.
In theory we could simply die trying but given few complaints this behavior doesn't seem to bother many people if not actually please them because at least they can get a correct result even if it is munged a bit. Date interval math is fraught with problems.; you can bypass most of them by manipulating days instead.
The “logical” bug can be seen also by adding and subtracting the same quantity:SELECT '1912-02-29'::date - '10 years'::interval + '10 years'::intervalIt returns '1912-02-28 00:00:00' instead of '1912-02-29 00:00:00'.Expect 1912-02-28 is a correct response. The only reason you think the 29th comes into play here is because you remember that the starting point was the 29th. The system has no such memory.
It’s an issue because there’s no other way to obtain the original date (reversing the add operation).You are correct. Given the presence of leap years what you describe is a mathematical impossibility - not a bug.
Also:SELECT age('1922-02-28'::date, '1912-02-29'::date) = '10 years'::intervalreturns false, while:SELECT ('1922-02-29'::date + '10 years'::interval) = '1922-02-28'::datereturns true.The inconsistency (or bug) resides in the non unique meaning of the interval handling with respect of summing and subtracting the same quantity.It resides in the fact we apply date shifting after applying the interval in order to come up with a valid date. That such date shifting negates the commutative property is an unfortunate byproduct. To my knowledge there is no promise nor requirement for date arithmetic to be commutative. Or, more precisely, if you wish to use the commutative property here you must operate using days.
Again, the issue can be seen this way: adding the interval returned bySELECT age('1922-02-28'::date, '1912-02-29'::date) to the original date. So:SELECT '1912-02-29'::date + '9 years 11 mons 28 days'::intervalreturns:(c) 1922-02-26 00:00:00Here we loose 2 days even if we add the same interval used in (b)!Operate in days and you can do this just fine - but note you cannot just convert the interval to a number of days.SELECT '1922-02-28'::date - '1912-02-29'::date ; 3652SELECT '1922-02-28'::date - 3652 ; 1912-02-29This particular example leads me to suspect that some improvement in this area might be possible...The bug basically consists of the vague meaning of “years” applied to leap years. It should be revised in order to be consistent and correct.How?
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
This is inconsistent (and wrong) against the result provided by this query:select age('1922-02-28'::date, '1912-02-29'::date)age-------------------------9 years 11 mons 28 daysI expect it to return ’10 years’ instead.
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
Expect 1912-02-28 is a correct response. The only reason you think the 29th comes into play here is because you remember that the starting point was the 29th. The system has no such memory.And this is logically wrong because it leads to wrong results. I’m aware that time intervals are difficult to manage but more exactness is needed here: '10 years' must have the same meaning when added to a date and subtracted from it, otherwise it leads to wrong results.
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency with leap years and year intervals.
I don't know if you intended to supersize your response but it ended up that way in my mail client.
I'll agree that the age function has enough data available to it to return 10 years in this case.28 days, target month is February in a non-leap-year (1922), convert to 1 month. 11 months + 1 month = 12 months = 1 year. 9 years + 1 year = 10 years.But the provided answer is correct as well...
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
It’s an issue because there’s no other way to obtain the original date (reversing the add operation).You are correct. Given the presence of leap years what you describe is a mathematical impossibility - not a bug.It should be advised in the official docs (I can’t find any reference for this topic).
How?Probably considering leap dates as singularities and threat them by subtracting 1 day.For example, a possible workaround to properly calculate the interval between 1922-02-28 and 1922-02-29 is the following:SELECT age('1922-02-28'::date, '1912-02-29'::date - '1 day'::interval)age----------10 years
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
How?Probably considering leap dates as singularities and threat them by subtracting 1 day.For example, a possible workaround to properly calculate the interval between 1922-02-28 and 1922-02-29 is the following:SELECT age('1922-02-28'::date, '1912-02-29'::date - '1 day'::interval)age----------10 yearsAt first glance I would consider this a worthwhile path to explore; and it meshes well with the comment I made above (before reading this) about the functions are either ignoring or mapping the 29th to the 28th. Subtracting 1 day to leap-year dates effective does both. I'm sure there are other interactions involved here but if for someone willing to do the work this approach seems to have some merit.
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency with leap years and year intervals.
On Thu, Apr 27, 2017 at 1:01 AM, Pietro Pugni <pietro.pugni@gmail.com> wrote:Expect 1912-02-28 is a correct response. The only reason you think the 29th comes into play here is because you remember that the starting point was the 29th. The system has no such memory.And this is logically wrong because it leads to wrong results. I’m aware that time intervals are difficult to manage but more exactness is needed here: '10 years' must have the same meaning when added to a date and subtracted from it, otherwise it leads to wrong results.Your suggestion just moves the wrong results to another use case; see my response upthread. There is no objectively correct answer here, like you seem to think.
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency with leap years and year intervals.
>>>>> "Pietro" == Pietro Pugni <pietro.pugni@gmail.com> writes: Pietro> The whole point (for leap years) is: Pietro> - inconsistency (adding and subtracting the same quantity to aPietro> date should provide the date itself, butit doesn't); It should be obvious that this is mathematically impossible; a function from a larger set (the 366 days of a leap year) to a smaller one (the 365 days of a common year) cannot have an inverse. -- Andrew (irc:RhodiumToad) -- Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-bugs
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency withleap years and year intervals.
On Thu, Apr 27, 2017 at 1:01 AM, Pietro Pugni <pietro.pugni@gmail.com> wrote:Expect 1912-02-28 is a correct response. The only reason you think the 29th comes into play here is because you remember that the starting point was the 29th. The system has no such memory.And this is logically wrong because it leads to wrong results. I’m aware that time intervals are difficult to manage but more exactness is needed here: '10 years' must have the same meaning when added to a date and subtracted from it, otherwise it leads to wrong results.Your suggestion just moves the wrong results to another use case; see my response upthread. There is no objectively correct answer here, like you seem to think.I just found this query clearly shows the underlying algorithm is somewhat wrong:(I)postgres=# select age('2017-01-30'::date, '1912-02-29'::date) + '1912-02-29'::date;?column?---------------------2017-01-30 00:00:00(II)postgres=# select age('2017-02-01'::date, '1912-02-29'::date) + '1912-02-29'::date;?column?---------------------2017-01-30 00:00:00Query (II) should return 2017-02-01 instead of 2017-01-30 and the two queries should return different results.More generally, ...I understand why this happens (this has been discussed previously) but clearly there’s something wrong..
(dateD + intervalA) + intervalB
dateD + (intervalA + intervalB)
SELECT
'2017-03-31'::date + '1 month'::interval + '1 month'::interval,
'2017-03-31'::date + ('1 month'::interval + '1 month'::interval) ;
Not all months have the same number of days and there is not a way to fix that.
Pantelis
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency with leap years and year intervals.
Il giorno 27 apr 2017, alle ore 21:55, Pedro Gimeno <pgsql-004@personal.formauri.es> ha scritto:
I would like to better understand your expectations regarding your proposed fix.
1. What do you propose that this specific query should return?
SELECT '1912-02-28'::date + interval '10 years’;
2. What do you propose that this specific query should return?
SELECT (whatever your answer to question 1 is)::date - interval '10 years’;
3. What do you propose that this specific query should return?
SELECT '1912-02-29'::date + interval '10 years’;
4. What do you propose that this specific query should return?
SELECT (whatever your answer to question 3 is)::date - interval '10 years’;
5. What do you propose that this specific query should return?
SELECT '1912-03-01'::date + interval '10 years’;
6. What do you propose that this specific query should return?
SELECT (whatever your answer to question 5 is)::date - interval '10 years’;
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency with leap years and year intervals.
Il giorno 27 apr 2017, alle ore 14:31, Pantelis Theodosiou <ypercube@gmail.com> ha scritto:You seem to think that expressions similar to these should yield the same results:dateD + intervalA + intervalB
(dateD + intervalA) + intervalB
dateD + (intervalA + intervalB)But they don't and they couldn't, as many others have mentioned already.Expressions 1 and 2 are equivalent but the 3rd is not.And we don't even need leap years to find such "wrong" results. We have leap seconds (which are rare) and leap months (which are pretty common although we don't call them leap months). Just try
SELECT
'2017-03-31'::date + '1 month'::interval + '1 month'::interval,
'2017-03-31'::date + ('1 month'::interval + '1 month'::interval) ;
Not all months have the same number of days and there is not a way to fix that.It's just not possible to squeeze 31 days in a 30-days month. What should '2017-03-31'::date + '1 month'::interval result and what should '2017-03-30'::date + '1 month'::interval result?Regards,
Pantelis
Re: [BUGS] BUG #14632: Plus and minus operators inconsistency with leap years and year intervals.
Probably it should be worth it to implement a sort of age2(timestamp, timestamp) function that returns ages accounting for leap years, months, days and seconds, without changing the actual implementation of age(timestamp, timestamp).
[BUGS] Re: BUG #14632: Plus and minus operators inconsistency with leapyears and year intervals.
Pietro Pugni wrote, On 2017-04-28 10:28: > I’m aware that 2. and 4. can’t return different result because they are the same queries, but in that cases the resultcorrectness depends on the context. You can use 10*interval '31556952 seconds' to add or subtract 10 years with consistent results. 31556952 seconds is the averagelength of the year in the Gregorian calendar. To use this approach, you need to start using timestamp instead of date,and avoid using timestamp with time zone on a time zone that uses daylight savings. I don't think PostgreSQL needs to do anything about this issue. -- Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-bugs