blob: d45959982fb94a6054afbc1c141b5c6ffe08b9c3 [file] [log] [blame]
/*
* 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(this.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);
}
}