You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ab...@apache.org on 2007/05/24 17:43:09 UTC

svn commit: r541333 - in /db/derby/code/trunk/java: client/org/apache/derby/client/am/DateTime.java testing/org/apache/derbyTesting/functionTests/tests/lang/TimeHandlingTest.java

Author: abrown
Date: Thu May 24 08:43:08 2007
New Revision: 541333

URL: http://svn.apache.org/viewvc?view=rev&rev=541333
Log:
DERBY-1816: ResultSet.getTime() on a SQL TIMESTAMP should retain millisecond
precision.  Patch does the following:

  1. Separates the timestamp parse logic in client/am/DateTime.java into a new
     method called "parseTimestampString()". The new method takes a timestamp
     string and a Calendar object, and sets the fields of the Calendar based on
     the fields that are parsed from the timestamp string.  The method also
     returns the parsed microseconds value since that cannot be set on a
     Calendar object (the precision of a Calendar is milliseconds).

  2. Modifies timestampBytesToTimestamp(...) to call the new method for
     parsing timestamps.

  3. Changes the timestampBytesToTime(...) method so that it now parses the
     full timestamp (via the new parseTimestampString() method) instead of
     just parsing the hours, minutes, and seconds. Then a java.sql.Time
     object is created from the Calendar object into which the timestamp
     string was parsed. This allows us to preserve the sub-second resolution
     that is parsed from the timestamp.

  4. Re-enables the relevant test case in lang/TimeHandlingTest.java so that
     it now runs in client mode. 

Modified:
    db/derby/code/trunk/java/client/org/apache/derby/client/am/DateTime.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TimeHandlingTest.java

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/DateTime.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/DateTime.java?view=diff&rev=541333&r1=541332&r2=541333
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/DateTime.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/DateTime.java Thu May 24 08:43:08 2007
@@ -166,31 +166,62 @@
         String timestamp = new String(buffer, offset, 
                 DateTime.timestampRepresentationLength,encoding);
        
+        Calendar cal = getCleanCalendar(recyclableCal);
+
+        /* java.sql.Timestamp has nanosecond precision, so we have to keep
+         * the parsed microseconds value and use that to set nanos.
+         */
+        int micros = parseTimestampString(timestamp, cal);
+        java.sql.Timestamp ts = new java.sql.Timestamp(cal.getTimeInMillis());
+        ts.setNanos(micros * 1000);
+        return ts;
+    }
+
+    /**
+     * Parse a String of the form <code>yyyy-mm-dd-hh.mm.ss.ffffff</code>
+     * and store the various fields into the received Calendar object.
+     *
+     * @param timestamp Timestamp value to parse, as a String.
+     * @param cal Calendar into which to store the parsed fields.  Should
+     *  not be null.
+     *
+     * @return The microseconds field as parsed from the timestamp string.
+     *  This cannot be set in the Calendar object but we still want to
+     *  preserve the value, in case the caller needs it (for example, to
+     *  create a java.sql.Timestamp with microsecond precision).
+     */
+    private static int parseTimestampString(String timestamp,
+        Calendar cal)
+    {
         int zeroBase = ((int) '0');
 
-        year =
+        cal.set(Calendar.YEAR,
                 1000 * (((int) timestamp.charAt(0)) - zeroBase) +
                 100 * (((int) timestamp.charAt(1)) - zeroBase) +
                 10 * (((int) timestamp.charAt(2)) - zeroBase) +
-                (((int) timestamp.charAt(3)) - zeroBase);
+                (((int) timestamp.charAt(3)) - zeroBase));
 
-        month =
+        cal.set(Calendar.MONTH,
                 10 * (((int) timestamp.charAt(5)) - zeroBase) +
-                (((int) timestamp.charAt(6)) - zeroBase) -
-                1;
-        day =
+                (((int) timestamp.charAt(6)) - zeroBase) - 1);
+
+        cal.set(Calendar.DAY_OF_MONTH,
                 10 * (((int) timestamp.charAt(8)) - zeroBase) +
-                (((int) timestamp.charAt(9)) - zeroBase);
-        hour =
+                (((int) timestamp.charAt(9)) - zeroBase));
+
+        cal.set(Calendar.HOUR,
                 10 * (((int) timestamp.charAt(11)) - zeroBase) +
-                (((int) timestamp.charAt(12)) - zeroBase);
-        minute =
+                (((int) timestamp.charAt(12)) - zeroBase));
+
+        cal.set(Calendar.MINUTE,
                 10 * (((int) timestamp.charAt(14)) - zeroBase) +
-                (((int) timestamp.charAt(15)) - zeroBase);
-        second =
+                (((int) timestamp.charAt(15)) - zeroBase));
+
+        cal.set(Calendar.SECOND,
                 10 * (((int) timestamp.charAt(17)) - zeroBase) +
-                (((int) timestamp.charAt(18)) - zeroBase);
-        fraction =
+                (((int) timestamp.charAt(18)) - zeroBase));
+
+        int micros = 
                 100000 * (((int) timestamp.charAt(20)) - zeroBase) +
                 10000 * (((int) timestamp.charAt(21)) - zeroBase) +
                 1000 * (((int) timestamp.charAt(22)) - zeroBase) +
@@ -198,11 +229,12 @@
                 10 * (((int) timestamp.charAt(24)) - zeroBase) +
                 (((int) timestamp.charAt(25)) - zeroBase);
 
-        Calendar cal = getCleanCalendar(recyclableCal);
-        cal.set(year, month, day, hour, minute, second);
-        java.sql.Timestamp ts = new java.sql.Timestamp(cal.getTimeInMillis());
-        ts.setNanos(fraction * 1000);
-        return ts;
+        /* The "ffffff" that we parsed is microseconds.  In order to
+         * capture that information inside of the MILLISECOND field
+         * we have to divide by 1000.
+         */
+        cal.set(Calendar.MILLISECOND, micros / 1000);
+        return micros;
     }
 
     // ********************************************************
