@@ -1591,115 +1591,113 @@ public static Time toTime(String s) throws SQLException
15911591}
15921592}
15931593
1594- public static Timestamp toTimestamp (String s ,ResultSet resultSet )throws SQLException
1594+ /**
1595+ * Parse a string and return a timestamp representing its value.
1596+ *
1597+ * The driver is set to return ISO date formated strings. We modify this
1598+ * string from the ISO format to a format that Java can understand. Java
1599+ * expects timezone info as 'GMT+09:00' where as ISO gives '+09'.
1600+ * Java also expects fractional seconds to 3 places where postgres
1601+ * will give, none, 2 or 6 depending on the time and postgres version.
1602+ * From version 7.2 postgres returns fractional seconds to 6 places.
1603+ * If available, we drop the last 3 digits.
1604+ *
1605+ * @param s The ISO formated date string to parse.
1606+ * @param resultSet The ResultSet this date is part of.
1607+ *
1608+ * @return null if s is null or a timestamp of the parsed string s.
1609+ *
1610+ * @throws SQLException if there is a problem parsing s.
1611+ **/
1612+ public static Timestamp toTimestamp (String s ,ResultSet resultSet )
1613+ throws SQLException
15951614{
15961615if (s ==null )
15971616return null ;
15981617
1599- boolean subsecond ;
1600- //if string contains a '.' we have fractional seconds
1601- if (s .indexOf ('.' ) == -1 )
1602- {
1603- subsecond =false ;
1604- }
1605- else
1606- {
1607- subsecond =true ;
1608- }
1609-
1610- //here we are modifying the string from ISO format to a format java can understand
1611- //java expects timezone info as 'GMT-08:00' instead of '-08' in postgres ISO format
1612- //and java expects three digits if fractional seconds are present instead of two for postgres
1613- //so this code strips off timezone info and adds on the GMT+/-...
1614- //as well as adds a third digit for partial seconds if necessary
1618+ // We must be synchronized here incase more theads access the ResultSet
1619+ // bad practice but possible. Anyhow this is to protect sbuf and
1620+ // SimpleDateFormat objects
16151621synchronized (resultSet )
16161622{
1617- // We must be synchronized here incase more theads access the ResultSet
1618- // bad practice but possible. Anyhow this is to protect sbuf and
1619- // SimpleDateFormat objects
1623+ SimpleDateFormat df =null ;
16201624
1621- //First time?
1625+ //If first time, create the buffer, otherwise clear it.
16221626if (resultSet .sbuf ==null )
16231627resultSet .sbuf =new StringBuffer ();
1628+ else
1629+ resultSet .sbuf .setLength (0 );
16241630
1625- resultSet . sbuf . setLength ( 0 );
1631+ // Copy s into sbuf for parsing.
16261632resultSet .sbuf .append (s );
16271633
1628- //we are looking to see if the backend has appended on a timezone.
1629- //currently postgresql will return +/-HH:MM or +/-HH for timezone offset
1630- //(i.e. -06, or +06:30, note the expectation of the leading zero for the
1631- //hours, and the use of the : for delimiter between hours and minutes)
1632- //if the backend ISO format changes in the future this code will
1633- //need to be changed as well
1634- char sub =resultSet .sbuf .charAt (resultSet .sbuf .length () -3 );
1635- if (sub =='+' ||sub =='-' )
1634+ if (s .length () >19 )
16361635{
1637- //we have found timezone info of format +/-HH
1636+ // The len of the ISO string to the second value is 19 chars. If
1637+ // greater then 19, there should be tz info and perhaps fractional
1638+ // second info which we need to change to java to read it.
16381639
1639- resultSet .sbuf .setLength (resultSet .sbuf .length () -3 );
1640- if (subsecond )
1641- {
1642- resultSet .sbuf .append ('0' ).append ("GMT" ).append (s .substring (s .length () -3 )).append (":00" );
1643- }
1644- else
1645- {
1646- resultSet .sbuf .append ("GMT" ).append (s .substring (s .length () -3 )).append (":00" );
1647- }
1648- }
1649- else if (sub ==':' )
1650- {
1651- //we may have found timezone info of format +/-HH:MM, or there is no
1652- //timezone info at all and this is the : preceding the seconds
1653- char sub2 =resultSet .sbuf .charAt (resultSet .sbuf .length () -5 );
1654- if (sub2 =='+' ||sub2 =='-' )
1640+ // cut the copy to second value "2001-12-07 16:29:22"
1641+ int i =19 ;
1642+ resultSet .sbuf .setLength (i );
1643+
1644+ char c =s .charAt (i ++);
1645+ if (c =='.' )
16551646{
1656- //we have found timezone info of format +/-HH:MM
1657- resultSet .sbuf .setLength (resultSet .sbuf .length () -5 );
1658- if (subsecond )
1659- {
1660- resultSet .sbuf .append ('0' ).append ("GMT" ).append (s .substring (s .length () -5 ));
1661- }
1662- else
1647+ // Found a fractional value. Append up to 3 digits including
1648+ // the leading '.'
1649+ do
16631650{
1664- resultSet .sbuf .append ("GMT" ).append (s .substring (s .length () -5 ));
1665- }
1651+ if (i <24 )
1652+ resultSet .sbuf .append (c );
1653+ c =s .charAt (i ++);
1654+ }while (Character .isDigit (c ));
1655+
1656+ // If there wasn't at least 3 digits we should add some zeros
1657+ // to make up the 3 digits we tell java to expect.
1658+ for (int j =i ;j <24 ;j ++)
1659+ resultSet .sbuf .append ('0' );
16661660}
1667- else if ( subsecond )
1661+ else
16681662{
1669- resultSet .sbuf .append ('0' );
1663+ // No fractional seconds, lets add some.
1664+ resultSet .sbuf .append (".000" );
16701665}
1671- }
1672- else if (subsecond )
1673- {
1674- resultSet .sbuf .append ('0' );
1675- }
16761666
1677- // could optimize this a tad to remove too many object creations...
1678- SimpleDateFormat df =null ;
1667+ // prepend the GMT part and then add the remaining bit of
1668+ // the string.
1669+ resultSet .sbuf .append (" GMT" );
1670+ resultSet .sbuf .append (c );
1671+ resultSet .sbuf .append (s .substring (i ,s .length ()));
16791672
1680- if (resultSet .sbuf .length () >23 &&subsecond )
1681- {
1682- df =new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss.SSSzzzzzzzzz" );
1683- }
1684- else if (resultSet .sbuf .length () >23 && !subsecond )
1685- {
1686- df =new SimpleDateFormat ("yyyy-MM-dd HH:mm:sszzzzzzzzz" );
1687- }
1688- else if (resultSet .sbuf .length () >10 &&subsecond )
1689- {
1690- df =new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss.SSS" );
1673+ // Lastly, if the tz part doesn't specify the :MM part then
1674+ // we add ":00" for java.
1675+ if (s .length () -i <5 )
1676+ resultSet .sbuf .append (":00" );
1677+
1678+ // we'll use this dateformat string to parse the result.
1679+ df =new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss.SSS z" );
16911680}
1692- else if (resultSet . sbuf . length ()> 10 && ! subsecond )
1681+ else if (s . length ()== 19 )
16931682{
1683+ // No tz or fractional second info.
1684+ // I'm not sure if it is
1685+ // possible to have a string in this format, as pg
1686+ // should give us tz qualified timestamps back, but it was
1687+ // in the old code, so I'm handling it for now.
16941688df =new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" );
16951689}
16961690else
16971691{
1692+ // We must just have a date. This case is
1693+ // needed if this method is called on a date
1694+ // column
16981695df =new SimpleDateFormat ("yyyy-MM-dd" );
16991696}
17001697
17011698try
17021699{
1700+ // All that's left is to parse the string and return the ts.
17031701return new Timestamp (df .parse (resultSet .sbuf .toString ()).getTime ());
17041702}
17051703catch (ParseException e )
@@ -1708,7 +1706,5 @@ else if (resultSet.sbuf.length() > 10 && !subsecond)
17081706}
17091707}
17101708}
1711-
1712-
17131709}
17141710