Patch from Florian Wunderlich to correctly support java Timestamps. Previously
authorBarry Lind
Tue, 14 Jan 2003 09:13:51 +0000 (09:13 +0000)
committerBarry Lind
Tue, 14 Jan 2003 09:13:51 +0000 (09:13 +0000)
the code would only capture milliseconds where as both postgres and the java
Timestamp object support greater resolution.
Also fixed a bug reported by Rhett Sutphin where the last digit of the
fractional seconds was lost when using timestamp without time zone

 Modified Files:
  jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java
  jdbc/org/postgresql/test/jdbc2/TimestampTest.java

src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/TimestampTest.java

index f75ab70d16cb152c19b051c5e4386c2acddd8e9c..48e8b8f70bbae8c6a80afd64f6731d234aa4ee32 100644 (file)
@@ -13,7 +13,7 @@ import org.postgresql.largeobject.*;
 import org.postgresql.util.PGbytea;
 import org.postgresql.util.PSQLException;
 
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1ResultSet.java,v 1.7 2002/10/19 22:10:36 barry Exp $
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1ResultSet.java,v 1.8 2003/01/14 09:13:51 barry Exp $
  * This class defines methods of the jdbc1 specification.  This class is
  * extended by org.postgresql.jdbc2.AbstractJdbc2ResultSet which adds the jdbc2
  * methods.  The real ResultSet class (for jdbc1) is org.postgresql.jdbc1.Jdbc1ResultSet
@@ -844,7 +844,14 @@ public abstract class AbstractJdbc1ResultSet
    * 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,13 @@ public abstract class AbstractJdbc1ResultSet
            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 +908,36 @@ public abstract class AbstractJdbc1ResultSet
                char c = s.charAt(i++);
                if (c == '.')
                {
-                   // Found a fractional value. Append up to 3 digits including
-                   // the leading '.'
-                   do
+                   // Found a fractional value.
+                   final int start = i;
+                   while (true)
                    {
-                       if (i < 24)
-                           rs.sbuf.append(c);
                        c = s.charAt(i++);
+                       if (!Character.isDigit(c))
+                           break;
+                       if (i == slen)
+                           {
+                               i++;
+                               break;
+                           }
                    }
-                   while (i < slen && Character.isDigit(c));
 
-                   // 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');
-               }
-               else
-               {
-                   // No fractional seconds, lets add some.
-                   rs.sbuf.append(".000");
+                   // The range [start, i - 1) contains all fractional digits.
+                   final int end = i - 1;
+                   try
+                       {
+                           nanos = Integer.parseInt(s.substring(start, end));
+                       }
+                   catch (NumberFormatException e)
+                       {
+                           throw new PSQLException("postgresql.unusual", e);
+                       }
+
+                   // 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 +954,7 @@ public abstract class AbstractJdbc1ResultSet
                        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 +963,11 @@ public abstract class AbstractJdbc1ResultSet
                    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");
                    }
                }
            }
