diff -rcN clean/org/postgresql/test/jdbc2/IntervalTest.java interval/org/postgresql/test/jdbc2/IntervalTest.java *** clean/org/postgresql/test/jdbc2/IntervalTest.java Wed Dec 31 16:00:00 1969 --- interval/org/postgresql/test/jdbc2/IntervalTest.java Fri Apr 29 00:35:35 2005 *************** *** 0 **** --- 1,163 ---- + package org.postgresql.test.jdbc2; + + import java.util.Calendar; + import java.util.Date; + import java.util.GregorianCalendar; + import java.sql.*; + + import junit.framework.TestCase; + + import org.postgresql.util.PGInterval; + import org.postgresql.test.TestUtil; + + public class IntervalTest extends TestCase + { + + private Connection _conn; + + public IntervalTest(String name) + { + super(name); + } + + protected void setUp() throws Exception + { + _conn = TestUtil.openDB(); + TestUtil.createTable(_conn, "testinterval", "v interval"); + } + + protected void tearDown() throws Exception + { + TestUtil.dropTable(_conn, "testinterval"); + TestUtil.closeDB(_conn); + } + + public void testOnlineTests() throws SQLException + { + PreparedStatement pstmt = _conn.prepareStatement("INSERT INTO testinterval VALUES (?)"); + pstmt.setObject(1, new PGInterval(2004, 13, 28, 0, 0, 43000.9013)); + pstmt.executeUpdate(); + pstmt.close(); + + Statement stmt = _conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT v FROM testinterval"); + assertTrue(rs.next()); + PGInterval pgi = (PGInterval)rs.getObject(1); + assertEquals(2005, pgi.getYears()); + assertEquals(1, pgi.getMonths()); + assertEquals(28, pgi.getDays()); + assertEquals(11, pgi.getHours()); + assertEquals(56, pgi.getMinutes()); + assertEquals(40.9013, pgi.getSeconds(), 0.000001); + assertTrue(!rs.next()); + rs.close(); + stmt.close(); + } + + public void testOfflineTests() + throws Exception + { + PGInterval pgi = new PGInterval(2004, 4, 20, 15, 57, 12.1); + + assertEquals(2004, pgi.getYears()); + assertEquals(4, pgi.getMonths()); + assertEquals(20, pgi.getDays()); + assertEquals(15, pgi.getHours()); + assertEquals(57, pgi.getMinutes()); + assertEquals(12.1, pgi.getSeconds(), 0); + + PGInterval pgi2 = new PGInterval("@ 2004 years 4 mons 20 days 15 hours 57 mins 12.1 secs"); + assertEquals(pgi, pgi2); + + // Singular units + PGInterval pgi3 = new PGInterval("@ 2004 year 4 mon 20 day 15 hour 57 min 12.1 sec"); + assertEquals(pgi, pgi3); + + PGInterval pgi4 = new PGInterval("2004 years 4 mons 20 days 15:57:12.1"); + assertEquals(pgi, pgi4); + + // Ago test + pgi = new PGInterval("@ 2004 years 4 mons 20 days 15 hours 57 mins 12.1 secs ago"); + assertEquals(-2004, pgi.getYears()); + assertEquals(-4, pgi.getMonths()); + assertEquals(-20, pgi.getDays()); + assertEquals(-15, pgi.getHours()); + assertEquals(-57, pgi.getMinutes()); + assertEquals(-12.1, pgi.getSeconds(), 0); + + // Char test + pgi = new PGInterval("@ +2004 years -4 mons +20 days -15 hours +57 mins -12.1 secs"); + assertEquals(2004, pgi.getYears()); + assertEquals(-4, pgi.getMonths()); + assertEquals(20, pgi.getDays()); + assertEquals(-15, pgi.getHours()); + assertEquals(57, pgi.getMinutes()); + assertEquals(-12.1, pgi.getSeconds(), 0); + } + + public void testCalendar() + throws Exception + { + Calendar cal = new GregorianCalendar(); + Calendar cal2 = (Calendar)cal.clone(); + + PGInterval pgi = new PGInterval("@ 1 year -23 hours -3 mins -3.30 secs"); + pgi.roll(cal2); + + cal2.roll(Calendar.YEAR, -1); + cal2.roll(Calendar.HOUR, 23); + cal2.roll(Calendar.MINUTE, 3); + cal2.roll(Calendar.SECOND, 3); + cal2.roll(Calendar.MILLISECOND, 300); + + assertEquals(cal, cal2); + + pgi.roll(cal2); + PGInterval pgi2 = new PGInterval("@ 1 year -23 hours -3 mins -3.30 secs ago"); + pgi2.roll(cal2); + + assertEquals(cal, cal2); + + pgi = new PGInterval("@ 1 year -23 hours -3 mins -3.30 secs ago"); + pgi.roll(cal2); + + cal2.roll(Calendar.YEAR, 1); + cal2.roll(Calendar.HOUR, -23); + cal2.roll(Calendar.MINUTE, -3); + cal2.roll(Calendar.SECOND, -3); + cal2.roll(Calendar.MILLISECOND, -300); + + assertEquals(cal, cal2); + } + + public void testDate() + throws Exception + { + Date date = new Date(); + Date date2 = new Date(date.getTime()); + + PGInterval pgi = new PGInterval("@ +2004 years -4 mons +20 days -15 hours +57 mins -12.1 secs"); + pgi.roll(date); + + PGInterval pgi2 = new PGInterval("@ +2004 years -4 mons +20 days -15 hours +57 mins -12.1 secs ago"); + pgi2.roll(date); + + assertEquals(date2, date); + } + + public void testISODate() + throws Exception + { + Date date = new Date(); + Date date2 = new Date(date.getTime()); + + PGInterval pgi = new PGInterval("+2004 years -4 mons +20 days -15:57:12.1"); + pgi.roll(date); + + PGInterval pgi2 = new PGInterval("-2004 years 4 mons -20 days 15:57:12.1"); + pgi2.roll(date); + + assertEquals(date2, date); + } + + } diff -rcN clean/org/postgresql/test/jdbc2/Jdbc2TestSuite.java interval/org/postgresql/test/jdbc2/Jdbc2TestSuite.java *** clean/org/postgresql/test/jdbc2/Jdbc2TestSuite.java Fri Feb 4 01:14:27 2005 --- interval/org/postgresql/test/jdbc2/Jdbc2TestSuite.java Fri Apr 29 00:01:12 2005 *************** *** 81,86 **** --- 81,87 ---- suite.addTestSuite(CursorFetchTest.class); suite.addTestSuite(ServerCursorTest.class); + suite.addTestSuite(IntervalTest.class); suite.addTestSuite(GeometricTest.class); suite.addTestSuite(LoginTimeoutTest.class); diff -rcN clean/org/postgresql/util/PGInterval.java interval/org/postgresql/util/PGInterval.java *** clean/org/postgresql/util/PGInterval.java Mon Jan 24 22:13:41 2005 --- interval/org/postgresql/util/PGInterval.java Fri Apr 29 00:40:29 2005 *************** *** 10,33 **** package org.postgresql.util; import java.io.Serializable; public class PGInterval extends PGobject implements Serializable, Cloneable { public PGInterval() { setType("interval"); } ! public PGInterval(String value ) { ! setType("interval"); ! this.value = value; } ! /* ! * This must be overidden to allow the object to be cloned */ public Object clone() { ! return new PGInterval( value ); } } --- 10,446 ---- package org.postgresql.util; import java.io.Serializable; + import java.sql.SQLException; + import java.text.DecimalFormat; + import java.util.Calendar; + import java.util.Date; + import java.util.Locale; + import java.util.StringTokenizer; + /** + * This implements a class that handles the PostgreSQL interval type + */ public class PGInterval extends PGobject implements Serializable, Cloneable { + + private int years; + private int months; + private int days; + private int hours; + private int minutes; + private double seconds; + + private final static DecimalFormat secondsFormat = new DecimalFormat("#.00####"); + + + /** + * reuired by the driver + */ public PGInterval() { setType("interval"); } ! ! /** ! * Initialize a interval with a given interval string representation ! * ! * @param value String representated interval (e.g. '3 years 2 mons') ! * @throws SQLException Is thrown if the string representation has an unknown format ! * @see #setValue(String) ! */ ! public PGInterval(String value) ! throws SQLException { ! this(); ! setValue(value); ! } ! ! /** ! * Initializes all values of this interval to the specified values ! * ! * @param years Years ! * @param months Months ! * @param days Days ! * @param hours Hours ! * @param minutes Minutes ! * @param seconds Seconds ! * @see #setValue(int, int, int, int, int, double) ! */ ! public PGInterval(int years, int months, int days, int hours, int minutes, double seconds) ! { ! this(); ! setValue(years, months, days, hours, minutes, seconds); ! } ! ! /** ! * Sets a interval string represented value to this instance. ! * This method only recognize the format, that Postgres returns - ! * not all input formats are supported (e.g. '1 yr 2 m 3 s')! ! * ! * @param value String representated interval (e.g. '3 years 2 mons') ! * @throws SQLException Is thrown if the string representation has an unknown format ! */ ! public void setValue(String value) ! throws SQLException ! { ! final boolean ISOFormat = !value.startsWith("@"); ! ! // Just a simple '0' ! if (!ISOFormat && value.length() == 3 && value.charAt(2) == '0') ! { ! setValue(0, 0, 0, 0, 0, 0.0); ! return; ! } ! ! int years = 0; ! int months = 0; ! int days = 0; ! int hours = 0; ! int minutes = 0; ! double seconds = 0; ! ! try ! { ! String valueToken = null; ! ! value = value.replace('+', ' ').replace('@', ' '); ! final StringTokenizer st = new StringTokenizer(value); ! for (int i = 1; st.hasMoreTokens(); i++) ! { ! String token = st.nextToken(); ! ! if ((i & 1) == 1) ! { ! if (token.indexOf(':') == -1) ! { ! valueToken = token; ! continue; ! } ! ! // This handles hours, minutes, seconds and microseconds for ! // ISO intervals ! int offset = (token.charAt(0) == '-') ? 1 : 0; ! ! hours = nullSafeIntGet(token.substring(offset+0, offset+2)); ! minutes = nullSafeIntGet(token.substring(offset+3, offset+5)); ! seconds = nullSafeDoubleGet(token.substring(offset+6, token.length())); ! ! if (offset == 1) ! { ! hours = -hours; ! minutes = -minutes; ! seconds = -seconds; ! } ! ! valueToken = null; ! } ! else ! { ! // This handles years, months, days for both, ISO and ! // Non-ISO intervals. Hours, minutes, seconds and microseconds ! // are handled for Non-ISO intervals here. ! ! if (token.startsWith("year")) ! years = nullSafeIntGet(valueToken); ! else if (token.startsWith("mon")) ! months = nullSafeIntGet(valueToken); ! else if (token.startsWith("day")) ! days = nullSafeIntGet(valueToken); ! else if (token.startsWith("hour")) ! hours = nullSafeIntGet(valueToken); ! else if (token.startsWith("min")) ! minutes = nullSafeIntGet(valueToken); ! else if (token.startsWith("sec")) ! seconds = nullSafeDoubleGet(valueToken); ! } ! } ! } ! catch (NumberFormatException e) ! { ! throw new PSQLException(GT.tr("Conversion of interval failed"), PSQLState.NUMERIC_CONSTANT_OUT_OF_RANGE, e); ! } ! ! if (!ISOFormat && value.endsWith("ago")) ! { ! // Inverse the leading sign ! setValue(-years, -months, -days, -hours, -minutes, -seconds); ! } ! else ! { ! setValue(years, months, days, hours, minutes, seconds); ! } ! } ! ! /** ! * Set all values of this interval to the specified values ! * ! * @param years Years ! * @param months Months ! * @param days Days ! * @param hours Hours ! * @param minutes Minutes ! * @param seconds Seconds ! */ ! public void setValue(int years, int months, int days, int hours, int minutes, double seconds) ! { ! setYears(years); ! setMonths(months); ! setDays(days); ! setHours(hours); ! setMinutes(minutes); ! setSeconds(seconds); ! } ! ! /** ! * Returns the stored interval information as a string ! * ! * @return String represented interval ! */ ! public String getValue() ! { ! StringBuffer sb = new StringBuffer(); ! sb.append(years).append(" years "); ! sb.append(months).append(" mons "); ! sb.append(days).append(" days "); ! sb.append(hours).append(" hour "); ! sb.append(minutes).append(" mins "); ! sb.append(secondsFormat.format(seconds)).append(" secs"); ! return sb.toString(); ! } ! ! /** ! * Returns the years represented by this interval ! * ! * @return Years ! */ ! public int getYears() ! { ! return years; ! } ! ! /** ! * Set the years of this interval to the specified value ! * ! * @param Years ! */ ! public void setYears(int years) ! { ! this.years = years; ! } ! ! /** ! * Returns the months represented by this interval ! * ! * @return Months ! */ ! public int getMonths() ! { ! return months; ! } ! ! /** ! * Set the months of this interval to the specified value ! * ! * @param Months ! */ ! public void setMonths(int months) ! { ! this.months = months; ! } ! ! /** ! * Returns the days represented by this interval ! * ! * @return Days ! */ ! public int getDays() ! { ! return days; ! } ! ! /** ! * Set the days of this interval to the specified value ! * ! * @param Days ! */ ! public void setDays(int days) ! { ! this.days = days; ! } ! ! /** ! * Returns the hours represented by this interval ! * ! * @return Hours ! */ ! public int getHours() ! { ! return hours; ! } ! ! /** ! * Set the hours of this interval to the specified value ! * ! * @param Hours ! */ ! public void setHours(int hours) ! { ! this.hours = hours; ! } ! ! /** ! * Returns the minutes represented by this interval ! * ! * @return Minutes ! */ ! public int getMinutes() ! { ! return minutes; ! } ! ! /** ! * Set the minutes of this interval to the specified value ! * ! * @param Minutes ! */ ! public void setMinutes(int minutes) ! { ! this.minutes = minutes; ! } ! ! /** ! * Returns the seconds represented by this interval ! * ! * @return Seconds ! */ ! public double getSeconds() ! { ! return seconds; ! } ! ! /** ! * Set the seconds of this interval to the specified value ! * ! * @param Seconds ! */ ! public void setSeconds(double seconds) ! { ! this.seconds = seconds; ! } ! ! /** ! * Rolls this interval on a given calendar ! * ! * @param cal Calendar instance to roll on ! */ ! public void roll(Calendar cal) ! { ! // Avoid precision loss ! // Be aware postgres doesn't return more than 60 seconds - no overflow can happen ! final int microseconds = (int)(getSeconds() * 1000000.0); ! final int seconds = microseconds / 1000000; // Strip off fractal part ! final int milliseconds = microseconds / 1000 - seconds * 1000; ! ! cal.roll(Calendar.YEAR, getYears()); ! cal.roll(Calendar.MONTH, getMonths()); ! cal.roll(Calendar.DAY_OF_WEEK_IN_MONTH, getDays()); ! cal.roll(Calendar.HOUR, getHours()); ! cal.roll(Calendar.MINUTE, getMinutes()); ! cal.roll(Calendar.SECOND, seconds); ! cal.roll(Calendar.MILLISECOND, milliseconds); ! } ! ! /** ! * Rolls this interval on a given date ! * ! * @param cal Date instance to roll on ! */ ! public void roll(Date date) ! { ! final Calendar cal = Calendar.getInstance(); ! cal.setTime(date); ! roll(cal); ! date.setTime(cal.getTime().getTime()); ! } ! ! /** ! * Returns integer value of value or 0 if value is null ! * ! * @param value integer as string value ! * @return integer parsed from string value ! * @throws NumberFormatException if the string contains invalid chars ! */ ! private int nullSafeIntGet(String value) ! throws NumberFormatException ! { ! return (value == null) ? 0 : Integer.parseInt(value); ! } ! ! /** ! * Returns double value of value or 0 if value is null ! * ! * @param value double as string value ! * @return double parsed from string value ! * @throws NumberFormatException if the string contains invalid chars ! */ ! private double nullSafeDoubleGet(String value) ! throws NumberFormatException ! { ! return (value == null) ? 0 : Double.parseDouble(value); ! } ! ! /** ! * Returns whether an object is equal to this one or not ! * ! * @param obj Object to compare with ! * @return true if the two intervals are identical ! */ ! public boolean equals(Object obj) ! { ! if (obj == null) ! return false; ! ! if (obj == this) ! return true; ! ! if (!(obj instanceof PGInterval)) ! return false; ! ! final PGInterval pgi = (PGInterval)obj; ! ! return ! pgi.years == years && ! pgi.months == months && ! pgi.days == days && ! pgi.hours == hours && ! pgi.minutes == minutes && ! pgi.seconds == seconds; } ! /** ! * Returns a hashCode for this object ! * ! * @return hashCode ! */ ! public int hashCode() ! { ! return ((((((7 * 31 + ! (int)Double.doubleToLongBits(seconds)) * 31 + ! minutes) * 31 + ! hours) * 31 + ! days) * 31 + ! months) * 31 + ! years) * 31; ! } ! ! /** ! * This clones the interval ! * ! * @return Cloned interval */ public Object clone() { ! return new PGInterval(years, months, days, hours, minutes, seconds); } + }