| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| // |
| // 14/05/2009 ailitchev - Bug 267929: Oracle 11.1.0.7: TIMESTAMP test '100 Years from now -> 2109-03-10 13:22:28.5 EST5EDT EDT' began to fail after Daylight Saving Time started |
| // Changed the test "100 Years from now" to "Last DST year" in both TIMESTAMPDirectToFieldTester and TIMESTAMPTypeConversionTester: |
| // instead of hitting the current date 100 years ahead |
| // it now tests the current date on the latest year for which Daylight Saving Time is defined in Oracle db (by default lastDSTYear = 2020). |
| // The change was done because "100 Years from now" fails when run during DST (though passes outside of it). |
| // To figure out what is the latest year in your Oracle db for which DST defined, |
| // one of Oracle jdbc people suggested printing out the table which includes entries for each supported year |
| // (so the last entry in this table corresponds to the latest supported year). |
| // Here is the code that prints table with oracle jdbc 11.2.0.0.2 and later: |
| // String sTZ = conn.getSessionTimeZone(); |
| // if (sTZ != null && ZONEIDMAP.isValidRegion(sTZ)) { |
| // System.out.println("Session TZ is " + sTZ); |
| // int regionID = ZONEIDMAP.getID(sTZ); |
| // System.out.println("Session TZ ID is " + regionID); |
| // TIMEZONETAB tzTab = TIMEZONETAB.getInstance(1); |
| // if (tzTab.checkID(regionID)) { |
| // tzTab.updateTable(conn, regionID); |
| // } |
| // tzTab.displayTable(regionID); |
| // } |
| // Here is the code that prints table with oracle jdbc 11.1.0.7 and earlier: |
| // (note that unlike 11.2 the user has to explicitly set time zone |
| // conn.setSessionTimeZone(connTimeZone); |
| // String sTZ = conn.getSessionTimeZone(); |
| // if (sTZ != null) { |
| // System.out.println("Session TZ is " + sTZ); |
| // int regionID = ZONEIDMAP.getID(sTZ); |
| // System.out.println("Session TZ ID is " + regionID); |
| // if (TIMEZONETAB.checkID(regionID)) { |
| // TIMEZONETAB.updateTable(conn, regionID); |
| // } |
| // TIMEZONETAB.displayTable(regionID); |
| // } |
| package org.eclipse.persistence.testing.tests.types; |
| |
| import java.util.*; |
| |
| import java.sql.*; |
| |
| import oracle.jdbc.*; |
| |
| import org.eclipse.persistence.sessions.*; |
| import org.eclipse.persistence.testing.framework.*; |
| import org.eclipse.persistence.internal.helper.Helper; |
| import org.eclipse.persistence.internal.platform.database.oracle.TIMESTAMPHelper; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| |
| /** |
| * This class is the super class of TIMESTAMP, TIMESTAMPTZ and TIMESTAMPLTZ specific |
| * test cases. The current subclasses are TIMESTAMPDirectToFieldTester and TIMESTAMPTypeConversionTester |
| * that test Direct-to-Field or TypeConversion mapping. |
| */ |
| public abstract class TIMESTAMPTester extends TypeTester { |
| |
| public Timestamp tsToTS; |
| public Timestamp tsToTSTZ; |
| public Timestamp tsToTSLTZ; |
| public Calendar calToTSTZ; |
| public Calendar calToTSLTZ; |
| public java.util.Date utilDateToTS; |
| public java.util.Date utilDateToTSTZ; |
| public java.util.Date utilDateToTSLTZ; |
| public java.sql.Date dateToTS; |
| public java.sql.Date dateToTSTZ; |
| public java.sql.Date dateToTSLTZ; |
| public Time timeToTS; |
| public Time timeToTSTZ; |
| public Time timeToTSLTZ; |
| |
| // This tsToTS will be stored in DATE field to make sure Timestamp -> DATE is backward compatible. |
| public Timestamp tsToDate; |
| // This Calendar will be stored in DATE field to make sure Calendar -> DATE is backward compatible. |
| // ** So... why was this commmented out? Is it backward compatible?? |
| // public Calendar calToDate; |
| public String sessionTimeZone; |
| |
| // isTimestampInGmt==true if driverVersion is 11.1.0.7 or later and |
| // oracleConnection's property "oracle.jdbc.timestampTzInGmt" is set to "true". |
| // The flag indicates whether TIMESTAMPTZ keeps its timestamp in GMT. |
| public static boolean isTimestampInGmt; |
| |
| // true if driverVersion is 11.2.0.2 or later. |
| public static boolean isLtzTimestampInGmt; |
| |
| // last year for which Daylight Saving Time is supported. |
| static int lastDSTYear = 2020; |
| |
| public TIMESTAMPTester() { |
| super("NEW"); |
| } |
| |
| public TIMESTAMPTester(String nameOfTest, int year, int month, int date, int hrs, int min, int sec, int nano, |
| int zoneMillis) { |
| super(nameOfTest); |
| |
| calToTSTZ = Calendar.getInstance(); |
| calToTSTZ.getTimeZone().setRawOffset(zoneMillis); |
| calToTSTZ.set(year, month, date, hrs, min, sec); |
| calToTSTZ.set(Calendar.MILLISECOND, nano / 1000000); |
| calToTSLTZ = calToTSTZ; |
| buildUtilDates(calToTSTZ.getTime()); |
| buildTimestamps(utilDateToTS.getTime()); |
| buildDates(Helper.dateFromTimestamp(tsToTS)); |
| buildTimes(Helper.timeFromTimestamp(tsToTS)); |
| // calToDate = c; |
| } |
| |
| public TIMESTAMPTester(String nameOfTest, int year, int month, int date, int hrs, int min, int sec, int nano, |
| String zoneId) { |
| super(nameOfTest); |
| |
| /* TimeZone tz = TimeZone.getDefault(); |
| tz.setID(zoneId); |
| calToTSTZ = Calendar.getInstance(tz);*/ |
| calToTSTZ = Calendar.getInstance(TimeZone.getTimeZone(zoneId)); |
| calToTSTZ.set(year, month, date, hrs, min, sec); |
| calToTSTZ.set(Calendar.MILLISECOND, nano / 1000000); |
| calToTSLTZ = calToTSTZ; |
| buildUtilDates(calToTSTZ.getTime()); |
| buildTimestamps(utilDateToTS.getTime()); |
| buildDates(Helper.dateFromTimestamp(tsToTS)); |
| buildTimes(Helper.timeFromTimestamp(tsToTS)); |
| // calToDate = c; |
| } |
| |
| public TIMESTAMPTester(String nameOfTest, Calendar c) { |
| super(nameOfTest); |
| calToTSTZ = c; |
| calToTSLTZ = calToTSTZ; |
| buildUtilDates(calToTSTZ.getTime()); |
| buildTimestamps(utilDateToTS.getTime()); |
| buildDates(Helper.dateFromTimestamp(tsToTS)); |
| buildTimes(Helper.timeFromTimestamp(tsToTS)); |
| // calToDate = c; |
| } |
| |
| public TIMESTAMPTester(String nameOfTest, long time) { |
| super(nameOfTest); |
| buildTimestamps(time); |
| buildDates(Helper.dateFromTimestamp(tsToTS)); |
| buildTimes(Helper.timeFromTimestamp(tsToTS)); |
| buildUtilDates(new java.util.Date(time)); |
| calToTSTZ = Calendar.getInstance(); |
| calToTSTZ.setTime(utilDateToTS); |
| calToTSLTZ = calToTSTZ; |
| // calToDate = c; |
| } |
| |
| public void setSessionTimezone(String timeZone) { |
| this.sessionTimeZone = timeZone; |
| } |
| |
| @Override |
| protected void setup(Session session) { |
| super.setup(session); |
| } |
| |
| public void buildTimestamps(long time) { |
| tsToTS = new Timestamp(time); |
| tsToTSTZ = tsToTS; |
| tsToTSLTZ = tsToTS; |
| tsToDate = tsToTS; |
| } |
| |
| public void buildUtilDates(java.util.Date date) { |
| utilDateToTS = date; |
| utilDateToTSTZ = date; |
| utilDateToTSLTZ = date; |
| } |
| |
| public void buildDates(java.sql.Date date) { |
| dateToTS = date; |
| dateToTSTZ = date; |
| dateToTSLTZ = date; |
| } |
| |
| public void buildTimes(Time time) { |
| timeToTS = time; |
| timeToTSTZ = time; |
| timeToTSLTZ = time; |
| } |
| |
| public Timestamp getTsToTS() { |
| return tsToTS; |
| } |
| |
| public Timestamp getTsToTSTZ() { |
| return tsToTSTZ; |
| } |
| |
| public Timestamp getTsToTSLTZ() { |
| return tsToTSLTZ; |
| } |
| |
| public Calendar getCalToTSTZ() { |
| return calToTSTZ; |
| } |
| |
| public Calendar getCalToTSLTZ() { |
| return calToTSLTZ; |
| } |
| |
| public Timestamp getTsToDate() { |
| return tsToDate; |
| } |
| |
| public java.util.Date getUtilDateToTS() { |
| return utilDateToTS; |
| } |
| |
| public java.util.Date getUtilDateToTSTZ() { |
| return utilDateToTSTZ; |
| } |
| |
| public java.util.Date getUtilDateToTSLTZ() { |
| return utilDateToTSLTZ; |
| } |
| |
| public java.sql.Date getDateToTS() { |
| return dateToTS; |
| } |
| |
| public java.sql.Date getDateToTSTZ() { |
| return dateToTSTZ; |
| } |
| |
| public java.sql.Date getDateToTSLTZ() { |
| return dateToTSLTZ; |
| } |
| |
| public Time getTimeToTS() { |
| return timeToTS; |
| } |
| |
| public Time getTimeToTSTZ() { |
| return timeToTSTZ; |
| } |
| |
| public Time getTimeToTSLTZ() { |
| return timeToTSLTZ; |
| } |
| |
| public void setTsToTS(java.sql.Timestamp aTimestamp) { |
| tsToTS = aTimestamp; |
| } |
| |
| public void setTsToTSTZ(java.sql.Timestamp aTimestamp) { |
| tsToTSTZ = aTimestamp; |
| } |
| |
| public void setTsToTSLTZ(java.sql.Timestamp aTimestamp) { |
| tsToTSLTZ = aTimestamp; |
| } |
| |
| public void setCalToTSTZ(Calendar calToTSTZ) { |
| this.calToTSTZ = calToTSTZ; |
| } |
| |
| public void setCalToTSLTZ(Calendar calToTSLTZ) { |
| this.calToTSLTZ = calToTSLTZ; |
| } |
| |
| public void setTsToDate(java.sql.Timestamp aTimestamp) { |
| tsToDate = aTimestamp; |
| } |
| |
| public void setUtilDateToTS(java.util.Date aDate) { |
| utilDateToTS = aDate; |
| } |
| |
| public void setUtilDateToTSTZ(java.util.Date aDate) { |
| utilDateToTSTZ = aDate; |
| } |
| |
| public void setUtilDateToTSLTZ(java.util.Date aDate) { |
| utilDateToTSLTZ = aDate; |
| } |
| |
| public void setDateToTS(java.sql.Date aDate) { |
| dateToTS = aDate; |
| } |
| |
| public void setDateToTSTZ(java.sql.Date aDate) { |
| dateToTSTZ = aDate; |
| } |
| |
| public void setDateToTSLTZ(java.sql.Date aDate) { |
| dateToTSLTZ = aDate; |
| } |
| |
| public void setTimeToTS(Time aDate) { |
| timeToTS = aDate; |
| } |
| |
| public void setTimeToTSTZ(Time aDate) { |
| timeToTSTZ = aDate; |
| } |
| |
| public void setTimeToTSLTZ(Time aDate) { |
| timeToTSLTZ = aDate; |
| } |
| |
| /* public Calendar getCalToDate() { |
| return calToDate; |
| } |
| |
| public void setCalToDate(Calendar calToTSTZ) { |
| this.calToDate = calToTSTZ; |
| }*/ |
| |
| public String toString() { |
| return getTestName() + " -> " + TIMESTAMPHelper.printCalendar(calToTSTZ); |
| } |
| |
| // Calendar's time zone is inserted into the db if the test runs either with binding or with native sql - |
| // otherwise time zone is simply not present in the inserting sql and therefore |
| // Calendar-to-TIMESTAMPTZ and Calendar-to-TIMESTAMPLTZ comparisons fail because |
| // the objects are written into the db with connection's sessionTimeZone instead of the calendar's time zone. |
| boolean doesTestInsertCalendarTimeZone(WriteTypeObjectTest test) { |
| return test.shouldBindAllParameters()==null || test.shouldBindAllParameters() || test.shouldUseNativeSQL(); |
| } |
| |
| // In case connection's session is not default and isTimestampInGmt==true |
| // tsToTSTZ, utilDateToTSTZ and timeToTSTZ don't work. |
| // Note that dateToTSTZ and calToTSTZ work. |
| // The reason is Oracle jdbc bug 8206596: |
| // Timestamp for 5pm in default zone (say NewYork) written into TIMESTAMPTZ using setObject |
| // is inserted into the db as 5pm LosAngeles (in case it's set as a session time zone). |
| // That happens with both 11.1.0.6 and 11.1.0.7 ojdbc versions. |
| // When the result is read back 11.1.0.7 driver (in case timestampTzInGmt prop. is set to true - which is default setting) |
| // correctly reads 5pm LA - and fails comparison with 5pm NY, |
| // however version 11.0.0.6 (and 11.1.0.7 with timestampTzInGmt prop. set to false) |
| // incorrectly reads back 5pm in default zone - so the inserted and the read back objects are equal. |
| // Note that this incorrect reading happens only in case the target Java type is Timestamp (util.Date, Time), |
| // Eclipselink corrects the result in case the target type is Calendar. |
| boolean doesTimestampTZWork() { |
| return TimeZone.getDefault().getID().equals(this.sessionTimeZone) || !isTimestampInGmt; |
| } |
| |
| // see the previous comment: |
| // it used to work - due to even number of errors - in 11.1.0.7, |
| // but no longer works in 11.2.0.2. |
| // Again, as in TIMESTAMPTZ field case, |
| // writing Timestamp, or Time, or Date, or util.Date while sessionTimeZone is not equal to default time zone |
| // results in combining of a time in default time zone and session time zone. |
| // Therefore 5p.m. in default zone America/New_York written through America/Los_Angeles sessionTimeZone |
| // is recorded in the db. as 5p.m. America/Los_Angeles. |
| boolean doesTimestampLTZWork() { |
| return TimeZone.getDefault().getID().equals(this.sessionTimeZone) || !isLtzTimestampInGmt; |
| } |
| |
| @Override |
| protected void test(WriteTypeObjectTest testCase) { |
| try { |
| if(this.sessionTimeZone != null) { |
| getConnection((DatabaseSession)testCase.getSession()).setSessionTimeZone(this.sessionTimeZone); |
| } |
| } catch (Exception exception) { |
| throw new TestProblemException("Error setting timezone on connection.", exception); |
| } |
| super.test(testCase); |
| } |
| |
| @Override |
| protected void verify(WriteTypeObjectTest testCase) throws TestException { |
| try { |
| super.verify(testCase); |
| } catch (TestException e) { |
| TIMESTAMPTester fromDatabase = (TIMESTAMPTester)testCase.getObjectFromDatabase(); |
| if (fromDatabase == null) { |
| throw new TestErrorException("Value from database is null: " + e); |
| } |
| |
| String errorMsg = ""; |
| if (!tsToTS.equals(fromDatabase.getTsToTS())) { |
| errorMsg += "The tsToTS should be: " + tsToTS + ", but was read as: " + fromDatabase.getTsToTS() + "\n"; |
| } |
| if(doesTimestampTZWork()) { |
| if (!tsToTSTZ.equals(fromDatabase.getTsToTSTZ())) { |
| errorMsg += "The tsToTSTZ should be: " + tsToTSTZ + ", but was read as: " + fromDatabase.getTsToTSTZ() + "\n"; |
| } |
| } |
| if(doesTimestampLTZWork()) { |
| if (!tsToTSLTZ.equals(fromDatabase.getTsToTSLTZ())) { |
| errorMsg += "The tsToTSLTZ should be: " + tsToTSLTZ + ", but was read as: " + fromDatabase.getTsToTSLTZ() + "\n"; |
| } |
| } |
| if (!utilDateToTS.equals(fromDatabase.getUtilDateToTS())) { |
| errorMsg += "The utilDateToTS should be: " + utilDateToTS + ", but was read as: " + fromDatabase.getUtilDateToTS() + "\n"; |
| } |
| if(doesTimestampTZWork()) { |
| if (!utilDateToTSTZ.equals(fromDatabase.getUtilDateToTSTZ())) { |
| errorMsg += "The utilDateToTSTZ should be: " + utilDateToTSTZ + ", but was read as: " + fromDatabase.getUtilDateToTSTZ() + "\n"; |
| } |
| } |
| if(doesTimestampLTZWork()) { |
| if (!utilDateToTSLTZ.equals(fromDatabase.getUtilDateToTSLTZ())) { |
| errorMsg += "The utilDateToTSLTZ should be: " + utilDateToTSLTZ + ", but was read as: " + fromDatabase.getUtilDateToTSLTZ() + "\n"; |
| } |
| } |
| if (!dateToTS.equals(fromDatabase.getDateToTS())) { |
| errorMsg += "The dateToTS should be: " + dateToTS + ", but was read as: " + fromDatabase.getDateToTS() + "\n"; |
| } |
| if (!dateToTSTZ.equals(fromDatabase.getDateToTSTZ())) { |
| errorMsg += "The dateToTSTZ should be: " + dateToTSTZ + ", but was read as: " + fromDatabase.getDateToTSTZ() + "\n"; |
| } |
| if (!dateToTSLTZ.equals(fromDatabase.getDateToTSLTZ())) { |
| errorMsg += "The dateToTSLTZ should be: " + dateToTSLTZ + ", but was read as: " + fromDatabase.getDateToTSLTZ() + "\n"; |
| } |
| if (!timeToTS.equals(fromDatabase.getTimeToTS())) { |
| errorMsg += "The timeToTS should be: " + timeToTS + ", but was read as: " + fromDatabase.getTimeToTS() + "\n"; |
| } |
| if(doesTimestampTZWork()) { |
| if (!timeToTSTZ.equals(fromDatabase.getTimeToTSTZ())) { |
| errorMsg += "The timeToTSTZ should be: " + timeToTSTZ + ", but was read as: " + fromDatabase.getTimeToTSTZ() + "\n"; |
| } |
| } |
| if(doesTimestampLTZWork()) { |
| if (!timeToTSLTZ.equals(fromDatabase.getTimeToTSLTZ())) { |
| errorMsg += "The timeToTSLTZ should be: " + timeToTSLTZ + ", but was read as: " + fromDatabase.getTimeToTSLTZ() + "\n"; |
| } |
| } |
| |
| String originalCal = TIMESTAMPHelper.printCalendar(calToTSLTZ); |
| String dbCal = TIMESTAMPHelper.printCalendar(fromDatabase.getCalToTSLTZ()); |
| //calToTSLTZ from database should have the sessionTimeZone |
| if (sessionTimeZone!=null && !fromDatabase.getCalToTSLTZ().getTimeZone().getID().trim().equals(sessionTimeZone.trim())) { |
| errorMsg += "The sessionTimeZone should be: " + sessionTimeZone + ", but was read as: " + |
| fromDatabase.getCalToTSLTZ().getTimeZone().getID(); |
| } |
| |
| //Calendar-to-TIMESTAMPLTZ does not work if calendar's time stamp is not inserted into the db. |
| if (doesTestInsertCalendarTimeZone(testCase)) { |
| // 0 if and only if the two calendars refer to the same time |
| int compareCalToTSLTZ = calToTSLTZ.compareTo(fromDatabase.getCalToTSLTZ()); |
| if(compareCalToTSLTZ != 0) { |
| errorMsg += "calToTSLTZ.compareTo(fromDatabase.getCalToTSLTZ()) == " + compareCalToTSLTZ + "\n"; |
| errorMsg += "\t original calToTSLTZ = " + TIMESTAMPHelper.printCalendar(calToTSLTZ) + "\n"; |
| errorMsg += "\tfromDatabase.calToTSLTZ = " + TIMESTAMPHelper.printCalendar(fromDatabase.getCalToTSLTZ()) + "\n"; |
| errorMsg += "\t original calToTSLTZ = " + calToTSLTZ + "\n"; |
| errorMsg += "\tfromDatabase.calToTSLTZ = " + fromDatabase.getCalToTSLTZ() + "\n"; |
| } |
| } |
| |
| //Calendar-to-TIMESTAMPTZ does not work if calendar's time stamp is not inserted into the db. |
| if (doesTestInsertCalendarTimeZone(testCase)) { |
| originalCal = TIMESTAMPHelper.printCalendar(calToTSTZ); |
| dbCal = TIMESTAMPHelper.printCalendar(fromDatabase.getCalToTSTZ()); |
| // indicates whether the original and read back from the db calendars are equal |
| boolean areEqual = true; |
| if(isTimestampInGmt) { |
| // 0 if and only if the two calendars refer to the same time |
| int compareCalToTSTZ = calToTSTZ.compareTo(fromDatabase.getCalToTSTZ()); |
| boolean timeZonesEqual = calToTSTZ.getTimeZone().equals(fromDatabase.getCalToTSTZ().getTimeZone()); |
| if(compareCalToTSTZ != 0 || !timeZonesEqual) { |
| areEqual = false; |
| if(compareCalToTSTZ != 0) { |
| errorMsg += "calToTSTZ.compareTo(fromDatabase.getCalToTSTZ()) == " + compareCalToTSTZ + "\n"; |
| } else { |
| errorMsg += "time zones are not equal: \n"; |
| } |
| } |
| } else { |
| if (!originalCal.equals(dbCal)) { |
| areEqual = false; |
| errorMsg += "!originalCal.equals(dbCal)\n"; |
| } |
| } |
| if(!areEqual) { |
| errorMsg += "\t original calToTSTZ = " + originalCal + "\n"; |
| errorMsg += "\tfromDatabase.calToTSTZ = " + dbCal + "\n"; |
| errorMsg += "\t original calToTSTZ = " + calToTSTZ + "\n"; |
| errorMsg += "\tfromDatabase.calToTSTZ = " + fromDatabase.getCalToTSTZ() + "\n"; |
| } |
| } |
| |
| //DATE field does not store milliseconds. Need to compare the original tsToTS without milliseconds with |
| //the one from the database. |
| Timestamp objectTimeInCache = getTsToDate(); |
| int originalTS = objectTimeInCache.getNanos(); |
| //Some original tsToDate without milliseconds fell through the Calendar check and need to excluded from |
| //the following check. |
| // if (originalTS != 0) { |
| |
| // TsToDate always looses nanos, so do not throw an error if the ts match without the nanos. |
| objectTimeInCache.setNanos(0); |
| Timestamp objectTimeInDB = fromDatabase.getTsToDate(); |
| if (objectTimeInCache.equals(objectTimeInDB)) { |
| if (originalTS != objectTimeInDB.getNanos()) { |
| // Nanos don't match, ok, as everything else has been checked, assume this was the reason for the error. |
| if(errorMsg.length() == 0) { |
| return; |
| } |
| } else if (!calToTSLTZ.equals(fromDatabase.getCalToTSLTZ())) { |
| // This seems to be ok, as the timezones are not suppose to come back the same?? |
| // So ok, as everything else has been checked, assume this was the reason for the error. |
| if(errorMsg.length() == 0) { |
| return; |
| } |
| } |
| } else { |
| objectTimeInCache.setNanos(originalTS); |
| errorMsg += "The tsToDate should be: " + objectTimeInCache + " but were read back as: " + |
| objectTimeInDB; |
| } |
| if(errorMsg.length() > 0) { |
| throw new TestErrorException("\n"+errorMsg, e); |
| } else { |
| throw e; |
| } |
| } |
| } |
| |
| private OracleConnection getConnection(DatabaseSession session) { |
| Connection connection = ((AbstractSession)session).getAccessor().getConnection(); |
| return (OracleConnection)session.getServerPlatform().unwrapConnection(connection); |
| } |
| } |