@@ -1591,115 +1591,113 @@ public static Time toTime(String s) throws SQLException
1591
1591
}
1592
1592
}
1593
1593
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
1595
1614
{
1596
1615
if (s == null )
1597
1616
return null ;
1598
1617
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
1615
1621
synchronized (resultSet )
1616
1622
{
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 ;
1620
1624
1621
- // First time?
1625
+ // If first time, create the buffer, otherwise clear it.
1622
1626
if (resultSet .sbuf == null )
1623
1627
resultSet .sbuf = new StringBuffer ();
1628
+ else
1629
+ resultSet .sbuf .setLength (0 );
1624
1630
1625
- resultSet . sbuf . setLength ( 0 );
1631
+ // Copy s into sbuf for parsing.
1626
1632
resultSet .sbuf .append (s );
1627
1633
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 )
1636
1635
{
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.
1638
1639
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 == '.' )
1655
1646
{
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
1663
1650
{
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' );
1666
1660
}
1667
- else if ( subsecond )
1661
+ else
1668
1662
{
1669
- resultSet .sbuf .append ('0' );
1663
+ // No fractional seconds, lets add some.
1664
+ resultSet .sbuf .append (".000" );
1670
1665
}
1671
- }
1672
- else if (subsecond )
1673
- {
1674
- resultSet .sbuf .append ('0' );
1675
- }
1676
1666
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 ()));
1679
1672
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" );
1691
1680
}
1692
- else if (resultSet . sbuf . length () > 10 && ! subsecond )
1681
+ else if (s . length () == 19 )
1693
1682
{
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.
1694
1688
df = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" );
1695
1689
}
1696
1690
else
1697
1691
{
1692
+ // We must just have a date. This case is
1693
+ // needed if this method is called on a date
1694
+ // column
1698
1695
df = new SimpleDateFormat ("yyyy-MM-dd" );
1699
1696
}
1700
1697
1701
1698
try
1702
1699
{
1700
+ // All that's left is to parse the string and return the ts.
1703
1701
return new Timestamp (df .parse (resultSet .sbuf .toString ()).getTime ());
1704
1702
}
1705
1703
catch (ParseException e )
@@ -1708,7 +1706,5 @@ else if (resultSet.sbuf.length() > 10 && !subsecond)
1708
1706
}
1709
1707
}
1710
1708
}
1711
-
1712
-
1713
1709
}
1714
1710
0 commit comments