diff --git a/src/main/java/org/sqlite/core/CoreConnection.java b/src/main/java/org/sqlite/core/CoreConnection.java index 026bee42c..be72658fe 100644 --- a/src/main/java/org/sqlite/core/CoreConnection.java +++ b/src/main/java/org/sqlite/core/CoreConnection.java @@ -67,7 +67,7 @@ protected CoreConnection(String url, String fileName, Properties prop) throws SQ SQLiteConfig config = new SQLiteConfig(prop); this.dateClass = config.dateClass; this.dateMultiplier = config.dateMultiplier; - this.dateFormat = FastDateFormat.getInstance(config.dateStringFormat); + this.dateFormat = FastDateFormat.getInstance(config.dateStringFormat, java.util.TimeZone.getTimeZone("UTC")); this.dateStringFormat = config.dateStringFormat; this.datePrecision = config.datePrecision; this.transactionMode = config.getTransactionMode(); diff --git a/src/main/java/org/sqlite/jdbc3/JDBC3ResultSet.java b/src/main/java/org/sqlite/jdbc3/JDBC3ResultSet.java index f8dc3a51f..9ac19a7f2 100644 --- a/src/main/java/org/sqlite/jdbc3/JDBC3ResultSet.java +++ b/src/main/java/org/sqlite/jdbc3/JDBC3ResultSet.java @@ -17,6 +17,7 @@ import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Locale; +import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.sqlite.core.CoreResultSet; @@ -311,46 +312,21 @@ public Date getDate(int col) throws SQLException { * @see java.sql.ResultSet#getDate(int, java.util.Calendar) */ public Date getDate(int col, Calendar cal) throws SQLException { - checkCalendar(cal); - - switch (db.column_type(stmt.pointer, markCol(col))) { - case SQLITE_NULL: - return null; - - case SQLITE_TEXT: - try { - FastDateFormat dateFormat = FastDateFormat.getInstance(stmt.conn.dateStringFormat, cal.getTimeZone()); - - return new java.sql.Date(dateFormat.parse(db.column_text(stmt.pointer, markCol(col))).getTime()); - } - catch (Exception e) { - SQLException error = new SQLException("Error parsing time stamp"); - error.initCause(e); - - throw error; - } - - case SQLITE_FLOAT: - return new Date(julianDateToCalendar(db.column_double(stmt.pointer, markCol(col)), cal).getTimeInMillis()); - - default: // SQLITE_INTEGER: - cal.setTimeInMillis(db.column_long(stmt.pointer, markCol(col)) * stmt.conn.dateMultiplier); - return new Date(cal.getTime().getTime()); - } + return getDate(col); } /** * @see java.sql.ResultSet#getDate(java.lang.String) */ public Date getDate(String col) throws SQLException { - return getDate(findColumn(col), Calendar.getInstance()); + return getDate(findColumn(col)); } /** * @see java.sql.ResultSet#getDate(java.lang.String, java.util.Calendar) */ public Date getDate(String col, Calendar cal) throws SQLException { - return getDate(findColumn(col), cal); + return getDate(findColumn(col)); } /** @@ -474,32 +450,7 @@ public Time getTime(int col) throws SQLException { * @see java.sql.ResultSet#getTime(int, java.util.Calendar) */ public Time getTime(int col, Calendar cal) throws SQLException { - checkCalendar(cal); - - switch (db.column_type(stmt.pointer, markCol(col))) { - case SQLITE_NULL: - return null; - - case SQLITE_TEXT: - try { - FastDateFormat dateFormat = FastDateFormat.getInstance(stmt.conn.dateStringFormat, cal.getTimeZone()); - - return new Time(dateFormat.parse(db.column_text(stmt.pointer, markCol(col))).getTime()); - } - catch (Exception e) { - SQLException error = new SQLException("Error parsing time"); - error.initCause(e); - - throw error; - } - - case SQLITE_FLOAT: - return new Time(julianDateToCalendar(db.column_double(stmt.pointer, markCol(col)), cal).getTimeInMillis()); - - default: //SQLITE_INTEGER - cal.setTimeInMillis(db.column_long(stmt.pointer, markCol(col)) * stmt.conn.dateMultiplier); - return new Time(cal.getTime().getTime()); - } + return getTime(col); } /** @@ -547,35 +498,7 @@ public Timestamp getTimestamp(int col) throws SQLException { * @see java.sql.ResultSet#getTimestamp(int, java.util.Calendar) */ public Timestamp getTimestamp(int col, Calendar cal) throws SQLException { - if (cal == null) { - return getTimestamp(col); - } - - switch (db.column_type(stmt.pointer, markCol(col))) { - case SQLITE_NULL: - return null; - - case SQLITE_TEXT: - try { - FastDateFormat dateFormat = FastDateFormat.getInstance(stmt.conn.dateStringFormat, cal.getTimeZone()); - - return new Timestamp(dateFormat.parse(db.column_text(stmt.pointer, markCol(col))).getTime()); - } - catch (Exception e) { - SQLException error = new SQLException("Error parsing time stamp"); - error.initCause(e); - - throw error; - } - - case SQLITE_FLOAT: - return new Timestamp(julianDateToCalendar(db.column_double(stmt.pointer, markCol(col)), cal).getTimeInMillis()); - - default: //SQLITE_INTEGER - cal.setTimeInMillis(db.column_long(stmt.pointer, markCol(col)) * stmt.conn.dateMultiplier); - - return new Timestamp(cal.getTime().getTime()); - } + return getTimestamp(col); } /** @@ -589,7 +512,7 @@ public Timestamp getTimestamp(String col) throws SQLException { * @see java.sql.ResultSet#getTimestamp(java.lang.String, java.util.Calendar) */ public Timestamp getTimestamp(String c, Calendar ca) throws SQLException { - return getTimestamp(findColumn(c), ca); + return getTimestamp(findColumn(c)); } /** @@ -1016,22 +939,16 @@ public boolean rowUpdated() throws SQLException { return false; } - /** - * Transforms a Julian Date to java.util.Calendar object. - */ - private Calendar julianDateToCalendar(Double jd) { - return julianDateToCalendar(jd, Calendar.getInstance()); - } - /** * Transforms a Julian Date to java.util.Calendar object. * Based on Guine Christian's function found here: * http://java.ittoolbox.com/groups/technical-functional/java-l/java-function-to-convert-julian-date-to-calendar-date-1947446 */ - private Calendar julianDateToCalendar(Double jd, Calendar cal) { + private Calendar julianDateToCalendar(Double jd) { if (jd == null) { return null; } + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); int yyyy, dd, mm, hh, mn, ss, ms , A; @@ -1089,14 +1006,4 @@ private Calendar julianDateToCalendar(Double jd, Calendar cal) { return cal; } - public void checkCalendar(Calendar cal) throws SQLException { - if (cal != null) - return; - - SQLException e = new SQLException("Expected a calendar instance."); - e.initCause(new NullPointerException()); - - throw e; - } - } diff --git a/src/test/java/org/sqlite/AllTests.java b/src/test/java/org/sqlite/AllTests.java index 9e3ad4584..596fc7dbf 100644 --- a/src/test/java/org/sqlite/AllTests.java +++ b/src/test/java/org/sqlite/AllTests.java @@ -8,6 +8,7 @@ @Suite.SuiteClasses({ BackupTest.class, ConnectionTest.class, + DateTimeTest.class, DBMetaDataTest.class, ErrorMessageTest.class, ExtendedCommandTest.class, @@ -34,4 +35,4 @@ }) public class AllTests { // runs all Tests -} \ No newline at end of file +} diff --git a/src/test/java/org/sqlite/DateTimeTest.java b/src/test/java/org/sqlite/DateTimeTest.java new file mode 100644 index 000000000..82746b9bb --- /dev/null +++ b/src/test/java/org/sqlite/DateTimeTest.java @@ -0,0 +1,150 @@ +package org.sqlite; + +import static org.junit.Assert.*; + +import java.io.ByteArrayInputStream; +import java.io.UnsupportedEncodingException; +import java.sql.Connection; +import java.sql.Date; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.StringTokenizer; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/* + * Created by Alexander Galanin + */ + +public class DateTimeTest +{ + // Mon May 23 06:06:21.123 MSK 2016 = Mon May 23 03:06:21.123 GMT 2016 + private static final long DATETIME_UNIX = 1463972781L; + private static final long DATETIME_MILLISECONDS = 123; + private static final long DATETIME_UNIX_HIPRECISION = DATETIME_UNIX * 1000 + DATETIME_MILLISECONDS; + private static final Date DATETIME = new Date(DATETIME_UNIX * 1000); + private static final Date DATETIME_HIPRECISION = new Date(DATETIME_UNIX_HIPRECISION); + + private Connection conn; + private PreparedStatement stat; + + @After + public void close() throws SQLException { + stat.close(); + conn.close(); + } + + @Test + public void setDateUnix() throws SQLException { + SQLiteConfig config = new SQLiteConfig(); + + config.setReadOnly(true); + config.setDateClass("INTEGER"); + config.setDatePrecision("SECONDS"); + + conn = DriverManager.getConnection("jdbc:sqlite:", config.toProperties()); + stat = conn.prepareStatement("select strftime('%s', ?, 'unixepoch')"); + stat.setDate(1, DATETIME); + + ResultSet rs = stat.executeQuery(); + assertTrue(rs.next()); + assertEquals(rs.getLong(1), DATETIME_UNIX); + rs.close(); + } + + @Test + public void setDateJulian() throws SQLException { + SQLiteConfig config = new SQLiteConfig(); + + config.setReadOnly(true); + config.setDateClass("REAL"); + + conn = DriverManager.getConnection("jdbc:sqlite:", config.toProperties()); + stat = conn.prepareStatement("select strftime('%s', ?)"); + stat.setDate(1, DATETIME); + + ResultSet rs = stat.executeQuery(); + assertTrue(rs.next()); + assertEquals(rs.getLong(1), DATETIME_UNIX); + rs.close(); + } + + /** + * Driver MUST format date/time in UTC because SQLite's internal date/time format is UTC. + * + * To test this in UTC timezone use java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("GMT+3")); + */ + @Test + public void setDateText() throws SQLException { + SQLiteConfig config = new SQLiteConfig(); + + config.setReadOnly(true); + config.setDateClass("TEXT"); + + conn = DriverManager.getConnection("jdbc:sqlite:", config.toProperties()); + stat = conn.prepareStatement("select strftime('%s', ?)"); + stat.setDate(1, DATETIME); + + ResultSet rs = stat.executeQuery(); + assertTrue(rs.next()); + assertEquals(rs.getLong(1), DATETIME_UNIX); + rs.close(); + } + + @Test + public void getDateInt() throws SQLException { + SQLiteConfig config = new SQLiteConfig(); + + config.setReadOnly(true); + config.setDateClass("INTEGER"); + config.setDatePrecision("SECONDS"); + + conn = DriverManager.getConnection("jdbc:sqlite:", config.toProperties()); + stat = conn.prepareStatement("select " + String.valueOf(DATETIME_UNIX)); + + ResultSet rs = stat.executeQuery(); + assertTrue(rs.next()); + assertEquals(rs.getDate(1), DATETIME); + rs.close(); + } + + /** + * Driver MUST scan date/time in UTC because SQLite's internal date/time format is UTC. + * + * To test this in UTC timezone use java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("GMT+3")); + */ + @Test + public void getDateJulian() throws SQLException { + conn = DriverManager.getConnection("jdbc:sqlite:"); + stat = conn.prepareStatement("select julianday(" + String.valueOf(DATETIME_UNIX) + ", 'unixepoch', '+' || (" + String.valueOf(DATETIME_MILLISECONDS) + " / 1000.0) || ' seconds')"); + + ResultSet rs = stat.executeQuery(); + assertTrue(rs.next()); + assertEquals(rs.getDate(1), DATETIME_HIPRECISION); + rs.close(); + } + + /** + * Driver MUST scan date/time in UTC because SQLite's internal date/time format is UTC. + * + * To test this in UTC timezone use java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("GMT+3")); + */ + @Test + public void getDateString() throws SQLException { + conn = DriverManager.getConnection("jdbc:sqlite:"); + stat = conn.prepareStatement("select strftime('%Y-%m-%d %H:%M:%f', " + String.valueOf(DATETIME_UNIX) + ", 'unixepoch', '+' || (" + String.valueOf(DATETIME_MILLISECONDS) + " / 1000.0) || ' seconds')"); + + ResultSet rs = stat.executeQuery(); + assertTrue(rs.next()); + assertEquals(rs.getDate(1), DATETIME_HIPRECISION); + rs.close(); + } + +} diff --git a/src/test/java/org/sqlite/QueryTest.java b/src/test/java/org/sqlite/QueryTest.java index a61716bac..2d29125f7 100644 --- a/src/test/java/org/sqlite/QueryTest.java +++ b/src/test/java/org/sqlite/QueryTest.java @@ -21,6 +21,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.Date; +import java.util.TimeZone; import org.junit.Test; import org.sqlite.date.FastDateFormat; @@ -89,7 +90,7 @@ public void dateTimeTest() throws Exception { conn.createStatement().execute("create table sample (start_time datetime)"); Date now = new Date(); - String date = FastDateFormat.getInstance(SQLiteConfig.DEFAULT_DATE_STRING_FORMAT).format(now); + String date = FastDateFormat.getInstance(SQLiteConfig.DEFAULT_DATE_STRING_FORMAT, TimeZone.getTimeZone("UTC")).format(now); conn.createStatement().execute("insert into sample values(" + now.getTime() + ")"); conn.createStatement().execute("insert into sample values('" + date + "')");