]> granicus.if.org Git - postgresql/commitdiff
Applied patch from Thomas O'Dowd that fixes timestamp parsing. The jdbc code
authorBarry Lind <barry@xythos.com>
Tue, 11 Dec 2001 04:48:05 +0000 (04:48 +0000)
committerBarry Lind <barry@xythos.com>
Tue, 11 Dec 2001 04:48:05 +0000 (04:48 +0000)
wasn't updated to handle more than two decimal digits for fractional seconds
that now are possible in 7.2.  This patch fixes the timestamp parsing logic.
I have built and tested on both jdbc1 and jdbc2.

src/interfaces/jdbc/org/postgresql/jdbc1/ResultSet.java
src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java

index 7fea1dab6bea3c3d485b324cb4c418753714a937..b20d2f8a880ec4be51f78711aff11ca52c8b3a47 100644 (file)
@@ -491,6 +491,14 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu
        /*
         * Get the value of a column in the current row as a
         * java.sql.Timestamp object
+        *
+        * The driver is set to return ISO date formated strings. We modify this 
+    * string from the ISO format to a format that Java can understand. Java
+    * expects timezone info as 'GMT+09:00' where as ISO gives '+09'.
+    * 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.
         *
         * @param columnIndex the first column is 1, the second is 2...
         * @return the column value; null if SQL NULL
@@ -499,102 +507,80 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu
        public Timestamp getTimestamp(int columnIndex) throws SQLException
        {
                String s = getString(columnIndex);
+
                if (s == null)
                        return null;
 
-               boolean subsecond;
-               //if string contains a '.' we have fractional seconds
-               if (s.indexOf('.') == -1)
-               {
-                       subsecond = false;
-               }
-               else
-               {
-                       subsecond = true;
-               }
+               StringBuffer sbuf = new StringBuffer(s);
+               SimpleDateFormat df = null;
 
-               //here we are modifying the string from ISO format to a format java can understand
-               //java expects timezone info as 'GMT-08:00' instead of '-08' in postgres ISO format
-               //and java expects three digits if fractional seconds are present instead of two for postgres
-               //so this code strips off timezone info and adds on the GMT+/-...
-               //as well as adds a third digit for partial seconds if necessary
-               StringBuffer strBuf = new StringBuffer(s);
-
-               //we are looking to see if the backend has appended on a timezone.
-               //currently postgresql will return +/-HH:MM or +/-HH for timezone offset
-               //(i.e. -06, or +06:30, note the expectation of the leading zero for the
-               //hours, and the use of the : for delimiter between hours and minutes)
-               //if the backend ISO format changes in the future this code will
-               //need to be changed as well
-               char sub = strBuf.charAt(strBuf.length() - 3);
-               if (sub == '+' || sub == '-')
+               if (s.length() > 19)
                {
-                       strBuf.setLength(strBuf.length() - 3);
-                       if (subsecond)
-                       {
-                               strBuf.append('0').append("GMT").append(s.substring(s.length() - 3, s.length())).append(":00");
-                       }
-                       else
-                       {
-                               strBuf.append("GMT").append(s.substring(s.length() - 3, s.length())).append(":00");
-                       }
-               }
-               else if (sub == ':')
-               {
-                       //we may have found timezone info of format +/-HH:MM, or there is no
-                       //timezone info at all and this is the : preceding the seconds
-                       char sub2 = strBuf.charAt(strBuf.length() - 5);
-                       if (sub2 == '+' || sub2 == '-')
+                       // The len of the ISO string to the second value is 19 chars. If
+                       // greater then 19, there should be tz info and perhaps fractional
+                       // second info which we need to change to java to read it.
+
+                       // cut the copy to second value "2001-12-07 16:29:22"
+                       int i = 19;
+                       sbuf.setLength(i);
+
+                       char c = s.charAt(i++);
+                       if (c == '.')
                        {
-                               //we have found timezone info of format +/-HH:MM
-                               strBuf.setLength(strBuf.length() - 5);
-                               if (subsecond)
-                               {
-                                       strBuf.append('0').append("GMT").append(s.substring(s.length() - 5));
-                               }
-                               else
+                               // Found a fractional value. Append up to 3 digits including
+                               // the leading '.'
+                               do
                                {
-                                       strBuf.append("GMT").append(s.substring(s.length() - 5));
-                               }
+                                       if (i < 24)
+                                               sbuf.append(c);
+                                       c = s.charAt(i++);
+                               } while (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++)
+                                       sbuf.append('0');
                        }
-                       else if (subsecond)
+                       else
                        {
-                               strBuf.append('0');
+                               // No fractional seconds, lets add some.
+                               sbuf.append(".000");
                        }
-               }
-               else if (subsecond)
-               {
-                       strBuf = strBuf.append('0');
-               }
 
-               s = strBuf.toString();
+                       // prepend the GMT part and then add the remaining bit of
+                       // the string.
+                       sbuf.append(" GMT");
+                       sbuf.append(c);
+                       sbuf.append(s.substring(i, s.length()));
 
-               SimpleDateFormat df = null;
+                       // Lastly, if the tz part doesn't specify the :MM part then
+                       // we add ":00" for java.
+                       if (s.length() - i < 5)
+                               sbuf.append(":00");
 
-               if (s.length() > 23 && subsecond)
-               {
-                       df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSzzzzzzzzz");
-               }
-               else if (s.length() > 23 && !subsecond)
-               {
-                       df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzzzzzzzz");
-               }
-               else if (s.length() > 10 && subsecond)
-               {
-                       df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+                       // we'll use this dateformat string to parse the result.
+                       df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
                }
-               else if (s.length() > 10 && !subsecond)
+               else if (s.length() == 19)
                {
+                       // No tz or fractional second info. 
+                       // I'm not sure if it is
+                       // possible to have a string in this format, as pg
+                       // should give us tz qualified timestamps back, but it was
+                       // in the old code, so I'm handling it for now.
                        df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                }
                else
                {
+                       // We must just have a date. This case is
+                       // needed if this method is called on a date column
                        df = new SimpleDateFormat("yyyy-MM-dd");
                }
 
                try
                {
-                       return new Timestamp(df.parse(s).getTime());
+                       // All that's left is to parse the string and return the ts.
+                       return new Timestamp(df.parse(sbuf.toString()).getTime());
                }
                catch (ParseException e)
                {
index 66e7b4d3fac77dcbaa8413754415a365f12bd6bc..565847db9f16f4ab0c56693d85a6bf4a9e4b7e88 100644 (file)
@@ -1591,115 +1591,113 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu
                }
        }
 
-       public static Timestamp toTimestamp(String s, ResultSet resultSet) throws SQLException
+       /**
+       * Parse a string and return a timestamp representing its value.
+       *
+       * The driver is set to return ISO date formated strings. We modify this 
+       * string from the ISO format to a format that Java can understand. Java
+       * expects timezone info as 'GMT+09:00' where as ISO gives '+09'.
+       * 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.
+       *
+       * @param s         The ISO formated date string to parse.
+       * @param resultSet The ResultSet this date is part of.
+       *
+       * @return null if s is null or a timestamp of the parsed string s.
+       *
+       * @throws SQLException if there is a problem parsing s.
+       **/
+       public static Timestamp toTimestamp(String s, ResultSet resultSet)
+       throws SQLException
        {
                if (s == null)
                        return null;
 
-               boolean subsecond;
-               //if string contains a '.' we have fractional seconds
-               if (s.indexOf('.') == -1)
-               {
-                       subsecond = false;
-               }
-               else
-               {
-                       subsecond = true;
-               }
-
-               //here we are modifying the string from ISO format to a format java can understand
-               //java expects timezone info as 'GMT-08:00' instead of '-08' in postgres ISO format
-               //and java expects three digits if fractional seconds are present instead of two for postgres
-               //so this code strips off timezone info and adds on the GMT+/-...
-               //as well as adds a third digit for partial seconds if necessary
+               // We must be synchronized here incase more theads access the ResultSet
+               // bad practice but possible. Anyhow this is to protect sbuf and
+               // SimpleDateFormat objects
                synchronized (resultSet)
                {
-                       // We must be synchronized here incase more theads access the ResultSet
-                       // bad practice but possible. Anyhow this is to protect sbuf and
-                       // SimpleDateFormat objects
+                       SimpleDateFormat df = null;
 
-                       // First time?
+                       // If first time, create the buffer, otherwise clear it.
                        if (resultSet.sbuf == null)
                                resultSet.sbuf = new StringBuffer();
+                       else
+                               resultSet.sbuf.setLength(0);
 
-                       resultSet.sbuf.setLength(0);
+                       // Copy s into sbuf for parsing.
                        resultSet.sbuf.append(s);
 
-                       //we are looking to see if the backend has appended on a timezone.
-                       //currently postgresql will return +/-HH:MM or +/-HH for timezone offset
-                       //(i.e. -06, or +06:30, note the expectation of the leading zero for the
-                       //hours, and the use of the : for delimiter between hours and minutes)
-                       //if the backend ISO format changes in the future this code will
-                       //need to be changed as well
-                       char sub = resultSet.sbuf.charAt(resultSet.sbuf.length() - 3);
-                       if (sub == '+' || sub == '-')
+                       if (s.length() > 19)
                        {
-                               //we have found timezone info of format +/-HH
+                               // The len of the ISO string to the second value is 19 chars. If
+                               // greater then 19, there should be tz info and perhaps fractional
+                               // second info which we need to change to java to read it.
 
-                               resultSet.sbuf.setLength(resultSet.sbuf.length() - 3);
-                               if (subsecond)
-                               {
-                                       resultSet.sbuf.append('0').append("GMT").append(s.substring(s.length() - 3)).append(":00");
-                               }
-                               else
-                               {
-                                       resultSet.sbuf.append("GMT").append(s.substring(s.length() - 3)).append(":00");
-                               }
-                       }
-                       else if (sub == ':')
-                       {
-                               //we may have found timezone info of format +/-HH:MM, or there is no
-                               //timezone info at all and this is the : preceding the seconds
-                               char sub2 = resultSet.sbuf.charAt(resultSet.sbuf.length() - 5);
-                               if (sub2 == '+' || sub2 == '-')
+                               // cut the copy to second value "2001-12-07 16:29:22"
+                               int i = 19;
+                               resultSet.sbuf.setLength(i);
+
+                               char c = s.charAt(i++);
+                               if (c == '.')
                                {
-                                       //we have found timezone info of format +/-HH:MM
-                                       resultSet.sbuf.setLength(resultSet.sbuf.length() - 5);
-                                       if (subsecond)
-                                       {
-                                               resultSet.sbuf.append('0').append("GMT").append(s.substring(s.length() - 5));
-                                       }
-                                       else
+                                       // Found a fractional value. Append up to 3 digits including
+                                       // the leading '.'
+                                       do
                                        {
-                                               resultSet.sbuf.append("GMT").append(s.substring(s.length() - 5));
-                                       }
+                                               if (i < 24)
+                                                       resultSet.sbuf.append(c);
+                                               c = s.charAt(i++);
+                                       } while (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++)
+                                               resultSet.sbuf.append('0');
                                }
-                               else if (subsecond)
+                               else
                                {
-                                       resultSet.sbuf.append('0');
+                                       // No fractional seconds, lets add some.
+                                       resultSet.sbuf.append(".000");
                                }
-                       }
-                       else if (subsecond)
-                       {
-                               resultSet.sbuf.append('0');
-                       }
 
-                       // could optimize this a tad to remove too many object creations...
-                       SimpleDateFormat df = null;
+                               // prepend the GMT part and then add the remaining bit of
+                               // the string.
+                               resultSet.sbuf.append(" GMT");
+                               resultSet.sbuf.append(c);
+                               resultSet.sbuf.append(s.substring(i, s.length()));
 
-                       if (resultSet.sbuf.length() > 23 && subsecond)
-                       {
-                               df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSzzzzzzzzz");
-                       }
-                       else if (resultSet.sbuf.length() > 23 && !subsecond)
-                       {
-                               df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzzzzzzzz");
-                       }
-                       else if (resultSet.sbuf.length() > 10 && subsecond)
-                       {
-                               df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+                               // Lastly, if the tz part doesn't specify the :MM part then
+                               // we add ":00" for java.
+                               if (s.length() - i < 5)
+                                       resultSet.sbuf.append(":00");
+
+                               // we'll use this dateformat string to parse the result.
+                               df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
                        }
-                       else if (resultSet.sbuf.length() > 10 && !subsecond)
+                       else if (s.length() == 19)
                        {
+                               // No tz or fractional second info. 
+                               // I'm not sure if it is
+                               // possible to have a string in this format, as pg
+                               // should give us tz qualified timestamps back, but it was
+                               // in the old code, so I'm handling it for now.
                                df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        }
                        else
                        {
+                               // We must just have a date. This case is 
+                               // needed if this method is called on a date
+                               // column
                                df = new SimpleDateFormat("yyyy-MM-dd");
                        }
 
                        try
                        {
+                               // All that's left is to parse the string and return the ts.
                                return new Timestamp(df.parse(resultSet.sbuf.toString()).getTime());
                        }
                        catch (ParseException e)
@@ -1708,7 +1706,5 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu
                        }
                }
        }
-
-
 }