toTimestamp fractional seconds fix (patch against CVS) - Mailing list pgsql-jdbc

From Florian Wunderlich
Subject toTimestamp fractional seconds fix (patch against CVS)
Date
Msg-id 3E070A04.BB0EC23C@hq.factor3.com
Whole thread Raw
Responses Re: toTimestamp fractional seconds fix (patch against CVS)  (Barry Lind <blind@xythos.com>)
List pgsql-jdbc
In the CVS, in AbstractJdbc1ResultSet.java, a Timestamp returned from
toTimestamp is created with DateFormat.parse, which means that
fractional milliseconds are dropped - no more than 3 fractional digits
are considered.

1. This means that the JDBC driver returns values that are not in the
database. A subsequent query specifying this value in a WHERE clause for
example will not return the record in question.

2. java.sql.Timestamp specifies that all fractional seconds are to be
stored in the nanos field, and that the getTime method shall return only
integral seconds.

The attached patch against the most recent version from the CVS as of
2002-12-23 14:02 (rev 1.7) fixes this behavior. I have verified it
against postgresql-7.2.1.Index: AbstractJdbc1ResultSet.java
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java,v
retrieving revision 1.7
diff -u -b -r1.7 AbstractJdbc1ResultSet.java
--- AbstractJdbc1ResultSet.java    2002/10/19 22:10:36    1.7
+++ AbstractJdbc1ResultSet.java    2002/12/23 12:49:34
@@ -844,8 +844,15 @@
     * Java also expects fractional seconds to 3 places where postgres
     * will give, none, 2 or 6 depending on the time and postgres version.
     * From version 7.2 postgres returns fractional seconds to 6 places.
-    * If available, we drop the last 3 digits.
     *
+        * According to the Timestamp documentation, fractional digits are kept
+        * in the nanos field of Timestamp and not in the milliseconds of Date.
+        * Thus, parsing for fractional digits is entirely separated from the
+        * rest.
+        *
+        * The method assumes that there are no more than 9 fractional
+        * digits given. Undefined behavior if this is not the case.
+    *
     * @param s           The ISO formated date string to parse.
     * @param resultSet The ResultSet this date is part of.
     *
@@ -881,6 +888,18 @@
             rs.sbuf.append(s);
             int slen = s.length();

+
+                        //
+                        // For a Timestamp, the fractional seconds are stored in the
+                        // nanos field. As a DateFormat is used for parsing which can
+                        // only parse to millisecond precision and which returns a
+                        // Date object, the fractional second parsing is completely
+                        // separate.
+                        //
+
+                        int nanos = 0;
+
+
             if (slen > 19)
             {
                 // The len of the ISO string to the second value is 19 chars. If
@@ -894,25 +913,42 @@
                 char c = s.charAt(i++);
                 if (c == '.')
                 {
-                    // Found a fractional value. Append up to 3 digits including
-                    // the leading '.'
+                                        // Found a fractional value.
+
+                                        final int start = i;
+
                     do
                     {
-                        if (i < 24)
-                            rs.sbuf.append(c);
                         c = s.charAt(i++);
                     }
                     while (i < slen && Character.isDigit(c));
+
+
+                                        //
+                                        // The range [start, i - 1) contains all fractional digits.
+                                        //
+
+                                        final int end = i - 1;

-                    // If there wasn't at least 3 digits we should add some zeros
-                    // to make up the 3 digits we tell java to expect.
-                    for (int j = i; j < 24; j++)
-                        rs.sbuf.append('0');
+                                        try
+                                        {
+                                          nanos = Integer.parseInt(s.substring(start, end));
                 }
-                else
+                                        catch (NumberFormatException e)
                 {
-                    // No fractional seconds, lets add some.
-                    rs.sbuf.append(".000");
+                                          // this Exception can never happen, as the range has
+                                          // been checked before.
+                                        }
+
+
+                                        //
+                                        // The nanos field stores nanoseconds. Adjust the parsed
+                                        // value to the correct magnitude.
+                                        //
+
+                                        for (int digitsToNano = 9 - (end - start);
+                                             digitsToNano > 0; --digitsToNano)
+                                          nanos *= 10;
                 }

                 if (i < slen)
@@ -929,7 +965,7 @@
                         rs.sbuf.append(":00");

                     // we'll use this dateformat string to parse the result.
-                    df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
+                    df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
                 }
                 else
                 {
@@ -938,11 +974,11 @@
                     if (pgDataType.equals("timestamptz"))
                     {
                         rs.sbuf.append(" GMT");
-                        df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
+                        df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
                     }
                     else
                     {
-                        df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+                        df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                     }
                 }
             }
@@ -982,8 +1018,13 @@
                 // All that's left is to parse the string and return the ts.
                 if ( org.postgresql.Driver.logDebug )
                     org.postgresql.Driver.debug( "" + df.parse(rs.sbuf.toString()).getTime() );
+
+                                final Timestamp result =
+                                  new Timestamp(df.parse(rs.sbuf.toString()).getTime());
+
+                                result.setNanos(nanos);

-                return new Timestamp(df.parse(rs.sbuf.toString()).getTime());
+                                return result;
             }
             catch (ParseException e)
             {

pgsql-jdbc by date:

Previous
From: Daniel Serodio
Date:
Subject: Re: JDBC Error
Next
From: "Remigius Stalder"
Date:
Subject: Re: Stored procedures/functions that can return recordsets...