Thread: BUG #13670: DST discrepancy between age() and subtraction for timestamptz arguments
BUG #13670: DST discrepancy between age() and subtraction for timestamptz arguments
From
jpruitt@doozer.com
Date:
The following bug has been logged on the website: Bug reference: 13670 Logged by: John Pruitt Email address: jpruitt@doozer.com PostgreSQL version: 9.4.4 Operating system: x86_64-apple-darwin Description: We are seeing a discrepancy between what is returned by the age(timestamptz, timestamptz) function versus using the subtraction operator (timestamptz - timestamptz) on the DST transition days. It appears that the subtraction operator gives the correct answers, while the age function does not. /* short day - 2:00 is skipped - 1 hour is correct */ select '2015-03-08 03:00'::timestamptz - '2015-03-08 01:00'::timestamptz -- 1 hour , age('2015-03-08 03:00'::timestamptz, '2015-03-08 01:00'::timestamptz) -- 2 hours ; /* long day - 1:00 repeats - 3 hours is correct */ select '2014-11-02 02:00'::timestamptz - '2014-11-02 00:00'::timestamptz -- 3 hours , age('2014-11-02 02:00'::timestamptz, '2014-11-02 00:00'::timestamptz) -- 2 hours ; The timezone setting in the database is 'America/Chicago' which is the same setting in the operating system. show timezone; -- 'America/Chicago' version(): version "PostgreSQL 9.4.4 on x86_64-apple-darwin, compiled by i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00), 64-bit"
Re: BUG #13670: DST discrepancy between age() and subtraction for timestamptz arguments
From
Haribabu Kommi
Date:
On Fri, Oct 9, 2015 at 9:34 AM, <jpruitt@doozer.com> wrote: > The following bug has been logged on the website: > > Bug reference: 13670 > Logged by: John Pruitt > Email address: jpruitt@doozer.com > PostgreSQL version: 9.4.4 > Operating system: x86_64-apple-darwin > Description: > > We are seeing a discrepancy between what is returned by the age(timestamptz, > timestamptz) function versus using the subtraction operator (timestamptz - > timestamptz) on the DST transition days. It appears that the subtraction > operator gives the correct answers, while the age function does not. > > /* short day - 2:00 is skipped - 1 hour is correct */ > select > '2015-03-08 03:00'::timestamptz - '2015-03-08 01:00'::timestamptz -- 1 > hour > , age('2015-03-08 03:00'::timestamptz, '2015-03-08 01:00'::timestamptz) -- 2 > hours > ; > > /* long day - 1:00 repeats - 3 hours is correct */ > select > '2014-11-02 02:00'::timestamptz - '2014-11-02 00:00'::timestamptz -- 3 > hours > , age('2014-11-02 02:00'::timestamptz, '2014-11-02 00:00'::timestamptz) -- 2 > hours > ; From the PostgreSQL documentation it shows that the age function works with timestamp agruements instead of timestamptz. So the behavior is correct as it is ignoring the timezone effect. Because of default cast functions for timestamp and timestamptz, the function can accept any type of argument and works as per timestamp datatype described in the documentation. Refer: Date/Time Functions http://www.postgresql.org/docs/9.0/static/functions-datetime.html Because of the above reason, it works similar like as follows. select '2014-11-02 02:00'::timestamp - '2014-11-02 00:00'::timestamp , age('2014-11-02 02:00'::timestamp, '2014-11-02 00:00'::timestamp) From the code point of view, it just accepts the data timestamptz and just ignores the timezone in the calculation according to the documentation. Regards, Hari Babu Fujitsu Australia
Re: BUG #13670: DST discrepancy between age() and subtraction for timestamptz arguments
From
John Pruitt
Date:
On further inspection, an age function that explicitly takes timestamptz arguments does in fact exist. select ns.nspname || '.' || proname || '(' || oidvectortypes(proargtypes) || ')' from pg_proc inner join pg_namespace ns on (pg_proc.pronamespace = ns.oid) where proname = 'age' ; pg_catalog.age(xid) pg_catalog.age(timestamp without time zone) pg_catalog.age(timestamp with time zone) pg_catalog.age(timestamp without time zone, timestamp without time zone) pg_catalog.age(timestamp with time zone, timestamp with time zone) Shouldn't the versions that take timestamp with time zone honor the time zone? Thank you. John Pruitt Delivery Director Doozer Software, Inc. jpruitt@doozer.com work 205-413-8313 cell 205-746-7464 On Fri, Oct 9, 2015 at 2:53 AM, Haribabu Kommi <kommi.haribabu@gmail.com> wrote: > > On Fri, Oct 9, 2015 at 9:34 AM, <jpruitt@doozer.com> wrote: > > The following bug has been logged on the website: > > > > Bug reference: 13670 > > Logged by: John Pruitt > > Email address: jpruitt@doozer.com > > PostgreSQL version: 9.4.4 > > Operating system: x86_64-apple-darwin > > Description: > > > > We are seeing a discrepancy between what is returned by the > age(timestamptz, > > timestamptz) function versus using the subtraction operator (timestamptz > - > > timestamptz) on the DST transition days. It appears that the subtraction > > operator gives the correct answers, while the age function does not. > > > > /* short day - 2:00 is skipped - 1 hour is correct */ > > select > > '2015-03-08 03:00'::timestamptz - '2015-03-08 01:00'::timestamptz -- 1 > > hour > > , age('2015-03-08 03:00'::timestamptz, '2015-03-08 01:00'::timestamptz) > -- 2 > > hours > > ; > > > > /* long day - 1:00 repeats - 3 hours is correct */ > > select > > '2014-11-02 02:00'::timestamptz - '2014-11-02 00:00'::timestamptz -- 3 > > hours > > , age('2014-11-02 02:00'::timestamptz, '2014-11-02 00:00'::timestamptz) > -- 2 > > hours > > ; > > From the PostgreSQL documentation it shows that the age function works > with timestamp > agruements instead of timestamptz. So the behavior is correct as it is > ignoring the timezone > effect. > > Because of default cast functions for timestamp and timestamptz, the > function can accept > any type of argument and works as per timestamp datatype described in the > documentation. > > Refer: Date/Time Functions > http://www.postgresql.org/docs/9.0/static/functions-datetime.html > > Because of the above reason, it works similar like as follows. > > select > '2014-11-02 02:00'::timestamp - '2014-11-02 00:00'::timestamp > , age('2014-11-02 02:00'::timestamp, '2014-11-02 00:00'::timestamp) > > > From the code point of view, it just accepts the data timestamptz and just > ignores the > timezone in the calculation according to the documentation. > > Regards, > Hari Babu > Fujitsu Australia >
Re: BUG #13670: DST discrepancy between age() and subtraction for timestamptz arguments
From
John Pruitt
Date:
Firstly, thank your for the response and clarification. I'd like to suggest that the documentation you referred to be clarified. The description at the top of the page says: All the functions and operators described below that take time or timestamp inputs > actually come in two variants: one that takes time with time zone or time= stamp > with time zone, and one that takes time without time zone or timestamp > without time zone. For brevity, these variants are not shown separately. > Also, the + and * operators come in commutative pairs (for example both > date + integer and integer + date); we show only one of each such pair. Unless I'm interpreting this incorrectly, this does not appear to be true for the age function. Are there other functions and operators on the page for which the overloaded variants also do not exist? Thanks again,=E2=80=8B John Pruitt Delivery Director Doozer Software, Inc. jpruitt@doozer.com work 205-413-8313 cell 205-746-7464 On Fri, Oct 9, 2015 at 2:53 AM, Haribabu Kommi <kommi.haribabu@gmail.com> wrote: > > On Fri, Oct 9, 2015 at 9:34 AM, <jpruitt@doozer.com> wrote: > > The following bug has been logged on the website: > > > > Bug reference: 13670 > > Logged by: John Pruitt > > Email address: jpruitt@doozer.com > > PostgreSQL version: 9.4.4 > > Operating system: x86_64-apple-darwin > > Description: > > > > We are seeing a discrepancy between what is returned by the > age(timestamptz, > > timestamptz) function versus using the subtraction operator (timestampt= z > - > > timestamptz) on the DST transition days. It appears that the subtractio= n > > operator gives the correct answers, while the age function does not. > > > > /* short day - 2:00 is skipped - 1 hour is correct */ > > select > > '2015-03-08 03:00'::timestamptz - '2015-03-08 01:00'::timestamptz -- = 1 > > hour > > , age('2015-03-08 03:00'::timestamptz, '2015-03-08 01:00'::timestamptz) > -- 2 > > hours > > ; > > > > /* long day - 1:00 repeats - 3 hours is correct */ > > select > > '2014-11-02 02:00'::timestamptz - '2014-11-02 00:00'::timestamptz -- = 3 > > hours > > , age('2014-11-02 02:00'::timestamptz, '2014-11-02 00:00'::timestamptz) > -- 2 > > hours > > ; > > From the PostgreSQL documentation it shows that the age function works > with timestamp > agruements instead of timestamptz. So the behavior is correct as it is > ignoring the timezone > effect. > > Because of default cast functions for timestamp and timestamptz, the > function can accept > any type of argument and works as per timestamp datatype described in the > documentation. > > Refer: Date/Time Functions > http://www.postgresql.org/docs/9.0/static/functions-datetime.html > > Because of the above reason, it works similar like as follows. > > select > '2014-11-02 02:00'::timestamp - '2014-11-02 00:00'::timestamp > , age('2014-11-02 02:00'::timestamp, '2014-11-02 00:00'::timestamp) > > > From the code point of view, it just accepts the data timestamptz and jus= t > ignores the > timezone in the calculation according to the documentation. > > Regards, > Hari Babu > Fujitsu Australia >
Re: BUG #13670: DST discrepancy between age() and subtraction for timestamptz arguments
From
Haribabu Kommi
Date:
On Sat, Oct 10, 2015 at 3:01 AM, John Pruitt <jpruitt@doozer.com> wrote: > On further inspection, an age function that explicitly takes timestamptz > arguments does in fact exist. > > select ns.nspname || '.' || proname || '(' || oidvectortypes(proargtypes) || > ')' > from pg_proc > inner join pg_namespace ns on (pg_proc.pronamespace = ns.oid) > where proname = 'age' > ; > > pg_catalog.age(xid) > pg_catalog.age(timestamp without time zone) > pg_catalog.age(timestamp with time zone) > pg_catalog.age(timestamp without time zone, timestamp without time zone) > pg_catalog.age(timestamp with time zone, timestamp with time zone) > > Shouldn't the versions that take timestamp with time zone honor the time > zone? Thanks for providing more details and your analysis. But in function timestamptz_age which accepts timestamptz as arguments has the following comment in the code. /* * Note: we deliberately ignore any difference between tz1 and tz2. */ The following mail provides the details of timezone ignorance in age function. http://www.postgresql.org/message-id/8907.1101918113@sss.pgh.pa.us Because of this reason, the age function works similar to timestamp even if the given input is timestamptz. Regards, Hari Babu Fujitsu Australia
Re: BUG #13670: DST discrepancy between age() and subtraction for timestamptz arguments
From
John Pruitt
Date:
Okay, I'll acknowledge that the issue has been discussed before, however I fail to see how the issue can be considered resolved. The function takes time zone aware arguments, ignores that input, and returns plainly incorrect answers - the very definition of a bug. If you use the age function in any kind of calculation, you'll have problems. If you use it in calculations for metered billing (as I was), you'll over charge or under charge your customers. At the very minimum, the documentation should have a warning in big bold red letters describing this deficiency. Does anyone know if any of the other date/time functions exhibit similar behavior? John Pruitt Delivery Director Doozer Software, Inc. On Fri, Oct 9, 2015 at 3:35 PM, Haribabu Kommi <kommi.haribabu@gmail.com> wrote: > > > On Sat, Oct 10, 2015 at 3:01 AM, John Pruitt <jpruitt@doozer.com> wrote: > > On further inspection, an age function that explicitly takes timestamptz > > arguments does in fact exist. > > > > select ns.nspname || '.' || proname || '(' || > oidvectortypes(proargtypes) || > > ')' > > from pg_proc > > inner join pg_namespace ns on (pg_proc.pronamespace = ns.oid) > > where proname = 'age' > > ; > > > > pg_catalog.age(xid) > > pg_catalog.age(timestamp without time zone) > > pg_catalog.age(timestamp with time zone) > > pg_catalog.age(timestamp without time zone, timestamp without time zone) > > pg_catalog.age(timestamp with time zone, timestamp with time zone) > > > > Shouldn't the versions that take timestamp with time zone honor the time > > zone? > > Thanks for providing more details and your analysis. > But in function timestamptz_age which accepts timestamptz as arguments has > the following comment in the code. > > /* > * Note: we deliberately ignore any difference between tz1 and tz2. > */ > > The following mail provides the details of timezone ignorance in age > function. > http://www.postgresql.org/message-id/8907.1101918113@sss.pgh.pa.us > > Because of this reason, the age function works similar to timestamp even > if the given input is timestamptz. > > Regards, > Hari Babu > Fujitsu Australia >
Re: BUG #13670: DST discrepancy between age() and subtraction for timestamptz arguments
From
Haribabu Kommi
Date:
On Tue, Oct 13, 2015 at 1:50 AM, John Pruitt <jpruitt@doozer.com> wrote: > Okay, I'll acknowledge that the issue has been discussed before, however I > fail to see how the issue can be considered resolved. The function takes > time zone aware arguments, ignores that input, and returns plainly incorrect > answers - the very definition of a bug. I also feel like that it is a bug. Here I attached a patch that corrects the problem. The patch just adds the difference in DST to the hours of difference between the two timestamp. The drawback in this approach is that, it produces the result as 24:00:00 instead of 1 day. Any better approach to handle this problem? Regards, Hari Babu Fujitsu Australia
Attachment
Re: BUG #13670: DST discrepancy between age() and subtraction for timestamptz arguments
From
Tom Lane
Date:
Haribabu Kommi <kommi.haribabu@gmail.com> writes: > On Tue, Oct 13, 2015 at 1:50 AM, John Pruitt <jpruitt@doozer.com> wrote: >> Okay, I'll acknowledge that the issue has been discussed before, however I >> fail to see how the issue can be considered resolved. The function takes >> time zone aware arguments, ignores that input, and returns plainly incorrect >> answers - the very definition of a bug. > I also feel like that it is a bug. Here I attached a patch that > corrects the problem. I think you both are fundamentally missing the point. The age() function exists to provide a "symbolic" difference between two timestamps. In its intended use-case, the difference between midnight on March 1 2015 and midnight on April 1 2015 is one month. Not one month plus or minus an hour. If you want the other behavior, why aren't you using plain timestamp subtraction? regression=# select age('1 april 2015'::timestamptz, '1 march 2015'::timestamptz); age ------- 1 mon (1 row) regression=# select '1 april 2015'::timestamptz - '1 march 2015'::timestamptz; ?column? ------------------ 30 days 23:00:00 (1 row) (This is based on US DST rules, you might get different results in other timezones.) Even discounting any thought of backwards compatibility, it does not seem to me that erasing the distinction between these behaviors is a good thing. People might legitimately want either one. Another point worth considering is this: regression=# select '1 march 2015'::timestamptz + age('1 april 2015'::timestamptz, '1 march 2015'::timestamptz); ?column? ------------------------ 2015-04-01 00:00:00-04 (1 row) While I'm not sure that "X + age(Y, X) = Y" holds universally, it does hold in this example, and the proposed patch would break that. Having said that, I notice that the seemingly even more obvious identity "X + (Y - X) = Y" doesn't work in this case. Maybe we should do something about that, or maybe not. My point is mainly that there are a *lot* of moving parts in this area, as well as a considerable amount of backwards-compatibility history that we must not take lightly. It is well to remember also that civil time and DST laws were written by politicians who have never heard of mathematical consistency. regards, tom lane
Re: BUG #13670: DST discrepancy between age() and subtraction for timestamptz arguments
From
John Pruitt
Date:
> > I think you both are fundamentally missing the point. Yes, I clearly missed it. I didn't understand what was meant by "symbolic". Maybe the docs could be clarified a bit; the words "imprecise" and "display purposes" come to mind. If that is indeed the purpose of age(), then I do agree that it is useful to have both it and the subtraction operator. Thank you! John Pruitt Delivery Director Doozer Software, Inc. On Mon, Oct 12, 2015 at 10:49 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote: > Haribabu Kommi <kommi.haribabu@gmail.com> writes: > > On Tue, Oct 13, 2015 at 1:50 AM, John Pruitt <jpruitt@doozer.com> wrote: > >> Okay, I'll acknowledge that the issue has been discussed before, > however I > >> fail to see how the issue can be considered resolved. The function takes > >> time zone aware arguments, ignores that input, and returns plainly > incorrect > >> answers - the very definition of a bug. > > > I also feel like that it is a bug. Here I attached a patch that > > corrects the problem. > > I think you both are fundamentally missing the point. > > The age() function exists to provide a "symbolic" difference between two > timestamps. In its intended use-case, the difference between midnight > on March 1 2015 and midnight on April 1 2015 is one month. Not one month > plus or minus an hour. > > If you want the other behavior, why aren't you using plain timestamp > subtraction? > > regression=# select age('1 april 2015'::timestamptz, '1 march > 2015'::timestamptz); > age > ------- > 1 mon > (1 row) > > regression=# select '1 april 2015'::timestamptz - '1 march > 2015'::timestamptz; > ?column? > ------------------ > 30 days 23:00:00 > (1 row) > > (This is based on US DST rules, you might get different results in other > timezones.) > > Even discounting any thought of backwards compatibility, it does not seem > to me that erasing the distinction between these behaviors is a good > thing. People might legitimately want either one. > > Another point worth considering is this: > > regression=# select '1 march 2015'::timestamptz + age('1 april > 2015'::timestamptz, '1 march 2015'::timestamptz); > ?column? > ------------------------ > 2015-04-01 00:00:00-04 > (1 row) > > While I'm not sure that "X + age(Y, X) = Y" holds universally, it does > hold in this example, and the proposed patch would break that. > > Having said that, I notice that the seemingly even more obvious > identity "X + (Y - X) = Y" doesn't work in this case. Maybe we should do > something about that, or maybe not. My point is mainly that there are > a *lot* of moving parts in this area, as well as a considerable amount > of backwards-compatibility history that we must not take lightly. It is > well to remember also that civil time and DST laws were written by > politicians who have never heard of mathematical consistency. > > regards, tom lane >