Thread: Timestamps without time zone

Timestamps without time zone

From
Achilleas Mantzios
Date:
1st off, happy new year to everybody.
I know the issue with timestamps without time zone and the various set/get
methods maybe has been discussed exhaustively, and i understand that for new
applications serious studying must take place before any implementation
decisions are taken.

However it seems for my case that i am really trapped, and i ask your advice:
(We run 1 central master server with jboss, postgresql 7.4.18 and about 40
slave (heavily prunned clones) servers on the 7 seas over uucp satellite
connections.
Let me say in advance that the facts that a) we dont always have login
prompt/cheap comms to the servers
b) the heavy usage of our version of dbmirror + c) the size of the
database/apps +d) lack of adequate manforce, makes it quite hard to upgrade
to 8.2+.
I *shall* do it some time and i hope within 2008, though).

Now the problem.
I keep gps data timestamps as "timestamp without time zone".
(i'll use this to explain my problem, altho i have "timestamp without time
zone" which suffer from the same effects, in various other apps as well).
Our server local time zone is set to EET (Athens/Greece) at winter months
and EEST at summer months.

I had identified the problem, on some standalone applications and i used the
brute-force method of
java.util.TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
so all related problems were gone.

However if i do it on the jboss JVM then the whole thing gets screwed up, as
it will be unsafe to touch a JVM global property, just for the sake of some
operations, on behalf of some user.
One solution would be to synchronize on blocks containing Timestamp operations
like
syncronized(some global application object) {
    java.util.TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
    // database operations here
    ......
    java.util.TimeZone.setDefault(null);
}

This way i can solve the problem at the expense of some loss of concurrency.
Untill now i postponed talking about the problem in action, so let me now post
an example.
I have the following table
dynacom=# \d gpscookeddata
            Table "public.gpscookeddata"
  Column   |            Type             | Modifiers
-----------+-----------------------------+-----------
 vslid     | integer                     | not null
 utcts     | timestamp without time zone | not null
 latid     | double precision            |
 longi     | double precision            |
 tracktrue | double precision            |
 avgspeed  | double precision            |
 minspeed  | double precision            |
 maxspeed  | double precision            |
Indexes:
    "gpscookeddata_pkey" primary key, btree (vslid, utcts)
    "gpscookeddata_utcts" btree (utcts)
    "gpscookeddata_vslid" btree (vslid)

where utcts  holds the UTC (GMT) timestamp.
some sample data:
select utcts,latid,longi,tracktrue,avgspeed,minspeed,maxspeed from
gpscookeddata where vslid=92 and utcts<'2006-03-26 04:00:00' and
utcts>='2006-03-26 02:00:00' order by 1;
        utcts        |  latid   |  longi   | tracktrue | avgspeed | minspeed |
maxspeed
---------------------+----------+----------+-----------+----------+----------+----------
 2006-03-26 02:29:49 | -2256.13 | -3707.46 |     211.1 |     13.6 |     13.3 |
14
 2006-03-26 02:59:49 | -2302.31 | -3703.83 |     207.7 |       14 |     13.8 |
14.1
 2006-03-26 03:29:49 |  -2308.7 | -3700.11 |     209.4 |     14.4 |       14 |
14.6
 2006-03-26 03:59:49 | -2315.16 | -3656.16 |     210.4 |     14.8 |     14.5 |
15
(4 rows)


Now if the application wants to read data between start='2006-03-26 02:00:00'
and end='2006-03-26 04:00:00'

java.text.SimpleDateFormat dfrm =
new java.text.SimpleDateFormat ("yyyy-MM-dd HH:mm:ss");
dfrm.setTimeZone(TimeZone.getTimeZone("GMT"));
java.util.Date gstartDate = dfrm.parse(start);
java.util.Date gendDate = dfrm.parse(end);

//here the two dates have the correct values (millisecond wise)

st = con.prepareStatement("select
utcts,latid,longi,tracktrue,avgspeed,minspeed,maxspeed from gpscookeddata
where vslid=? and utcts<? and utcts>=? order by utcts");
st.setInt(1,Integer.parseInt(vslid));
st.setTimestamp(2,new java.sql.Timestamp(gendDate.getTime()));
st.setTimestamp(3,new java.sql.Timestamp(gstartDate.getTime()));

if i do that then in the pgsql.log i get
    select utcts,latid,longi,tracktrue,avgspeed,minspeed,maxspeed from
    gpscookeddata where vslid=92 and utcts<'2006-03-26 07:00:00.000000+03' and
    utcts>='2006-03-26 05:00:00.000000+03' order by utcts
which does a wrong WHERE since the '+03' part is disragarded (correctly) by
postgresql. So while technically the '2006-03-26 07:00:00.000000+03' value is
fine, and the timestamp is indeed that one, this does not work in the query.
If i rewrite the last 2 statements (as Chip Gobs suggested recently in the
list, http://archives.postgresql.org/pgsql-jdbc/2007-12/msg00013.php) as

st.setTimestamp(2,Timestamp.valueOf(end));
st.setTimestamp(3,Timestamp.valueOf(start));

then it does
    select utcts,latid,longi,tracktrue,avgspeed,minspeed,maxspeed from
    gpscookeddata where vslid=92 and utcts<'2006-03-26 04:00:00.000000+03' and
    utcts>='2006-03-26 02:00:00.000000+02' order by utcts
which although technically wrong gives the right query results
(notice that at 2006-03-26 03:00:00 EET the time zone is increased by 1 (+03),
to reflect dayligght savings).
Even then, it seems that jdbc does yet another conversion when transfering
timestamp values:

rs = st.executeQuery();
while (rs.next()) {
    java.util.Date thists = rs.getTimestamp(1);
    ....
    System.out.println("utc="+thists);
    ....
}
rs.close();
st.close();

The above gives,
utc=2006-03-26 02:29:49.0
utc=2006-03-26 02:59:49.0
utc=2006-03-26 04:29:49.0 (!!! +1)
utc=2006-03-26 04:59:49.0 (!!! +1)
so it interprets the above dates as greek dates, so at this point i have lost
track and cant get the right dates.

As you may have found (if you have read up to this point - i hope!!),
i have not found a decent way to deal with this beast.
Taking into account the huge difficulty to make everything "with time zone" in
the database, along with the fact that some "local" dates really have *no*
accompanying timezone info, and thus presenting them as "with time zone"
would be a lie, how should deal with this?

Any help much appreciated.
Sorry for the length of my post.
--
Achilleas Mantzios

Re: Timestamps without time zone

From
Achilleas Mantzios
Date:
Στις Tuesday 08 January 2008 14:12:56 ο/η Achilleas Mantzios έγραψε:
>...
> java.text.SimpleDateFormat dfrm =
> new java.text.SimpleDateFormat ("yyyy-MM-dd HH:mm:ss");
> dfrm.setTimeZone(TimeZone.getTimeZone("GMT"));
> java.util.Date gstartDate = dfrm.parse(start);
> java.util.Date gendDate = dfrm.parse(end);
>
> //here the two dates have the correct values (millisecond wise)
The input values passed are
start: 2006-03-26 02:00:00
end : 2006-03-26 04:00:00
It is confirmed by System.out.println("gstartDate="+gstartDate); which gives
"Sun Mar 26 05:00:00 EEST 2006" and "Sun Mar 26 07:00:00 EEST 2006"
and which is absolutely correct (EEST is +03, so at least up to this point
the millisecond values of gstartDate , gendDate are correct).
>
> st = con.prepareStatement("select
> utcts,latid,longi,tracktrue,avgspeed,minspeed,maxspeed from gpscookeddata
> where vslid=? and utcts<? and utcts>=? order by utcts");
> st.setInt(1,Integer.parseInt(vslid));
> st.setTimestamp(2,new java.sql.Timestamp(gendDate.getTime()));
> st.setTimestamp(3,new java.sql.Timestamp(gstartDate.getTime()));
Now instead of the above i tried something that should be "more" correct and
according to specs
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    st = con.prepareStatement("select
        utcts,latid,longi,tracktrue,avgspeed,minspeed,maxspeed from gpscookeddata
        where vslid=? and utcts<? and utcts>=? order by utcts");
    st.setInt(1,Integer.parseInt(vslid));
    st.setTimestamp(2,new java.sql.Timestamp(gendDate.getTime()),cal);
    st.setTimestamp(3,new java.sql.Timestamp(gstartDate.getTime()),cal);
however again i see that the JDBC insists producing code like:
    select utcts,latid,longi,tracktrue,avgspeed,minspeed,maxspeed from
    gpscookeddata where vslid=92 and utcts<'2006-03-26 07:00:00.000000+03' and
    utcts>='2006-03-26 05:00:00.000000+03' order by utcts
postgresql backend discards the +03 (as specified by the docs) so we come to
the question:
how can it be done, without dirty tricks? and i am not yet at the point to
interpret rs.getTimestamp(1) (from the above query yet).
What is the best practice (if any) to deal with "timestamps without time
zone"?

In the java docs it says about
PreparedStatement.setTimestamp(int parameterIndex,Timestamp x, Calendar cal):
"Sets the designated parameter to the given java.sql.Timestamp value, using
the given Calendar object. The driver uses the Calendar object to construct
an SQL TIMESTAMP value, which the driver then sends to the database. With a
Calendar object, the driver can calculate the timestamp taking into account a
custom timezone. If no Calendar object is specified, the driver uses the
default timezone, which is that of the virtual machine running the
application."
If we explicitly tell the driver that we want our effective SQL TIMESTAMP
value to be with cal timezone, why the driver inserts the JVM default time
zone?

>..

--
Achilleas Mantzios

Re: Timestamps without time zone

From
Oliver Jowett
Date:
Achilleas Mantzios wrote:

> Now instead of the above i tried something that should be "more" correct and
> according to specs
>     Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
>     st = con.prepareStatement("select
>         utcts,latid,longi,tracktrue,avgspeed,minspeed,maxspeed from gpscookeddata
>         where vslid=? and utcts<? and utcts>=? order by utcts");
>     st.setInt(1,Integer.parseInt(vslid));
>     st.setTimestamp(2,new java.sql.Timestamp(gendDate.getTime()),cal);
>     st.setTimestamp(3,new java.sql.Timestamp(gstartDate.getTime()),cal);
> however again i see that the JDBC insists producing code like:
>     select utcts,latid,longi,tracktrue,avgspeed,minspeed,maxspeed from
>     gpscookeddata where vslid=92 and utcts<'2006-03-26 07:00:00.000000+03' and
>     utcts>='2006-03-26 05:00:00.000000+03' order by utcts

This is unexpected - as you say the driver should generate timestamp
values in UTC if you pass a UTC calendar to setTimestamp.

Which driver version are you using?
How are you concluding that the JDBC driver is sending +03 values?

The driver will not generate a query string like you quoted above as it
uses parameter placeholders (assuming you are using protocol version 3
anyway) and sends the values out of line, which is why I'm asking.

-O

Re: Timestamps without time zone

From
Achilleas Mantzios
Date:
Στις Wednesday 09 January 2008 14:43:25 ο/η Oliver Jowett έγραψε:
> Achilleas Mantzios wrote:
> > Now instead of the above i tried something that should be "more" correct
> > and according to specs
> >     Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
> >     st = con.prepareStatement("select
> >         utcts,latid,longi,tracktrue,avgspeed,minspeed,maxspeed from
> > gpscookeddata where vslid=? and utcts<? and utcts>=? order by utcts");
> >     st.setInt(1,Integer.parseInt(vslid));
> >     st.setTimestamp(2,new java.sql.Timestamp(gendDate.getTime()),cal);
> >     st.setTimestamp(3,new java.sql.Timestamp(gstartDate.getTime()),cal);
> > however again i see that the JDBC insists producing code like:
> >     select utcts,latid,longi,tracktrue,avgspeed,minspeed,maxspeed from
> >     gpscookeddata where vslid=92 and utcts<'2006-03-26 07:00:00.000000+03'
> > and utcts>='2006-03-26 05:00:00.000000+03' order by utcts
>
> This is unexpected - as you say the driver should generate timestamp
> values in UTC if you pass a UTC calendar to setTimestamp.
>
> Which driver version are you using?
Its the 7.4.19 built from source.
> How are you concluding that the JDBC driver is sending +03 values?
by the postgresql log, after setting log_statement = true
>
> The driver will not generate a query string like you quoted above as it
> uses parameter placeholders (assuming you are using protocol version 3
> anyway) and sends the values out of line, which is why I'm asking.
>
> -O

--
Achilleas Mantzios

Re: Timestamps without time zone

From
Oliver Jowett
Date:
Achilleas Mantzios wrote:

>> Which driver version are you using?
> Its the 7.4.19 built from source.

Ah, there is your problem then. That is a very old driver and had
problems with exactly the case you're trying to use (setTimestamp() with
a Calendar), amongst many other bugs.

Can you try a recent driver and see if it fixes your problem? Looking at
CVS history it looks like 8.1 is the earliest driver with
timezone-related fixes, but I'd suggest you try the most recent stable
driver.

Newer drivers will still talk to older servers, so you shouldn't need to
upgrade your server to test this.

-O

Re: Timestamps without time zone

From
Achilleas Mantzios
Date:
Στις Wednesday 09 January 2008 15:05:24 ο/η Oliver Jowett έγραψε:
> Achilleas Mantzios wrote:
> >> Which driver version are you using?
> >
> > Its the 7.4.19 built from source.
>
> Ah, there is your problem then. That is a very old driver and had
> problems with exactly the case you're trying to use (setTimestamp() with
> a Calendar), amongst many other bugs.
>
> Can you try a recent driver and see if it fixes your problem? Looking at
> CVS history it looks like 8.1 is the earliest driver with
> timezone-related fixes, but I'd suggest you try the most recent stable
> driver.
>
> Newer drivers will still talk to older servers, so you shouldn't need to
> upgrade your server to test this.

Thanx a lot,i'll try that right away.

>
> -O
>
> ---------------------------(end of broadcast)---------------------------
> TIP 9: In versions below 8.0, the planner will ignore your desire to
>        choose an index scan if your joining column's datatypes do not
>        match



--
Achilleas Mantzios

Re: Timestamps without time zone

From
Achilleas Mantzios
Date:
Στις Wednesday 09 January 2008 15:05:24 ο/η Oliver Jowett έγραψε:
> Achilleas Mantzios wrote:
> >> Which driver version are you using?
> >
> > Its the 7.4.19 built from source.
>
> Ah, there is your problem then. That is a very old driver and had
> problems with exactly the case you're trying to use (setTimestamp() with
> a Calendar), amongst many other bugs.
>
> Can you try a recent driver and see if it fixes your problem? Looking at
> CVS history it looks like 8.1 is the earliest driver with
> timezone-related fixes, but I'd suggest you try the most recent stable
> driver.
>
> Newer drivers will still talk to older servers, so you shouldn't need to
> upgrade your server to test this.

I tried with postgresql-8.2-507.jdbc3.jar, and it works like a charm!
Not only that, but the getTimestamp() too! I was starting to question my night
sleep quality, read old lenghty articles about java dates/timezones etc!!
Thank you a lot! I didnt know jdbc newer versions were backward compatible
with older servers.

Just a comment:
Why not merge from current changes/bugfixes to the JDBC?
I mean 7.4 is old ok, but *officially supported* also!

>
> -O
>
> ---------------------------(end of broadcast)---------------------------
> TIP 9: In versions below 8.0, the planner will ignore your desire to
>        choose an index scan if your joining column's datatypes do not
>        match



--
Achilleas Mantzios

Re: Timestamps without time zone

From
Tom Lane
Date:
Achilleas Mantzios <achill@matrix.gatewaynet.com> writes:
> ���� Wednesday 09 January 2008 15:05:24 �/� Oliver Jowett ������:
>> Ah, there is your problem then. That is a very old driver and had
>> problems with exactly the case you're trying to use (setTimestamp() with
>> a Calendar), amongst many other bugs.

> Just a comment:
> Why not merge from current changes/bugfixes to the JDBC?
> I mean 7.4 is old ok, but *officially supported* also!

Back-patching this type of behavioral change would be a seriously bad
idea, because it would break applications that are expecting the old
behavior.

            regards, tom lane

Re: Timestamps without time zone

From
Achilleas Mantzios
Date:
Στις Wednesday 09 January 2008 17:42:57 ο/η Tom Lane έγραψε:
> Achilleas Mantzios <achill@matrix.gatewaynet.com> writes:
> > ���� Wednesday 09 January 2008 15:05:24 �/� Oliver Jowett �����
��:
> >> Ah, there is your problem then. That is a very old driver and had
> >> problems with exactly the case you're trying to use (setTimestamp() with
> >> a Calendar), amongst many other bugs.
> >
> > Just a comment:
> > Why not merge from current changes/bugfixes to the JDBC?
> > I mean 7.4 is old ok, but *officially supported* also!
>
> Back-patching this type of behavioral change would be a seriously bad
> idea, because it would break applications that are expecting the old
> behavior.
That is true indeed, but on the other hand a lot of people write extremely
buggy code (and some times intentionally as in my case) in order to deal
with those old JDBC drivers bugs. I mean those people would have to face
the reality sooner or later when upgrading to 8.x, why not force them to take
the action earlier, when, in addition, the new correct behaviour adheres to
the JDBC spec, whereas the old one didnt?
>
>             regards, tom lane



--
Achilleas Mantzios

Re: Timestamps without time zone

From
Oliver Jowett
Date:
Achilleas Mantzios wrote:

> Why not merge from current changes/bugfixes to the JDBC?
> I mean 7.4 is old ok, but *officially supported* also!

Pre 8.0 the JDBC driver was included with the server source.

Post 8.0 it was shipped separatedly, because amongst other things the
development cycle of the JDBC driver didn't match the server development
cycle well.

For 7.4 the decision about what to declare as "supported" is somewhat
out of our hands because the JDBC driver is only a small part of the
large system.

We really only support driver versions 8.0 and later - 7.4 gets security
and trivial bugfixes and that's about it. There were large architectural
changes in the 8.0 driver and so it's not a simple task to backport most
bugfixes to the 7.4 code. And given that newer drivers still talk to
older servers (back to 7.2) it's not really a pressing concern since
upgrading the driver works for most apps.

-O

Re: Timestamps without time zone

From
Kris Jurka
Date:

On Wed, 9 Jan 2008, Achilleas Mantzios wrote:

> Why not merge from current changes/bugfixes to the JDBC?
> I mean 7.4 is old ok, but *officially supported* also!
>

From the JDBC team the last version we support is 8.0 and as Oliver has
noted timestamps don't work correctly there either.  Some changes are just
too big/dangerous to backpatch.

http://jdbc.postgresql.org/download.html

Kris Jurka

Re: Timestamps without time zone

From
Achilleas Mantzios
Date:
Στις Wednesday 09 January 2008 22:40:11 ο/η Kris Jurka έγραψε:
> On Wed, 9 Jan 2008, Achilleas Mantzios wrote:
> > Why not merge from current changes/bugfixes to the JDBC?
> > I mean 7.4 is old ok, but *officially supported* also!
> >
> >
> From the JDBC team the last version we support is 8.0 and as Oliver has
>
> noted timestamps don't work correctly there either.  Some changes are just
> too big/dangerous to backpatch.
>
> http://jdbc.postgresql.org/download.html

I tried other apps with the new postgresql-8.2-507.jdbc3.jar and some of them
break. So i will need to live with the 7.4.19 driver for a while.
One question, is there a way to get a hold of the Default TimeZone instance
of the JVM (lets call it default_TZ) and put all code that alter it in some
sort of
    synchronized (default_TZ) {
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
        // jdbc calls here that deal with timestamps without time zone
        java.util.TimeZone.setDefault(null);
    }
block?
Any date/time/Calendar operation in java effectively tries to access the
default TimeZone.
So, the above would make the system work perfectly from a correctness point of
view (losing in parallelism of course) as a workaround until we upgrade
to 8.x when ofcourse i will have to rewrite all portions of code (among
others) that deal with timestamps without time zone.
What do you think?
>
> Kris Jurka
>
> ---------------------------(end of broadcast)---------------------------
> TIP 1: if posting/reading through Usenet, please send an appropriate
>        subscribe-nomail command to majordomo@postgresql.org so that your
>        message can get through to the mailing list cleanly



--
Achilleas Mantzios

Re: Timestamps without time zone

From
Kris Jurka
Date:

On Thu, 10 Jan 2008, Achilleas Mantzios wrote:

> I tried other apps with the new postgresql-8.2-507.jdbc3.jar and some of
> them break. So i will need to live with the 7.4.19 driver for a while.

Without an actual failure it's tough to advise you here.  You might try
the URL option protocolVersion=2 or stringtype=unspecified to solve some
of the more common problems people have come across with the upgrade as a
workaround.

> One question, is there a way to get a hold of the Default TimeZone
> instance of the JVM (lets call it default_TZ) and put all code that
> alter it in some sort of
>     synchronized (default_TZ) {
>         TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
>         // jdbc calls here that deal with timestamps without time zone
>         java.util.TimeZone.setDefault(null);
>     }
> block?

This seems unlikely to work.  Everyone would have to get the default_TZ
object before doing any TZ manipulation so they'd synchronzie on the same
object.  And there's no way you can control other code that just wants
to print out a date.  It's not going to synchronize itself on anything.

Kris Jurka

Re: Timestamps without time zone

From
Achilleas Mantzios
Date:
Στις Friday 11 January 2008 07:12:24 ο/η Kris Jurka έγραψε:
>
> On Thu, 10 Jan 2008, Achilleas Mantzios wrote:
>
> > I tried other apps with the new postgresql-8.2-507.jdbc3.jar and some of
> > them break. So i will need to live with the 7.4.19 driver for a while.
>
> Without an actual failure it's tough to advise you here.  You might try
> the URL option protocolVersion=2 or stringtype=unspecified to solve some
> of the more common problems people have come across with the upgrade as a
> workaround.
>
for example the following exception:
org.postgresql.util.PSQLException: Operation requires a scrollable ResultSet, but this ResultSet is FORWARD_ONLY
i have the ResultSet.last() call at least 290 times in my code so i should
adjust the relevant con.prepareStatement() to make the ResultSet scrollable.
I cannot do that at the moment.
I didnt post this actual problem, cause apart from the fact that it is natural
to happen, there could be others that i just dont know of yet. So due to lack
of time i didnt write about it at all.
> > One question, is there a way to get a hold of the Default TimeZone
> > instance of the JVM (lets call it default_TZ) and put all code that
> > alter it in some sort of
> >     synchronized (default_TZ) {
> >         TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
> >         // jdbc calls here that deal with timestamps without time zone
> >         java.util.TimeZone.setDefault(null);
> >     }
> > block?
>
> This seems unlikely to work.  Everyone would have to get the default_TZ
> object before doing any TZ manipulation so they'd synchronzie on the same
> object.  And there's no way you can control other code that just wants
> to print out a date.  It's not going to synchronize itself on anything.
In java 1.4.2 (yes i know we are way old on this one also!), class TimeZone has:
private static TimeZone  defaultZone = null;
Date in java 1.4.2 has a Calendar private field, and Calendar has a TimeZone.
All Date/Calendar manipulation eventually would access static field java.util.TimeZone.defaultZone.
If defaultZone was public, synchronizing on it would solve the problem,
unfortunately it is private.
Doing it with a "userland" object, as you say, would be impractical/impossible.
>
> Kris Jurka
>

--
Achilleas Mantzios