@@ -981,9 +1006,13 @@ public abstract class AbstractJdbc1ResultSet
            {
                // 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() );
+                   org.postgresql.Driver.debug("the data after parsing is " 
+                     + rs.sbuf.toString() + " with " + nanos + " nanos");
 
-               return new Timestamp(df.parse(rs.sbuf.toString()).getTime());
+               Timestamp result = 
+                   new Timestamp(df.parse(rs.sbuf.toString()).getTime());
+               result.setNanos(nanos);
+               return result;
            }
            catch (ParseException e)
            {
index 2472a3d3d82aff9dfe005e97ae93e1397385256e..a5307e3b42ed369056490e20caa64c9c916ccc65 100644 (file)
@@ -5,7 +5,7 @@ import junit.framework.TestCase;
 import java.sql.*;
 
 /*
- * $Id: TimestampTest.java,v 1.9 2002/09/06 21:23:06 momjian Exp $
+ * $Id: TimestampTest.java,v 1.10 2003/01/14 09:13:51 barry Exp $
  *
  * Test get/setTimestamp for both timestamp with time zone and
  * timestamp without time zone datatypes
@@ -52,11 +52,12 @@ public class TimestampTest extends TestCase
            assertEquals(1, stmt.executeUpdate(TestUtil.insertSQL(TSWTZ_TABLE, "'" + TS1WTZ_PGFORMAT + "'")));
            assertEquals(1, stmt.executeUpdate(TestUtil.insertSQL(TSWTZ_TABLE, "'" + TS2WTZ_PGFORMAT + "'")));
            assertEquals(1, stmt.executeUpdate(TestUtil.insertSQL(TSWTZ_TABLE, "'" + TS3WTZ_PGFORMAT + "'")));
+           assertEquals(1, stmt.executeUpdate(TestUtil.insertSQL(TSWTZ_TABLE, "'" + TS4WTZ_PGFORMAT + "'")));
 
            // Fall through helper
            timestampTestWTZ();
 
-           assertEquals(3, stmt.executeUpdate("DELETE FROM " + TSWTZ_TABLE));
+           assertEquals(4, stmt.executeUpdate("DELETE FROM " + TSWTZ_TABLE));
 
            stmt.close();
        }
@@ -88,10 +89,13 @@ public class TimestampTest extends TestCase
            pstmt.setTimestamp(1, TS3WTZ);
            assertEquals(1, pstmt.executeUpdate());
 
+           pstmt.setTimestamp(1, TS4WTZ);
+           assertEquals(1, pstmt.executeUpdate());
+
            // Fall through helper
            timestampTestWTZ();
 
-           assertEquals(3, stmt.executeUpdate("DELETE FROM " + TSWTZ_TABLE));
+           assertEquals(4, stmt.executeUpdate("DELETE FROM " + TSWTZ_TABLE));
 
            pstmt.close();
            stmt.close();
@@ -117,11 +121,12 @@ public class TimestampTest extends TestCase
            assertEquals(1, stmt.executeUpdate(TestUtil.insertSQL(TSWOTZ_TABLE, "'" + TS1WOTZ_PGFORMAT + "'")));
            assertEquals(1, stmt.executeUpdate(TestUtil.insertSQL(TSWOTZ_TABLE, "'" + TS2WOTZ_PGFORMAT + "'")));
            assertEquals(1, stmt.executeUpdate(TestUtil.insertSQL(TSWOTZ_TABLE, "'" + TS3WOTZ_PGFORMAT + "'")));
+           assertEquals(1, stmt.executeUpdate(TestUtil.insertSQL(TSWOTZ_TABLE, "'" + TS4WOTZ_PGFORMAT + "'")));
 
            // Fall through helper
            timestampTestWOTZ();
 
-           assertEquals(3, stmt.executeUpdate("DELETE FROM " + TSWOTZ_TABLE));
+           assertEquals(4, stmt.executeUpdate("DELETE FROM " + TSWOTZ_TABLE));
 
            stmt.close();
        }
@@ -154,10 +159,13 @@ public class TimestampTest extends TestCase
            pstmt.setTimestamp(1, TS3WOTZ);
            assertEquals(1, pstmt.executeUpdate());
 
+           pstmt.setTimestamp(1, TS4WOTZ);
+           assertEquals(1, pstmt.executeUpdate());
+
            // Fall through helper
            timestampTestWOTZ();
 
-           assertEquals(3, stmt.executeUpdate("DELETE FROM " + TSWOTZ_TABLE));
+           assertEquals(4, stmt.executeUpdate("DELETE FROM " + TSWOTZ_TABLE));
 
            pstmt.close();
            stmt.close();
@@ -195,6 +203,11 @@ public class TimestampTest extends TestCase
        assertNotNull(t);
        assertTrue(t.equals(TS3WTZ));
 
+       assertTrue(rs.next());
+       t = rs.getTimestamp(1);
+       assertNotNull(t);
+       assertTrue(t.equals(TS4WTZ));
+
        assertTrue(! rs.next()); // end of table. Fail if more entries exist.
 
        rs.close();
@@ -216,17 +229,22 @@ public class TimestampTest extends TestCase
        assertTrue(rs.next());
        t = rs.getTimestamp(1);
        assertNotNull(t);
-       assertTrue(t.toString().equals(TS1WOTZ_JAVAFORMAT));
+       assertTrue(t.equals(TS1WOTZ));
 
        assertTrue(rs.next());
        t = rs.getTimestamp(1);
        assertNotNull(t);
-       assertTrue(t.toString().equals(TS2WOTZ_JAVAFORMAT));
+       assertTrue(t.equals(TS2WOTZ));
 
        assertTrue(rs.next());
        t = rs.getTimestamp(1);
        assertNotNull(t);
-       assertTrue(t.toString().equals(TS3WOTZ_JAVAFORMAT));
+       assertTrue(t.equals(TS3WOTZ));
+
+       assertTrue(rs.next());
+       t = rs.getTimestamp(1);
+       assertNotNull(t);
+       assertTrue(t.equals(TS4WOTZ));
 
        assertTrue(! rs.next()); // end of table. Fail if more entries exist.
 
@@ -277,20 +295,21 @@ public class TimestampTest extends TestCase
    private static final java.sql.Timestamp TS3WTZ = getTimestamp(2000, 7, 7, 15, 0, 0, 123000000, "GMT");
    private static final String TS3WTZ_PGFORMAT = "2000-07-07 15:00:00.123+00";
 
+   private static final java.sql.Timestamp TS4WTZ = getTimestamp(2000, 7, 7, 15, 0, 0, 123456000, "GMT");
+   private static final String TS4WTZ_PGFORMAT = "2000-07-07 15:00:00.123456+00";
+
 
    private static final java.sql.Timestamp TS1WOTZ = getTimestamp(1950, 2, 7, 15, 0, 0, 100000000, null);
    private static final String TS1WOTZ_PGFORMAT = "1950-02-07 15:00:00.1";
-   private static final String TS1WOTZ_JAVAFORMAT = "1950-02-07 15:00:00.1";
 
    private static final java.sql.Timestamp TS2WOTZ = getTimestamp(2000, 2, 7, 15, 0, 0, 120000000, null);
    private static final String TS2WOTZ_PGFORMAT = "2000-02-07 15:00:00.12";
-   //there is probably a bug here in that this needs to be .1 instead of .12, but I couldn't find it now
-   private static final String TS2WOTZ_JAVAFORMAT = "2000-02-07 15:00:00.1";
 
    private static final java.sql.Timestamp TS3WOTZ = getTimestamp(2000, 7, 7, 15, 0, 0, 123000000, null);
    private static final String TS3WOTZ_PGFORMAT = "2000-07-07 15:00:00.123";
-   //there is probably a bug here in that this needs to be .12 instead of .123, but I couldn't find it now
-   private static final String TS3WOTZ_JAVAFORMAT = "2000-07-07 15:00:00.12";
+
+   private static final java.sql.Timestamp TS4WOTZ = getTimestamp(2000, 7, 7, 15, 0, 0, 123456000, null);
+   private static final String TS4WOTZ_PGFORMAT = "2000-07-07 15:00:00.123456";
 
    private static final String TSWTZ_TABLE = "testtimestampwtz";
    private static final String TSWOTZ_TABLE = "testtimestampwotz";