@@ -529,25 +561,38 @@
                                                            int offset,
                                                            Calendar recyclableCal, 
                                                            String encoding) 
-    throws  UnsupportedEncodingException {
-        int hour, minute, second;
-
+    throws  UnsupportedEncodingException
+    {
+        /* When getting a java.sql.Time object from a TIMESTAMP value we
+         * need to preserve the milliseconds from the timestamp.
+         * 
+         * Note: a Derby SQL TIME value has by definition resolution of only
+         * a second so its millisecond value is always zero.  However,
+         * java.sql.Time is not a direct mapping to the SQL Type; rather, it's
+         * a JDBC type, and the JDBC java.sql.Time class has a precision of
+         * milliseconds.  So when converting from a SQL TIMESTAMP we should
+         * retain the millisecond precision.  DERBY-1816.
+         *
+         * In order to accomplish this we parse *all* fields of the timestamp
+         * into a Calendar object, then create the java.sql.Time object from
+         * that Calendar. This allows us to preserve the sub-second resolution
+         * that is parsed from the timestamp. 
+         */
+ 
         String timestamp = new String(buffer, offset, 
                 DateTime.timestampRepresentationLength, encoding);
-        int zeroBase = ((int) '0');
-
-        hour =
-                10 * (((int) timestamp.charAt(11)) - zeroBase) +
-                (((int) timestamp.charAt(12)) - zeroBase);
-        minute =
-                10 * (((int) timestamp.charAt(14)) - zeroBase) +
-                (((int) timestamp.charAt(15)) - zeroBase);
-        second =
-                10 * (((int) timestamp.charAt(17)) - zeroBase) +
-                (((int) timestamp.charAt(18)) - zeroBase);
-
+       
         Calendar cal = getCleanCalendar(recyclableCal);
-        cal.set(1970, Calendar.JANUARY, 1, hour, minute, second);
+
+        /* Note that "parseTimestampString()" returns microseconds but we
+         * ignore micros because java.sql.Time only has millisecond precision.
+         */
+        parseTimestampString(timestamp, cal);
+
+        /* Java API indicates that the date components of a Time value
+         * must be set to January 1, 1970. So override those values now.
+         */
+        cal.set(1970, Calendar.JANUARY, 1);
         return new java.sql.Time(cal.getTimeInMillis());
     }
 

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TimeHandlingTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TimeHandlingTest.java?view=diff&rev=541333&r1=541332&r2=541333
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TimeHandlingTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TimeHandlingTest.java Thu May 24 08:43:08 2007
@@ -703,10 +703,7 @@
         assertTime1970(tv);
 
         // Check the TIME portion is set to the same as tv
-        // DERBY-1816 java.sql.Time values from TIMESTAMP
-        // colummns lose their precision with client.
-        if (!usingDerbyNetClient())
-            assertTimeEqual(tv, tsv);
+        assertTimeEqual(tv, tsv);
                
         String sv = rs.getString(column);
         assertNotNull(sv);