Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for a serverTimeZone property #605

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/main/java/com/microsoft/sqlserver/jdbc/DDC.java
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,9 @@ private static String fractionalSecondsString(long subSecondNanos, int scale) {
* (optional) a Calendar representing the time zone to associate with the resulting converted value. For
* DATETIMEOFFSET, this parameter represents the time zone associated with the value. Null means to use the
* default VM time zone.
*
* @param serverTimeZone
* the default server time zone, used if timeZoneCalendar is null.
*
* @param daysSinceBaseDate
* The date part of the value, expressed as a number of days since the base date for the specified SQL Server
Expand All @@ -758,10 +761,10 @@ private static String fractionalSecondsString(long subSecondNanos, int scale) {
* @return a Java object of the desired type.
*/
static final Object convertTemporalToObject(JDBCType jdbcType, SSType ssType, Calendar timeZoneCalendar,
int daysSinceBaseDate, long ticksSinceMidnight, int fractionalSecondsScale) {
TimeZone serverTimeZone, int daysSinceBaseDate, long ticksSinceMidnight, int fractionalSecondsScale) {
// Determine the local time zone to associate with the value. Use the default VM
// time zone if no time zone is otherwise specified.
TimeZone localTimeZone = (null != timeZoneCalendar) ? timeZoneCalendar.getTimeZone() : TimeZone.getDefault();
TimeZone localTimeZone = (null != timeZoneCalendar) ? timeZoneCalendar.getTimeZone() : serverTimeZone;

// Assumed time zone associated with the date and time parts of the value.
//
Expand Down Expand Up @@ -900,7 +903,7 @@ static final Object convertTemporalToObject(JDBCType jdbcType, SSType ssType, Ca
}
int localMillisOffset;
if (null == timeZoneCalendar) {
TimeZone tz = TimeZone.getDefault();
TimeZone tz = serverTimeZone;
GregorianCalendar _cal = new GregorianCalendar(componentTimeZone, Locale.US);
_cal.setLenient(true);
_cal.clear();
Expand Down
55 changes: 28 additions & 27 deletions src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -3362,8 +3362,9 @@ void writeSqlVariantInternalBigDecimal(BigDecimal bigDecimalVal, int srcJdbcType
}

void writeSmalldatetime(String value) throws SQLServerException {
GregorianCalendar calendar = initializeCalender(TimeZone.getDefault());
GregorianCalendar calendar = initializeCalender(con.getServerTimeZone());
long utcMillis; // Value to which the calendar is to be set (in milliseconds 1/1/1970 00:00:00 GMT)

java.sql.Timestamp timestampValue = java.sql.Timestamp.valueOf(value);
utcMillis = timestampValue.getTime();

Expand Down Expand Up @@ -3400,8 +3401,9 @@ void writeSmalldatetime(String value) throws SQLServerException {
}

void writeDatetime(String value) throws SQLServerException {
GregorianCalendar calendar = initializeCalender(TimeZone.getDefault());
GregorianCalendar calendar = initializeCalender(con.getServerTimeZone());
long utcMillis; // Value to which the calendar is to be set (in milliseconds 1/1/1970 00:00:00 GMT)

int subSecondNanos;
java.sql.Timestamp timestampValue = java.sql.Timestamp.valueOf(value);
utcMillis = timestampValue.getTime();
Expand Down Expand Up @@ -3452,7 +3454,7 @@ void writeDatetime(String value) throws SQLServerException {
}

void writeDate(String value) throws SQLServerException {
GregorianCalendar calendar = initializeCalender(TimeZone.getDefault());
GregorianCalendar calendar = initializeCalender(con.getServerTimeZone());
long utcMillis;
java.sql.Date dateValue = java.sql.Date.valueOf(value);
utcMillis = dateValue.getTime();
Expand All @@ -3466,8 +3468,9 @@ void writeDate(String value) throws SQLServerException {
}

void writeTime(java.sql.Timestamp value, int scale) throws SQLServerException {
GregorianCalendar calendar = initializeCalender(TimeZone.getDefault());
GregorianCalendar calendar = initializeCalender(con.getServerTimeZone());
long utcMillis; // Value to which the calendar is to be set (in milliseconds 1/1/1970 00:00:00 GMT)

int subSecondNanos;
utcMillis = value.getTime();
subSecondNanos = value.getNanos();
Expand Down Expand Up @@ -3554,10 +3557,10 @@ void writeOffsetDateTimeWithTimezone(OffsetDateTime offsetDateTimeValue, int sca
calendar.setTimeInMillis(utcMillis);

// Local timezone value in minutes
int minuteAdjustment = ((TimeZone.getDefault().getRawOffset()) / (60 * 1000));
int minuteAdjustment = ((con.getServerTimeZone().getRawOffset()) / (60 * 1000));
// check if date is in day light savings and add daylight saving minutes
if (TimeZone.getDefault().inDaylightTime(calendar.getTime()))
minuteAdjustment += (TimeZone.getDefault().getDSTSavings()) / (60 * 1000);
if (con.getServerTimeZone().inDaylightTime(calendar.getTime()))
minuteAdjustment += (con.getServerTimeZone().getDSTSavings()) / (60 * 1000);
// If the local time is negative then positive minutesOffset must be subtracted from calender
minuteAdjustment += (minuteAdjustment < 0) ? (minutesOffset * (-1)) : minutesOffset;
calendar.add(Calendar.MINUTE, minuteAdjustment);
Expand Down Expand Up @@ -3611,10 +3614,10 @@ void writeOffsetTimeWithTimezone(OffsetTime offsetTimeValue, int scale) throws S
calendar = initializeCalender(timeZone);
calendar.setTimeInMillis(utcMillis);

int minuteAdjustment = (TimeZone.getDefault().getRawOffset()) / (60 * 1000);
int minuteAdjustment = (con.getServerTimeZone().getRawOffset()) / (60 * 1000);
// check if date is in day light savings and add daylight saving minutes to Local timezone(in minutes)
if (TimeZone.getDefault().inDaylightTime(calendar.getTime()))
minuteAdjustment += ((TimeZone.getDefault().getDSTSavings()) / (60 * 1000));
if (con.getServerTimeZone().inDaylightTime(calendar.getTime()))
minuteAdjustment += ((con.getServerTimeZone().getDSTSavings()) / (60 * 1000));
// If the local time is negative then positive minutesOffset must be subtracted from calender
minuteAdjustment += (minuteAdjustment < 0) ? (minutesOffset * (-1)) : minutesOffset;
calendar.add(Calendar.MINUTE, minuteAdjustment);
Expand Down Expand Up @@ -6725,13 +6728,13 @@ final Object readDateTime(int valueLength, Calendar appTimeZoneCalendar, JDBCTyp
}

// Convert the DATETIME/SMALLDATETIME value to the desired Java type.
return DDC.convertTemporalToObject(jdbcType, SSType.DATETIME, appTimeZoneCalendar, daysSinceSQLBaseDate,
msecSinceMidnight, 0); // scale
// (ignored
// for
// fixed-scale
// DATETIME/SMALLDATETIME
// types)
return DDC.convertTemporalToObject(jdbcType, SSType.DATETIME, appTimeZoneCalendar, con.getServerTimeZone(),
daysSinceSQLBaseDate, msecSinceMidnight, 0); // scale
// (ignored
// for
// fixed-scale
// DATETIME/SMALLDATETIME
// types)
}

final Object readDate(int valueLength, Calendar appTimeZoneCalendar, JDBCType jdbcType) throws SQLServerException {
Expand All @@ -6742,10 +6745,8 @@ final Object readDate(int valueLength, Calendar appTimeZoneCalendar, JDBCType jd
int localDaysIntoCE = readDaysIntoCE();

// Convert the DATE value to the desired Java type.
return DDC.convertTemporalToObject(jdbcType, SSType.DATE, appTimeZoneCalendar, localDaysIntoCE, 0, // midnight
// local to
// app time
// zone
return DDC.convertTemporalToObject(jdbcType, SSType.DATE, appTimeZoneCalendar, con.getServerTimeZone(),
localDaysIntoCE, 0, // midnight local to app time zone
0); // scale (ignored for DATE)
}

Expand All @@ -6758,8 +6759,8 @@ final Object readTime(int valueLength, TypeInfo typeInfo, Calendar appTimeZoneCa
long localNanosSinceMidnight = readNanosSinceMidnight(typeInfo.getScale());

// Convert the TIME value to the desired Java type.
return DDC.convertTemporalToObject(jdbcType, SSType.TIME, appTimeZoneCalendar, 0, localNanosSinceMidnight,
typeInfo.getScale());
return DDC.convertTemporalToObject(jdbcType, SSType.TIME, appTimeZoneCalendar, con.getServerTimeZone(), 0,
localNanosSinceMidnight, typeInfo.getScale());
}

final Object readDateTime2(int valueLength, TypeInfo typeInfo, Calendar appTimeZoneCalendar,
Expand All @@ -6772,8 +6773,8 @@ final Object readDateTime2(int valueLength, TypeInfo typeInfo, Calendar appTimeZ
int localDaysIntoCE = readDaysIntoCE();

// Convert the DATETIME2 value to the desired Java type.
return DDC.convertTemporalToObject(jdbcType, SSType.DATETIME2, appTimeZoneCalendar, localDaysIntoCE,
localNanosSinceMidnight, typeInfo.getScale());
return DDC.convertTemporalToObject(jdbcType, SSType.DATETIME2, appTimeZoneCalendar, con.getServerTimeZone(),
localDaysIntoCE, localNanosSinceMidnight, typeInfo.getScale());
}

final Object readDateTimeOffset(int valueLength, TypeInfo typeInfo, JDBCType jdbcType) throws SQLServerException {
Expand All @@ -6788,8 +6789,8 @@ final Object readDateTimeOffset(int valueLength, TypeInfo typeInfo, JDBCType jdb

// Convert the DATETIMEOFFSET value to the desired Java type.
return DDC.convertTemporalToObject(jdbcType, SSType.DATETIMEOFFSET,
new GregorianCalendar(new SimpleTimeZone(localMinutesOffset * 60 * 1000, ""), Locale.US), utcDaysIntoCE,
utcNanosSinceMidnight, typeInfo.getScale());
new GregorianCalendar(new SimpleTimeZone(localMinutesOffset * 60 * 1000, ""), Locale.US),
con.getServerTimeZone(), utcDaysIntoCE, utcNanosSinceMidnight, typeInfo.getScale());
}

private int readDaysIntoCE() throws SQLServerException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3201,7 +3201,7 @@ private byte[] getEncryptedTemporalBytes(TDSWriter tdsWriter, JDBCType srcTempor

switch (srcTemporalJdbcType) {
case DATE:
calendar = new GregorianCalendar(java.util.TimeZone.getDefault(), java.util.Locale.US);
calendar = new GregorianCalendar(connection.getServerTimeZone(), java.util.Locale.US);
calendar.setLenient(true);
calendar.clear();
calendar.setTimeInMillis(((Date) colValue).getTime());
Expand All @@ -3210,7 +3210,7 @@ private byte[] getEncryptedTemporalBytes(TDSWriter tdsWriter, JDBCType srcTempor
SSType.DATE, (short) 0);

case TIME:
calendar = new GregorianCalendar(java.util.TimeZone.getDefault(), java.util.Locale.US);
calendar = new GregorianCalendar(connection.getServerTimeZone(), java.util.Locale.US);
calendar.setLenient(true);
calendar.clear();
utcMillis = ((java.sql.Timestamp) colValue).getTime();
Expand All @@ -3226,7 +3226,7 @@ private byte[] getEncryptedTemporalBytes(TDSWriter tdsWriter, JDBCType srcTempor
return tdsWriter.writeEncryptedScaledTemporal(calendar, subSecondNanos, scale, SSType.TIME, (short) 0);

case TIMESTAMP:
calendar = new GregorianCalendar(java.util.TimeZone.getDefault(), java.util.Locale.US);
calendar = new GregorianCalendar(connection.getServerTimeZone(), java.util.Locale.US);
calendar.setLenient(true);
calendar.clear();
utcMillis = ((java.sql.Timestamp) colValue).getTime();
Expand All @@ -3237,7 +3237,7 @@ private byte[] getEncryptedTemporalBytes(TDSWriter tdsWriter, JDBCType srcTempor

case DATETIME:
case SMALLDATETIME:
calendar = new GregorianCalendar(java.util.TimeZone.getDefault(), java.util.Locale.US);
calendar = new GregorianCalendar(connection.getServerTimeZone(), java.util.Locale.US);
calendar.setLenient(true);
calendar.clear();
utcMillis = ((java.sql.Timestamp) colValue).getTime();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
Expand Down Expand Up @@ -592,6 +593,12 @@ boolean getServerSupportsColumnEncryption() {
return serverSupportsColumnEncryption;
}

private TimeZone serverTimeZone = TimeZone.getDefault();

TimeZone getServerTimeZone() {
return serverTimeZone;
}

private boolean serverSupportsDataClassification = false;

boolean getServerSupportsDataClassification() {
Expand Down Expand Up @@ -1840,6 +1847,12 @@ else if (0 == requestedPacketSize)
activeConnectionProperties.setProperty(sPropKey, SSLProtocol.valueOfString(sPropValue).toString());
}

sPropKey = SQLServerDriverStringProperty.SERVER_TIME_ZONE.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
serverTimeZone = sPropValue.isEmpty() ? TimeZone.getDefault() : TimeZone.getTimeZone(sPropValue);
}

FailoverInfo fo = null;
String databaseNameProperty = SQLServerDriverStringProperty.DATABASE_NAME.toString();
String serverNameProperty = SQLServerDriverStringProperty.SERVER_NAME.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,15 @@ public String getTrustManagerConstructorArg() {
SQLServerDriverStringProperty.TRUST_MANAGER_CONSTRUCTOR_ARG.getDefaultValue());
}

public void setServerTimeZone(String serverTimeZone) {
setStringProperty(connectionProps, SQLServerDriverStringProperty.SERVER_TIME_ZONE.toString(), serverTimeZone);
}

public String getServerTimeZone() {
return getStringProperty(connectionProps, SQLServerDriverStringProperty.SERVER_TIME_ZONE.toString(),
SQLServerDriverStringProperty.SERVER_TIME_ZONE.getDefaultValue());
}

/**
* Sets the datasource URL.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ enum SQLServerDriverStringProperty {
SELECT_METHOD("selectMethod", "direct"),
SERVER_NAME("serverName", ""),
SERVER_SPN("serverSpn", ""),
SERVER_TIME_ZONE("serverTimeZone", ""),
TRUST_STORE_TYPE("trustStoreType", "JKS"),
TRUST_STORE("trustStore", ""),
TRUST_STORE_PASSWORD("trustStorePassword", ""),
Expand Down Expand Up @@ -372,6 +373,7 @@ public final class SQLServerDriver implements java.sql.Driver {
static final String DEFAULT_APP_NAME = "Microsoft JDBC Driver for SQL Server";

private static final String[] TRUE_FALSE = {"true", "false"};

private static final SQLServerDriverPropertyInfo[] DRIVER_PROPERTIES = {
// default required available choices
// property name value property (if appropriate)
Expand Down Expand Up @@ -442,6 +444,8 @@ public final class SQLServerDriver implements java.sql.Driver {
SQLServerDriverStringProperty.SERVER_NAME.getDefaultValue(), false, null),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.SERVER_SPN.toString(),
SQLServerDriverStringProperty.SERVER_SPN.getDefaultValue(), false, null),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.SERVER_TIME_ZONE.toString(),
SQLServerDriverStringProperty.SERVER_TIME_ZONE.getDefaultValue(), false, null),
new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.TRANSPARENT_NETWORK_IP_RESOLUTION.toString(),
Boolean.toString(
SQLServerDriverBooleanProperty.TRANSPARENT_NETWORK_IP_RESOLUTION.getDefaultValue()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,5 +534,7 @@ protected Object[][] getContents() {
{"R_unknownUTF8SupportValue", "Unknown value for UTF8 support."},
{"R_illegalWKT", "Illegal Well-Known text. Please make sure Well-Known text is valid."},
{"R_illegalTypeForGeometry", "{0} is not supported for Geometry."},
{"R_illegalWKTposition", "Illegal character in Well-Known text at position {0}."},};
{"R_illegalWKTposition", "Illegal character in Well-Known text at position {0}."},
{"R_serverTimeZonePropertyDescription",
"The default time zone to use to convert date and time values. The default is the VM default time zone."},};
}
Loading