信创-为什么ORACLE使用JDBC查询SYSDATE时,RS.getDate能获取到时间部分?
这是个什问题?在某些版本ORACLE对应的JDBC驱动中,使用查询语句select sysdate from dual,使用rs.getDate获取到日期包含时间部分.而其他大部分数据库返回都不会包含时间部分.间接造成在做信创迁移时,造成时间部分丢失.由于测试代码简单,这里不提供代码部分,直接测试Oraclemysql两种数据的情况,同时经过测试返现,对应的kingbaseguassdb同样有一下情况✅ [oracle] 数据库连接已建立 当前查询SQL:select sysdate from dual Date类型值93 getDate_FMT 2026-05-03 21:30:34 getDate 2026-05-03 getTimestamp2026-05-03 21:30:34.0 ✅ [oracle] 数据库连接已关闭 ✅ [mysql] 数据库连接已建立 当前查询SQL:select sysdate() Date类型值93 getDate_FMT 2026-05-03 00:00:00 getDate 2026-05-03 getTimestamp2026-05-03 21:30:34.0 ✅ [mysql] 数据库连接已关闭针对以上输出结果可以反应了以下信息:ORACLE 和 MYSQL日期列对应的TYPE值都是 93使用格式yyyy-MM-dd HH:mm:ss格式化,可以发现ORACL包含时间部分21:30:34当前的查询sql,复制到数据执行,不管是oracle还是mysql查询接口都包含了时间部分以下是直接在mysql数据库执行的情况sysdate() | -------------------| 2026-05-03 21:36:24|通过这些输出信息可以得到一个结论:数据库底层查询sysdate都是包含时间部分的,只是有些JDBC的驱动去掉了时间部分2. Oracle的JDBC驱动是如何处理的呢?带着上边的疑问,我分析了ojdbc8-19.7.0.0.jar的源码,找到对应的处理逻辑.由此发现,Oracle针对这种情况,还提供了专门的配置oracle.jdbc.DateZeroTime,让用户确定是否保留日期类型的时间部分那这部分代码到底在哪里呢?以下是我定位到堆栈信息T4CDateAccessor(DateTimeCommonAccessor).getDate(int) line: 135 T4CStatement(GeneratedStatement).getDate(long, int) line: 163 ForwardOnlyResultSet(GeneratedScrollableResultSet).getDate(int) line: 186 Multdbtest.testSysdate() line: 138由此我们可以知道,Oralce处理日期的类是DateTimeCommonAccessor我们看下这部分代码Date getDate(int paramInt, Calendar paramCalendar) throws SQLException { Calendar calendar; if (isNull(paramInt)) return null; if (paramCalendar null) { calendar this.statement.getDefaultCalendar(); } else { calendar (Calendar)paramCalendar.clone(); } getBytesInternal(paramInt, this.tmpBytes); int i oracleYear(this.tmpBytes); calendar.clear(); calendar.set(1, i); calendar.set(2, oracleMonth(this.tmpBytes)); calendar.set(5, oracleDay(this.tmpBytes)); if (OracleDriver.getSystemPropertyDateZeroTime()) { calendar.set(11, 0); calendar.set(12, 0); calendar.set(13, 0); } else { calendar.set(11, oracleHour(this.tmpBytes)); calendar.set(12, oracleMin(this.tmpBytes)); calendar.set(13, oracleSec(this.tmpBytes)); } calendar.set(14, 0); if (i 0 calendar.isSet(0)) { calendar.set(0, 1); } return new Date(calendar.getTimeInMillis()); }其中有一部分关键代码if (OracleDriver.getSystemPropertyDateZeroTime())如果这个值为true,就直接把时间部分值设置成0了,否则,就取数据返回的时间部分的信息.那OracleDriver.getSystemPropertyDateZeroTime这个函数,从函数名基本可以知道它的意图,应该是获取SystemProperty,这些信息可以通过启动时注入.那我们推测是否是正确的呢?public static boolean getSystemPropertyDateZeroTime() { String str PhysicalConnection.getSystemPropertyDateZeroTime(false); return str.equalsIgnoreCase(true); }从这里代码只知道,可以大概推测到,这方法默认值返回的是false,就是要获取数据库返回的时间部分,我们继续往下追代码static String getSystemPropertyDateZeroTime(String defaultValue) { return getSystemProperty(oracle.jdbc.DateZeroTime, defaultValue); } private static String getSystemProperty(String configKey, String defaultValue) { if (configKey ! null) { final String fstr configKey; final String fdefaultValue defaultValue; final String[] rets { defaultValue }; AccessController.doPrivileged(new PrivilegedAction() { public Object run() { rets[0] System.getProperty(fstr, fdefaultValue); return null; } }); return rets[0]; } return defaultValue; }3. 知道原因,那如何使用呢?目前知道时间部分存在的原因,如果目前希望rs.getDate返回的日期,跟其他数据库保一样不返回时间部分,就可以设置oracle.jdbc.DateZeroTimetrue,对应测试代码如下:String exesql select sysdate ; if (dbName oracle) { exesql from dual; System.setProperty(oracle.jdbc.DateZeroTime, true); }4. MYSQL是如何处理的呢?通过跟踪代码,数据也是返回了日期类型的,但是rs.getDate使用的是SqlDateValueFactory做了值转换,可以找到这个类,跟踪一下方法.public Date localCreateFromDate(InternalDate idate) { synchronized (this.cal) { try { if (idate.isZero()) { throw new DataReadException(Messages.getString(ResultSet.InvalidZeroDate)); } this.cal.clear(); this.cal.set(idate.getYear(), idate.getMonth() - 1, idate.getDay()); long ms this.cal.getTimeInMillis(); return new Date(ms); } catch (IllegalArgumentException e) { throw ExceptionFactory.createException(WrongArgumentException.class, e.getMessage(), e); } } }从以上代码可以知道,mysql只赋值了了年月日,对应调试的堆栈信息如下SqlDateValueFactory.localCreateFromDate(InternalDate) line: 80 SqlDateValueFactory.localCreateFromDate(InternalDate) line: 50 SqlDateValueFactory(AbstractDateTimeValueFactoryT).createFromDate(InternalDate) line: 67 SqlDateValueFactory.localCreateFromTimestamp(InternalTimestamp) line: 120 SqlDateValueFactory.localCreateFromTimestamp(InternalTimestamp) line: 50 SqlDateValueFactory(AbstractDateTimeValueFactoryT).createFromTimestamp(InternalTimestamp) line: 87 MysqlTextValueDecoder.decodeTimestamp(byte[], int, int, ValueFactoryT) line: 79 ByteArrayRow(AbstractResultsetRow).decodeAndCreateReturnValue(int, byte[], int, int, ValueFactoryT) line: 87 ByteArrayRow(AbstractResultsetRow).getValueFromBytes(int, byte[], int, int, ValueFactoryT) line: 241 ByteArrayRow.getValue(int, ValueFactoryT) line: 91 ResultSetImpl.getDate(int) line: 7405. 信创总结在适配ORACLE数据信创时,由于oracle返回了日期时间的部分.导致迁移到其他数据库时