Re: (2) patch against cvs for getTimestamp() problem.

Поиск
Список
Период
Сортировка
От Barry Lind
Тема Re: (2) patch against cvs for getTimestamp() problem.
Дата
Msg-id 3C1595CA.3010209@xythos.com
обсуждение исходный текст
Ответ на patch against cvs for getTimestamp() problem.  ("Thomas O'Dowd" <tom@nooper.com>)
Ответы Re: (2) patch against cvs for getTimestamp() problem.  ("Thomas O'Dowd" <tom@nooper.com>)
Список pgsql-jdbc
Thomas,

I have reviewed this patch in detail (I thought I found a problem with
it, but I was wrong :-) ).  The only change I made was to your comments
in the code.  There is a reason that the YYYY-MM-DD format is supported
(according to the jdbc spec you can call getTimestamp() on a date
column).  I have tested both the jdbc1 and jdbc2 builds on my code base
and it works for me.

Given how close we are to the 7.2 release, I would appreciate it if
others tested any timestamp code they might have.  I have applied the
patch and built new jar files and placed them on the website.

thanks,
--Barry



Thomas O'Dowd wrote:

> As a followup to this I noticed a small typo in the jdbc1 part of the
> patch which is fixed in this patch. So please ignore the first patch
> that I sent. I'd like to add that the jdbc2 part is tested as far as
> I can but someone should briefly test the jdbc1 part as I haven't
> the environment to test that here.
>
> Regards,
>
> Tom.
>
> On Sat, Dec 08, 2001 at 02:40:51PM +0900, Thomas O'Dowd wrote:
>
>>Hi all,
>>
>>I just upgraded my development environment to 7.2b3 and the latest cvs
>>driver both built from source. I found that ResultSet.getTimestamp()
>>was no longer working against a 7.2 database and causing an exception
>>parsing the timestamp that pg was returning.
>>
>>Bad Timestamp Format at 23 in 2001-12-06 23:24:07.895882+09
>>    at org.postgresql.jdbc2.ResultSet.toTimestamp(ResultSet.java:1707)
>>    at org.postgresql.jdbc2.ResultSet.getTimestamp(ResultSet.java:398)
>>
>>It seems that timestamps have become more accurate in 7.2 going from
>>2 fractional second digits to 6 which was causing the exception. Looking
>>at the code in toTimestamp() I decided that it was better to rewrite
>>it, then add a small fix as the method of parsing before was error
>>prone in my opinion and the new code (less the comments) is smaller
>>and easier to read IMHO :)
>>
>>I'm including patches against jdbc1 and jdbc2 ResultSet.java. I generated
>>the patch against the latest cvs, using cvs diff -c. I've tested the
>>jdbc2 versions pretty well and it parses the following combinations
>>
>>"2001-12-07 16:29:22.47+09",
>>"2001-12-07 16:29:26+09",
>>"2001-12-07 16:26:50.144213+09",
>>"2001-12-07 16:26:59.461349+09",
>>"2001-12-07 16:26:59.461349+05:30",
>>"2001-12-07 00:00:00+09",
>>"2001-12-07 12:00:00",
>>"2001-12-07"
>>
>>The driver is set to return ISO datestyle formats so I'm not sure if the
>>latter two formats are required but as the older code supported them, I've
>>added code to do this. If anyone can suggest more formats that pg may
>>return I can test those on the new code too. I've been running the new
>>driver with my code with no problem now.
>>
>>If anyone has any questions or suggestions please let me know. I think
>>this is an important bug fix for the 7.2 release of pg. Can someone test
>>the changes in the jdbc1 driver as I don't have a means of doing this.
>>
>>The patch file is attached.
>>
>>Regards,
>>
>>Tom.
>>--
>>Thomas O'Dowd. - Nooping - http://nooper.com
>>tom@nooper.com - Testing - http://nooper.co.jp/labs
>>
>>
>>------------------------------------------------------------------------
>>
>>Index: src/interfaces/jdbc/org/postgresql/jdbc1/ResultSet.java
>>===================================================================
>>RCS file: /projects/cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/ResultSet.java,v
>>retrieving revision 1.27
>>diff -c -r1.27 ResultSet.java
>>*** src/interfaces/jdbc/org/postgresql/jdbc1/ResultSet.java    2001/11/25 23:26:59    1.27
>>--- src/interfaces/jdbc/org/postgresql/jdbc1/ResultSet.java    2001/12/08 05:18:03
>>***************
>>*** 492,497 ****
>>--- 492,505 ----
>>       * 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
>>       * @exception SQLException if a database access error occurs
>>***************
>>*** 499,600 ****
>>      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;
>>!         }
>>
>>!         //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 == '-')
>>!         {
>>!             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 == '-')
>>              {
>>!                 //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
>>                  {
>>!                     strBuf.append("GMT").append(s.substring(s.length() - 5));
>>!                 }
>>              }
>>!             else if (subsecond)
>>              {
>>!                 strBuf.append('0');
>>              }
>>-         }
>>-         else if (subsecond)
>>-         {
>>-             strBuf = strBuf.append('0');
>>-         }
>>
>>!         s = strBuf.toString();
>>!
>>!         SimpleDateFormat df = null;
>>!
>>!         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");
>>!         }
>>!         else if (s.length() > 10 && !subsecond)
>>!         {
>>              df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
>>          }
>>          else
>>          {
>>              df = new SimpleDateFormat("yyyy-MM-dd");
>>          }
>>
>>          try
>>          {
>>!             return new Timestamp(df.parse(s).getTime());
>>          }
>>          catch (ParseException e)
>>          {
>>--- 507,593 ----
>>      public Timestamp getTimestamp(int columnIndex) throws SQLException
>>      {
>>          String s = getString(columnIndex);
>>+
>>          if (s == null)
>>              return null;
>>
>>!         StringBuffer sbuf = new StringBuffer(s);
>>!         SimpleDateFormat df = null;
>>
>>!         if (s.length() > 19)
>>          {
>>!             // 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 == '.')
>>              {
>>!                 // Found a fractional value. Append up to 3 digits including
>>!                 // the leading '.'
>>!                 do
>>                  {
>>!                     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
>>              {
>>!                 // No fractional seconds, lets add some.
>>!                 sbuf.append(".000");
>>              }
>>
>>!             // 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()));
>>!
>>!             // 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");
>>!
>>!             // 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() == 19)
>>!         {
>>!             // No tz or fractional second info. We could add some, but
>>!             // then we have to figure out what tz we're in so its easier
>>!             // to just let Java do that for us. I'm not sure if this is
>>!             // possible to have to parse 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 a date. Again, I wonder if we have to handle
>>+             // this case, but it was in the old code and I've no time
>>+             // to figure it out.
>>              df = new SimpleDateFormat("yyyy-MM-dd");
>>          }
>>
>>+         // NOTE: if we don't have to handle the last two cases, then
>>+         // we can just initialise df to the first string in a resultset
>>+         // and use that throughout without having to create an object
>>+         // everytime. Leave this for now.
>>+
>>          try
>>          {
>>!             // 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: src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java
>>===================================================================
>>RCS file: /projects/cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java,v
>>retrieving revision 1.45
>>diff -c -r1.45 ResultSet.java
>>*** src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java    2001/11/25 23:26:59    1.45
>>--- src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java    2001/12/08 05:18:04
>>***************
>>*** 1591,1705 ****
>>          }
>>      }
>>
>>!     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
>>          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
>>
>>!             // First time?
>>              if (resultSet.sbuf == null)
>>                  resultSet.sbuf = new StringBuffer();
>>
>>!             resultSet.sbuf.setLength(0);
>>              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 == '-')
>>              {
>>!                 //we have found timezone info of format +/-HH
>>
>>!                 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 == '-')
>>                  {
>>!                     //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
>>!                     {
>>!                         resultSet.sbuf.append("GMT").append(s.substring(s.length() - 5));
>>!                     }
>>                  }
>>!                 else if (subsecond)
>>                  {
>>!                     resultSet.sbuf.append('0');
>>                  }
>>-             }
>>-             else if (subsecond)
>>-             {
>>-                 resultSet.sbuf.append('0');
>>-             }
>>-
>>-             // could optimize this a tad to remove too many object creations...
>>-             SimpleDateFormat df = null;
>>
>>!             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");
>>!             }
>>!             else if (resultSet.sbuf.length() > 10 && !subsecond)
>>!             {
>>                  df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
>>              }
>>              else
>>              {
>>                  df = new SimpleDateFormat("yyyy-MM-dd");
>>              }
>>
>>              try
>>              {
>>                  return new Timestamp(df.parse(resultSet.sbuf.toString()).getTime());
>>              }
>>              catch (ParseException e)
>>--- 1591,1709 ----
>>          }
>>      }
>>
>>!     /**
>>!     * 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;
>>
>>!         // 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)
>>          {
>>!             SimpleDateFormat df = null;
>>
>>!             // If first time, create the buffer, otherwise clear it.
>>              if (resultSet.sbuf == null)
>>                  resultSet.sbuf = new StringBuffer();
>>+             else
>>+                 resultSet.sbuf.setLength(0);
>>
>>!             // Copy s into sbuf for parsing.
>>              resultSet.sbuf.append(s);
>>
>>!             if (s.length() > 19)
>>              {
>>!                 // 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;
>>!                 resultSet.sbuf.setLength(i);
>>
>>!                 char c = s.charAt(i++);
>>!                 if (c == '.')
>>                  {
>>!                     // Found a fractional value. Append up to 3 digits including
>>!                     // the leading '.'
>>!                     do
>>                      {
>>!                         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
>>                  {
>>!                     // No fractional seconds, lets add some.
>>!                     resultSet.sbuf.append(".000");
>>                  }
>>
>>!                 // 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()));
>>!
>>!                 // 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 (s.length() == 19)
>>!             {
>>!                 // No tz or fractional second info. We could add some, but
>>!                 // then we have to figure out what tz we're in so its easier
>>!                 // to just let Java do that for us. I'm not sure if this is
>>!                 // possible to have to parse 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 a date. Again, I wonder if we have to handle
>>+                 // this case, but it was in the old code and I've no time
>>+                 // to figure it out.
>>                  df = new SimpleDateFormat("yyyy-MM-dd");
>>              }
>>
>>+             // NOTE: if we don't have to handle the last two cases, then
>>+             // we can just initialise df to the first string in a resultset
>>+             // and use that throughout without having to create an object
>>+             // everytime. Leave this for now.
>>+
>>              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,1714 ****
>>              }
>>          }
>>      }
>>-
>>-
>>  }
>>
>>--- 1712,1716 ----
>>
>>
>>------------------------------------------------------------------------
>>
>>
>>---------------------------(end of broadcast)---------------------------
>>TIP 5: Have you checked our extensive FAQ?
>>
>>http://www.postgresql.org/users-lounge/docs/faq.html
>>



В списке pgsql-jdbc по дате отправления:

Предыдущее
От: Barry Lind
Дата:
Сообщение: Re: Bug with caching SQLTypes in Connection:getSQLType(oid)
Следующее
От: Barry Lind
Дата:
Сообщение: Re: JDBC 3.0 / JDK 1.4 build issues