/* * Copyright (c) 2004, PostgreSQL Global Development Group * See the LICENSE file in the project root for more information. */ package org.postgresql.test.jdbc2; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import org.postgresql.test.TestUtil; import org.postgresql.util.PGInterval; import org.postgresql.util.PGTimestamp; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.TimeZone; /** * Tests {@link PGTimestamp} in various scenarios including setTimestamp, setObject for both * {@code timestamp with time zone} and {@code timestamp without time zone} data types. */ public class PGTimestampTest { /** * The name of the test table. */ private static final String TEST_TABLE = "testtimestamp"; private Connection con; @Before public void setUp() throws Exception { con = TestUtil.openDB(); TestUtil.createTable(con, TEST_TABLE, "ts timestamp, tz timestamp with time zone"); } @After public void tearDown() throws Exception { TestUtil.dropTable(con, TEST_TABLE); TestUtil.closeDB(con); } /** * Tests {@link PGTimestamp} with {@link PGInterval}. * * @throws SQLException if a JDBC or database problem occurs. */ @Test public void testTimestampWithInterval() throws SQLException { PGTimestamp timestamp = new PGTimestamp(System.currentTimeMillis()); PGInterval interval = new PGInterval(0, 0, 0, 1, 2, 3.14); verifyTimestampWithInterval(timestamp, interval, true); verifyTimestampWithInterval(timestamp, interval, false); timestamp = new PGTimestamp(System.currentTimeMillis(), Calendar.getInstance(TimeZone.getTimeZone("GMT"))); interval = new PGInterval(0, 0, 0, 1, 2, 3.14); verifyTimestampWithInterval(timestamp, interval, true); verifyTimestampWithInterval(timestamp, interval, false); timestamp = new PGTimestamp(System.currentTimeMillis(), Calendar.getInstance(TimeZone.getTimeZone("GMT+01:00"))); interval = new PGInterval(-3, -2, -1, 1, 2, 3.14); verifyTimestampWithInterval(timestamp, interval, true); verifyTimestampWithInterval(timestamp, interval, false); } /** * Executes a test with the given timestamp and interval. * * @param timestamp the timestamp under test. * @param interval the interval. * @param useSetObject indicates if setObject should be used instead of setTimestamp. * @throws SQLException if a JDBC or database problem occurs. */ private void verifyTimestampWithInterval(PGTimestamp timestamp, PGInterval interval, boolean useSetObject) throws SQLException { // Construct the SQL query. String sql; if (timestamp.getCalendar() != null) { sql = "SELECT ?::timestamp with time zone + ?"; } else { sql = "SELECT ?::timestamp + ?"; } // Execute a query using a casted timestamp string + PGInterval. PreparedStatement ps = con.prepareStatement(sql); SimpleDateFormat sdf = createSimpleDateFormat(timestamp); final String timestampString = sdf.format(timestamp); ps.setString(1, timestampString); ps.setObject(2, interval); ResultSet rs = ps.executeQuery(); assertNotNull(rs); assertTrue(rs.next()); Timestamp result1 = rs.getTimestamp(1); assertNotNull(result1); ps.close(); // Execute a query as PGTimestamp + PGInterval. ps = con.prepareStatement("SELECT ? + ?"); if (useSetObject) { ps.setObject(1, timestamp); } else { ps.setTimestamp(1, timestamp); } ps.setObject(2, interval); rs = ps.executeQuery(); // Verify that the query produces the same results. assertTrue(rs.next()); Timestamp result2 = rs.getTimestamp(1); assertEquals(result1, result2); ps.close(); } /** * Tests inserting and selecting {@code PGTimestamp} objects with {@code timestamp} and * {@code timestamp with time zone} columns. * * @throws SQLException if a JDBC or database problem occurs. */ @Test public void testTimeInsertAndSelect() throws SQLException { final long now = System.currentTimeMillis(); verifyInsertAndSelect(new PGTimestamp(now), true); verifyInsertAndSelect(new PGTimestamp(now), false); verifyInsertAndSelect(new PGTimestamp(now, Calendar.getInstance(TimeZone.getTimeZone("GMT"))), true); verifyInsertAndSelect(new PGTimestamp(now, Calendar.getInstance(TimeZone.getTimeZone("GMT"))), false); verifyInsertAndSelect( new PGTimestamp(now, Calendar.getInstance(TimeZone.getTimeZone("GMT+01:00"))), true); verifyInsertAndSelect( new PGTimestamp(now, Calendar.getInstance(TimeZone.getTimeZone("GMT+01:00"))), false); } /** * Verifies that inserting the given {@code PGTimestamp} as a timestamp string and an object * produces the same results. * * @param timestamp the timestamp to test. * @param useSetObject {@code true} if the setObject method should be used instead of * setTimestamp. * @throws SQLException if a JDBC or database problem occurs. */ private void verifyInsertAndSelect(PGTimestamp timestamp, boolean useSetObject) throws SQLException { // Construct the INSERT statement of a casted timestamp string. String sql; if (timestamp.getCalendar() != null) { sql = "INSERT INTO " + TEST_TABLE + " VALUES (?::timestamp with time zone, ?::timestamp with time zone)"; } else { sql = "INSERT INTO " + TEST_TABLE + " VALUES (?::timestamp, ?::timestamp)"; } SimpleDateFormat sdf = createSimpleDateFormat(timestamp); // Insert the timestamps as casted strings. PreparedStatement pstmt1 = con.prepareStatement(sql); pstmt1.setString(1, sdf.format(timestamp)); pstmt1.setString(2, sdf.format(timestamp)); assertEquals(1, pstmt1.executeUpdate()); // Insert the timestamps as PGTimestamp objects. PreparedStatement pstmt2 = con.prepareStatement("INSERT INTO " + TEST_TABLE + " VALUES (?, ?)"); if (useSetObject) { pstmt2.setObject(1, timestamp); pstmt2.setObject(2, timestamp); } else { pstmt2.setTimestamp(1, timestamp); pstmt2.setTimestamp(2, timestamp); } assertEquals(1, pstmt2.executeUpdate()); // Query the values back out. Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(TestUtil.selectSQL(TEST_TABLE, "ts,tz")); assertNotNull(rs); // Read the casted string values. assertTrue(rs.next()); Timestamp ts1 = rs.getTimestamp(1); Timestamp tz1 = rs.getTimestamp(2); // System.out.println(pstmt1 + " -> " + ts1 + ", " + sdf.format(tz1)); // Read the PGTimestamp values. assertTrue(rs.next()); Timestamp ts2 = rs.getTimestamp(1); Timestamp tz2 = rs.getTimestamp(2); // System.out.println(pstmt2 + " -> " + ts2 + ", " + sdf.format(tz2)); // Verify that the first and second versions match. assertEquals(ts1, ts2); assertEquals(tz1, tz2); // Clean up. assertEquals(2, stmt.executeUpdate("DELETE FROM " + TEST_TABLE)); stmt.close(); pstmt2.close(); pstmt1.close(); } /** * Creates a {@code SimpleDateFormat} that is appropriate for the given timestamp. * * @param timestamp the timestamp object. * @return the new format instance. */ private SimpleDateFormat createSimpleDateFormat(PGTimestamp timestamp) { String pattern = "yyyy-MM-dd HH:mm:ss.SSS"; if (timestamp.getCalendar() != null) { pattern += " Z"; } SimpleDateFormat sdf = new SimpleDateFormat(pattern); if (timestamp.getCalendar() != null) { sdf.setTimeZone(timestamp.getCalendar().getTimeZone()); } return sdf; } }