From: Barry Lind Date: Tue, 14 Jan 2003 09:13:51 +0000 (+0000) Subject: Patch from Florian Wunderlich to correctly support java Timestamps. Previously X-Git-Tag: REL7_4_BETA1~1206 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=feefc329bd64ee552d12c8e40bed76e3573c3fa4;p=postgresql Patch from Florian Wunderlich to correctly support java Timestamps. Previously 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 --- diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java index f75ab70d16..48e8b8f70b 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java @@ -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) { diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/TimestampTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/TimestampTest.java index 2472a3d3d8..a5307e3b42 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/TimestampTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/TimestampTest.java @@ -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";