Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3dd85bc

Browse files
author
Barry Lind
committed
Applied patch from Thomas O'Dowd that fixes timestamp parsing. The jdbc code
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.
1 parent 45a6343 commit 3dd85bc

File tree

2 files changed

+133
-151
lines changed

2 files changed

+133
-151
lines changed

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

Lines changed: 58 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,14 @@ public Time getTime(int columnIndex) throws SQLException
491491
/*
492492
* Get the value of a column in the current row as a
493493
* java.sql.Timestamp object
494+
*
495+
* The driver is set to return ISO date formated strings. We modify this
496+
* string from the ISO format to a format that Java can understand. Java
497+
* expects timezone info as 'GMT+09:00' where as ISO gives '+09'.
498+
* Java also expects fractional seconds to 3 places where postgres
499+
* will give, none, 2 or 6 depending on the time and postgres version.
500+
* From version 7.2 postgres returns fractional seconds to 6 places.
501+
* If available, we drop the last 3 digits.
494502
*
495503
* @param columnIndex the first column is 1, the second is 2...
496504
* @return the column value; null if SQL NULL
@@ -499,102 +507,80 @@ public Time getTime(int columnIndex) throws SQLException
499507
public Timestamp getTimestamp(int columnIndex) throws SQLException
500508
{
501509
String s = getString(columnIndex);
510+
502511
if (s == null)
503512
return null;
504513

505-
boolean subsecond;
506-
//if string contains a '.' we have fractional seconds
507-
if (s.indexOf('.') == -1)
508-
{
509-
subsecond = false;
510-
}
511-
else
512-
{
513-
subsecond = true;
514-
}
514+
StringBuffer sbuf = new StringBuffer(s);
515+
SimpleDateFormat df = null;
515516

516-
//here we are modifying the string from ISO format to a format java can understand
517-
//java expects timezone info as 'GMT-08:00' instead of '-08' in postgres ISO format
518-
//and java expects three digits if fractional seconds are present instead of two for postgres
519-
//so this code strips off timezone info and adds on the GMT+/-...
520-
//as well as adds a third digit for partial seconds if necessary
521-
StringBuffer strBuf = new StringBuffer(s);
522-
523-
//we are looking to see if the backend has appended on a timezone.
524-
//currently postgresql will return +/-HH:MM or +/-HH for timezone offset
525-
//(i.e. -06, or +06:30, note the expectation of the leading zero for the
526-
//hours, and the use of the : for delimiter between hours and minutes)
527-
//if the backend ISO format changes in the future this code will
528-
//need to be changed as well
529-
char sub = strBuf.charAt(strBuf.length() - 3);
530-
if (sub == '+' || sub == '-')
517+
if (s.length() > 19)
531518
{
532-
strBuf.setLength(strBuf.length() - 3);
533-
if (subsecond)
534-
{
535-
strBuf.append('0').append("GMT").append(s.substring(s.length() - 3, s.length())).append(":00");
536-
}
537-
else
538-
{
539-
strBuf.append("GMT").append(s.substring(s.length() - 3, s.length())).append(":00");
540-
}
541-
}
542-
else if (sub == ':')
543-
{
544-
//we may have found timezone info of format +/-HH:MM, or there is no
545-
//timezone info at all and this is the : preceding the seconds
546-
char sub2 = strBuf.charAt(strBuf.length() - 5);
547-
if (sub2 == '+' || sub2 == '-')
519+
// The len of the ISO string to the second value is 19 chars. If
520+
// greater then 19, there should be tz info and perhaps fractional
521+
// second info which we need to change to java to read it.
522+
523+
// cut the copy to second value "2001-12-07 16:29:22"
524+
int i = 19;
525+
sbuf.setLength(i);
526+
527+
char c = s.charAt(i++);
528+
if (c == '.')
548529
{
549-
//we have found timezone info of format +/-HH:MM
550-
strBuf.setLength(strBuf.length() - 5);
551-
if (subsecond)
552-
{
553-
strBuf.append('0').append("GMT").append(s.substring(s.length() - 5));
554-
}
555-
else
530+
// Found a fractional value. Append up to 3 digits including
531+
// the leading '.'
532+
do
556533
{
557-
strBuf.append("GMT").append(s.substring(s.length() - 5));
558-
}
534+
if (i < 24)
535+
sbuf.append(c);
536+
c = s.charAt(i++);
537+
} while (Character.isDigit(c));
538+
539+
// If there wasn't at least 3 digits we should add some zeros
540+
// to make up the 3 digits we tell java to expect.
541+
for (int j = i; j < 24; j++)
542+
sbuf.append('0');
559543
}
560-
else if (subsecond)
544+
else
561545
{
562-
strBuf.append('0');
546+
// No fractional seconds, lets add some.
547+
sbuf.append(".000");
563548
}
564-
}
565-
else if (subsecond)
566-
{
567-
strBuf = strBuf.append('0');
568-
}
569549

570-
s = strBuf.toString();
550+
// prepend the GMT part and then add the remaining bit of
551+
// the string.
552+
sbuf.append(" GMT");
553+
sbuf.append(c);
554+
sbuf.append(s.substring(i, s.length()));
571555

572-
SimpleDateFormat df = null;
556+
// Lastly, if the tz part doesn't specify the :MM part then
557+
// we add ":00" for java.
558+
if (s.length() - i < 5)
559+
sbuf.append(":00");
573560

574-
if (s.length() > 23 && subsecond)
575-
{
576-
df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSzzzzzzzzz");
577-
}
578-
else if (s.length() > 23 && !subsecond)
579-
{
580-
df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzzzzzzzz");
581-
}
582-
else if (s.length() > 10 && subsecond)
583-
{
584-
df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
561+
// we'll use this dateformat string to parse the result.
562+
df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
585563
}
586-
else if (s.length() > 10 && !subsecond)
564+
else if (s.length() == 19)
587565
{
566+
// No tz or fractional second info.
567+
// I'm not sure if it is
568+
// possible to have a string in this format, as pg
569+
// should give us tz qualified timestamps back, but it was
570+
// in the old code, so I'm handling it for now.
588571
df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
589572
}
590573
else
591574
{
575+
// We must just have a date. This case is
576+
// needed if this method is called on a date column
592577
df = new SimpleDateFormat("yyyy-MM-dd");
593578
}
594579

595580
try
596581
{
597-
return new Timestamp(df.parse(s).getTime());
582+
// All that's left is to parse the string and return the ts.
583+
return new Timestamp(df.parse(sbuf.toString()).getTime());
598584
}
599585
catch (ParseException e)
600586
{

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

Lines changed: 75 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -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
{
15961615
if (s == null)
15971616
return 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
16151621
synchronized (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.
16221626
if (resultSet.sbuf == null)
16231627
resultSet.sbuf = new StringBuffer();
1628+
else
1629+
resultSet.sbuf.setLength(0);
16241630

1625-
resultSet.sbuf.setLength(0);
1631+
// Copy s into sbuf for parsing.
16261632
resultSet.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.
16941688
df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
16951689
}
16961690
else
16971691
{
1692+
// We must just have a date. This case is
1693+
// needed if this method is called on a date
1694+
// column
16981695
df = new SimpleDateFormat("yyyy-MM-dd");
16991696
}
17001697

17011698
try
17021699
{
1700+
// All that's left is to parse the string and return the ts.
17031701
return new Timestamp(df.parse(resultSet.sbuf.toString()).getTime());
17041702
}
17051703
catch (ParseException e)
@@ -1708,7 +1706,5 @@ else if (resultSet.sbuf.length() > 10 && !subsecond)
17081706
}
17091707
}
17101708
}
1711-
1712-
17131709
}
17141710

0 commit comments

Comments
 (0)