This is an automated email from the ASF dual-hosted git repository.

stoty pushed a commit to branch 5.1
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/5.1 by this push:
     new 621e9abd4d PHOENIX-5066 The TimeZone is incorrectly used during 
writing or reading data
621e9abd4d is described below

commit 621e9abd4d27b338e1b94804d3542d56c955b455
Author: Istvan Toth <st...@apache.org>
AuthorDate: Tue Mar 7 08:45:23 2023 +0100

    PHOENIX-5066 The TimeZone is incorrectly used during writing or reading data
    
    Add the phoenix.query.applyTimeZoneDisplacement option, that causes the
    java.sql time types to be treated as local times.
    This option is disabled by default, it needs to be enabled to get the new
    behaviour.
---
 .../phoenix/end2end/TimeZoneDisplacementIT.java    | 299 +++++++++++++++++++++
 .../apache/phoenix/compile/StatementContext.java   |  55 ++--
 .../expression/function/CurrentDateFunction.java   |   4 +-
 .../expression/function/CurrentTimeFunction.java   |   2 +-
 .../expression/function/ToDateFunction.java        |   2 +-
 .../org/apache/phoenix/jdbc/PhoenixConnection.java |  29 +-
 .../phoenix/jdbc/PhoenixPreparedStatement.java     |  75 ++++--
 .../org/apache/phoenix/jdbc/PhoenixResultSet.java  | 100 ++++---
 .../org/apache/phoenix/jdbc/PhoenixStatement.java  |  10 +-
 .../apache/phoenix/parse/CurrentDateParseNode.java |   2 +-
 .../apache/phoenix/parse/CurrentTimeParseNode.java |   2 +-
 .../org/apache/phoenix/parse/ToDateParseNode.java  |   2 +-
 .../org/apache/phoenix/parse/ToTimeParseNode.java  |   2 +-
 .../apache/phoenix/parse/ToTimestampParseNode.java |   2 +-
 .../org/apache/phoenix/query/QueryServices.java    |   1 +
 .../apache/phoenix/query/QueryServicesOptions.java |   1 +
 .../java/org/apache/phoenix/util/DateUtil.java     | 117 ++++++++
 .../java/org/apache/phoenix/util/DateUtilTest.java | 171 ++++++++++++
 pom.xml                                            |   8 +-
 19 files changed, 773 insertions(+), 111 deletions(-)

diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TimeZoneDisplacementIT.java
 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TimeZoneDisplacementIT.java
new file mode 100644
index 0000000000..6b50efd43b
--- /dev/null
+++ 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TimeZoneDisplacementIT.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.time.LocalDateTime;
+import java.util.Properties;
+import java.util.TimeZone;
+
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.DateUtil;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(ParallelStatsEnabledTest.class)
+public class TimeZoneDisplacementIT extends ParallelStatsEnabledIT {
+
+    @Test
+    public void testCompliantCorrection() throws Exception {
+        String tableName = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        props.put(QueryServices.APPLY_TIME_ZONE_DISPLACMENT_ATTRIB, 
Boolean.TRUE.toString());
+        try (PhoenixConnection conn =
+                (PhoenixConnection) DriverManager.getConnection(getUrl(), 
props);
+                Statement stmt = conn.createStatement();
+                PreparedStatement insertPstmt =
+                        conn.prepareStatement("upsert into " + tableName
+                                + " (ID, D1, D2, T1, T2, S1, S2, UD1, UD2, 
UT1, UT2, US1, US2) VALUES"
+                                + " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 
")) {
+            conn.setAutoCommit(true);
+            stmt.executeUpdate("create table " + tableName + " (id integer 
primary key,"
+                    + "D1 DATE, D2 DATE," + "T1 TIME, T2 TIME," + "S1 
TIMESTAMP, S2 TIMESTAMP,"
+                    + "UD1 UNSIGNED_DATE, UD2 UNSIGNED_DATE,"
+                    + "UT1 UNSIGNED_TIME, UT2 UNSIGNED_TIME,"
+                    + "US1 UNSIGNED_TIMESTAMP, US2 UNSIGNED_TIMESTAMP)");
+
+            java.util.Date nowJud = new java.util.Date();
+            java.sql.Date nowSqlDate = new java.sql.Date(nowJud.getTime());
+            java.sql.Time nowSqlTime = new java.sql.Time(nowJud.getTime());
+            java.sql.Timestamp nowSqlTimestamp = new 
java.sql.Timestamp(nowJud.getTime());
+
+            String nowString =
+                    DateUtil.getDateFormatter(conn.getDatePattern(), 
TimeZone.getDefault().getID())
+                            .format(nowJud);
+
+            insertPstmt.setInt(1, 1);
+            insertPstmt.setString(2, nowString);
+            insertPstmt.setDate(3, nowSqlDate);
+            insertPstmt.setString(4, nowString);
+            insertPstmt.setTime(5, nowSqlTime);
+            insertPstmt.setString(6, nowString);
+            insertPstmt.setTimestamp(7, nowSqlTimestamp);
+            insertPstmt.setString(8, nowString);
+            insertPstmt.setDate(9, nowSqlDate);
+            insertPstmt.setString(10, nowString);
+            insertPstmt.setTime(11, nowSqlTime);
+            insertPstmt.setString(12, nowString);
+            insertPstmt.setTimestamp(13, nowSqlTimestamp);
+            insertPstmt.execute();
+
+            ResultSet rs =
+                    stmt.executeQuery("select * from " + tableName + " where "
+                            + "D1 = D2 AND T1 = T2 and S1 = S2 and UD1 = UD2 
and UT1 = UT2 and US1 = US2");
+            assertTrue(rs.next());
+            assertEquals(nowSqlDate, rs.getDate("D1"));
+            assertEquals(nowSqlDate, rs.getDate("D2"));
+            assertEquals(nowSqlTime, rs.getDate("T1"));
+            assertEquals(nowSqlTime, rs.getDate("T2"));
+            assertEquals(nowSqlTimestamp, rs.getTimestamp("S1"));
+            assertEquals(nowSqlTimestamp, rs.getTimestamp("S2"));
+            assertEquals(nowSqlDate, rs.getDate("UD1"));
+            assertEquals(nowSqlDate, rs.getDate("UD2"));
+            assertEquals(nowSqlTime, rs.getDate("UT1"));
+            assertEquals(nowSqlTime, rs.getDate("UT2"));
+            assertEquals(nowSqlTimestamp, rs.getTimestamp("US1"));
+            assertEquals(nowSqlTimestamp, rs.getTimestamp("US2"));
+
+            assertEquals(nowString, rs.getString("D1"));
+            assertEquals(nowString, rs.getString("D2"));
+            assertEquals(nowString, rs.getString("T1"));
+            assertEquals(nowString, rs.getString("T2"));
+            assertEquals(nowString, rs.getString("S1"));
+            assertEquals(nowString, rs.getString("S2"));
+            assertEquals(nowString, rs.getString("UD1"));
+            assertEquals(nowString, rs.getString("UD2"));
+            assertEquals(nowString, rs.getString("UT1"));
+            assertEquals(nowString, rs.getString("UT2"));
+            assertEquals(nowString, rs.getString("US1"));
+            assertEquals(nowString, rs.getString("US2"));
+        }
+    }
+
+    @Test
+    public void testPhoenix5066() throws Exception {
+        String tableName = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        props.put(QueryServices.APPLY_TIME_ZONE_DISPLACMENT_ATTRIB, 
Boolean.TRUE.toString());
+        try (PhoenixConnection conn =
+                (PhoenixConnection) DriverManager.getConnection(getUrl(), 
props);
+                Statement stmt = conn.createStatement();
+                PreparedStatement insertPstmt =
+                        conn.prepareStatement("upsert into " + tableName + " 
(ID, D, T, S) VALUES"
+                                + " (?, ?, ?, ?) ")) {
+            conn.setAutoCommit(true);
+            stmt.executeUpdate("create table " + tableName + " (id integer 
primary key,"
+                    + "D DATE, T TIME, S TIMESTAMP)");
+
+            String dateString = "2018-12-10 15:40:47";
+            java.util.Date dateJud =
+                    new 
java.util.Date(LocalDateTime.parse(dateString.replace(" ", "T"))
+                            
.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli());
+            java.sql.Date sqlDate = new java.sql.Date(dateJud.getTime());
+            java.sql.Time sqlTime = new java.sql.Time(dateJud.getTime());
+            java.sql.Timestamp sqlTimestamp = new 
java.sql.Timestamp(dateJud.getTime());
+
+            stmt.executeUpdate("upsert into " + tableName + "(ID, D, T, S) 
VALUES (" + "1," + "'"
+                    + dateString + "'," + "'" + dateString + "'," + "'" + 
dateString + "')");
+
+            stmt.executeUpdate("upsert into " + tableName + "(ID, D, T, S) 
VALUES (" + "2,"
+                    + "to_date('" + dateString + "')," + "to_time('" + 
dateString + "'),"
+                    + "to_timestamp('" + dateString + "'))");
+
+            insertPstmt.setInt(1, 3);
+            insertPstmt.setDate(2, sqlDate);
+            insertPstmt.setTime(3, sqlTime);
+            insertPstmt.setTimestamp(4, sqlTimestamp);
+            insertPstmt.executeUpdate();
+
+            ResultSet rs = stmt.executeQuery("select * from " + tableName + " 
order by id asc");
+            for (int i = 0; i < 3; i++) {
+                assertTrue(rs.next());
+                assertEquals(dateString + ".000", rs.getString("D"));
+                assertEquals(dateString + ".000", rs.getString("T"));
+                assertEquals(dateString + ".000", rs.getString("S"));
+                assertEquals(sqlDate, rs.getDate("D"));
+                assertEquals(sqlTime, rs.getTime("T"));
+                assertEquals(sqlTimestamp, rs.getTimestamp("S"));
+            }
+
+        }
+    }
+
+    @Test
+    public void testCurrent() throws Exception {
+        String tableName = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        // NOTE that this succeeds with either TRUE or FALSE, but the actual 
HBase value stored
+        // is different
+        // We are testing that the displacement gets applied both ways
+        props.put(QueryServices.APPLY_TIME_ZONE_DISPLACMENT_ATTRIB, 
Boolean.TRUE.toString());
+        try (PhoenixConnection conn =
+                (PhoenixConnection) DriverManager.getConnection(getUrl(), 
props);
+                Statement stmt = conn.createStatement();) {
+            conn.setAutoCommit(true);
+            stmt.executeUpdate("create table " + tableName + " (ID integer 
primary key,"
+                    + " D DATE," + " T TIME," + " S TIMESTAMP," + " UD 
UNSIGNED_DATE,"
+                    + " UT UNSIGNED_TIME," + " US UNSIGNED_TIMESTAMP)");
+
+            java.util.Date nowJud = new java.util.Date();
+
+            stmt.executeUpdate("UPSERT INTO " + tableName + " (ID, D, T, S, 
UD, UT, US) "
+                    + "VALUES (1, CURRENT_DATE(), CURRENT_TIME(), 
CURRENT_DATE(),"
+                    + "CURRENT_DATE(), CURRENT_TIME(), CURRENT_DATE())");
+
+            ResultSet rs = stmt.executeQuery("select * from " + tableName + " 
order by id asc");
+            assertTrue(rs.next());
+            assertTrue(Math.abs(nowJud.getTime() - rs.getDate("D").getTime()) 
< 1000);
+            assertTrue(Math.abs(nowJud.getTime() - rs.getTime("T").getTime()) 
< 1000);
+            assertTrue(Math.abs(nowJud.getTime() - 
rs.getTimestamp("S").getTime()) < 1000);
+            assertTrue(Math.abs(nowJud.getTime() - rs.getDate("UD").getTime()) 
< 1000);
+            assertTrue(Math.abs(nowJud.getTime() - rs.getTime("UT").getTime()) 
< 1000);
+            assertTrue(Math.abs(nowJud.getTime() - 
rs.getTimestamp("US").getTime()) < 1000);
+        }
+    }
+
+    @Test
+    public void testRowTimestamp() throws Exception {
+        // The lack of DATE WITH TIMEZONE type in Phoenix causes the 
rowTimestamp function
+        // to behave erratically with COMPLIANT_TIMEZONE_HANDLING
+
+        // For normal operation HBase expects the TS to be a Instant-like UTC 
timestamp.
+        // However we apply the TZ displacement on setting and retrieving 
Temporal objects from/to
+        // java temporal types.
+
+        // This can cause a lot of problems as if the HBase TS is displaced 
relative to UTC
+        // by setting it explicitly to the current timestamp by setDate() or 
similar, then
+        // The the valid value may not be actual latest TS if clients from 
more than one TZ
+        // are writing the row.
+
+        // When using ROW_TIMESTAMP with COMPLIANT_TIMEZONE_HANDLING, the 
ROWTS should always be
+        // set from string or long types, NOT from java temporal types to 
avoid this.
+
+        TimeZone tz = TimeZone.getDefault();
+
+        String tableName = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        props.put(QueryServices.APPLY_TIME_ZONE_DISPLACMENT_ATTRIB, 
Boolean.TRUE.toString());
+        try (PhoenixConnection conn =
+                (PhoenixConnection) DriverManager.getConnection(getUrl(), 
props);
+                Statement stmt = conn.createStatement();
+                PreparedStatement upsertStmt =
+                        conn.prepareStatement(
+                            "upsert into " + tableName + " (ID, ROWTS, D) 
VALUES (?, ?, ?)");) {
+            conn.setAutoCommit(true);
+            stmt.executeUpdate(
+                "create table " + tableName + " (ID integer not null," + " 
ROWTS DATE NOT NULL,"
+                        + " D DATE," + " CONSTRAINT pk PRIMARY KEY (ID, ROWTS 
ROW_TIMESTAMP) )");
+
+            String dateString = "2018-12-10 15:40:47";
+            java.util.Date dateJudLocal =
+                    new 
java.util.Date(LocalDateTime.parse(dateString.replace(" ", "T"))
+                            
.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli());
+            java.sql.Date sqlDateLocal = new 
java.sql.Date(dateJudLocal.getTime());
+
+            // Default ROWTS
+            stmt.executeUpdate(
+                "upsert into " + tableName + " (ID, D) VALUES (1, '" + 
dateString + "')");
+            // Set ROWTS from literal, displacement doesn't apply
+            stmt.executeUpdate("upsert into " + tableName + " (ID, ROWTS, D) 
VALUES (2, '"
+                    + dateString + "','" + dateString + "')");
+            // Set ROWTS from parameter, displacement DOES apply
+            upsertStmt.setInt(1, 3);
+            upsertStmt.setDate(2, sqlDateLocal);
+            upsertStmt.setDate(3, sqlDateLocal);
+            upsertStmt.executeUpdate();
+
+            java.sql.Date nowDate = new java.sql.Date(new 
java.util.Date().getTime());
+            upsertStmt.setInt(1, 4);
+            upsertStmt.setDate(2, nowDate);
+            upsertStmt.setDate(3, nowDate);
+            upsertStmt.executeUpdate();
+
+            ResultSet rs = stmt.executeQuery("select * from " + tableName + " 
order by id asc");
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt("ID"));
+            assertEquals(sqlDateLocal, rs.getDate("D"));
+            // UTC now() rowTs gets gets applied the displacement when read as 
java.sql.Date
+            // This is NOT intuitive, but at least the TS contains the current 
UTC epoch.
+            assertTrue(Math.abs(DateUtil
+                    .applyOutputDisplacement(new java.sql.Date(new 
java.util.Date().getTime()), tz)
+                    .getTime() - rs.getDate("ROWTS").getTime()) < 10000);
+
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt("ID"));
+            assertEquals(sqlDateLocal, rs.getDate("D"));
+            // No displacement on write, because the date literal gets parsed 
as UTC.
+            // So the HBase Ttimestamp is "2018-12-10 15:40:47 UTC"
+            // getDate() applies the TZ displacement on read as normal
+            // so we get "2018-12-10 15:40:47" parsed in the client TZ (which 
is sqlDate)
+            assertEquals(sqlDateLocal, rs.getDate("ROWTS"));
+            assertEquals(dateString + ".000", rs.getString("ROWTS"));
+
+            assertTrue(rs.next());
+            assertEquals(3, rs.getInt("ID"));
+            assertEquals(sqlDateLocal, rs.getDate("D"));
+            assertEquals(sqlDateLocal, rs.getDate("ROWTS"));
+            // the stored timestamp is in UTC, but only because sqlDateLocal 
pre-applies the
+            // displacement by parsing the date in the local TZ
+            assertEquals(dateString + ".000", rs.getString("ROWTS"));
+
+            assertTrue(rs.next());
+            assertEquals(4, rs.getInt("ID"));
+            assertEquals(nowDate, rs.getDate("D"));
+            // The stored Hbase timestamp is NOT the current UTC timestamp. We 
have applied the
+            // displacement on setting the date value.
+            // This is very much not ideal behaviour, and probably NOT what 
the user wanted
+            // in this case
+            assertEquals(nowDate, rs.getDate("ROWTS"));
+            // To further demonstrate that the HBase TS is NOT the current 
epoch value.
+            
assertEquals(DateUtil.getDateFormatter(DateUtil.DEFAULT_DATE_FORMAT)
+                    .format(DateUtil.applyInputDisplacement(nowDate, tz)),
+                rs.getString("ROWTS"));
+        }
+    }
+}
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
index 699a95a81a..bd27773afc 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
@@ -42,12 +42,14 @@ import org.apache.phoenix.schema.PColumn;
 import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.schema.TableRef;
+import org.apache.phoenix.schema.types.PDate;
+import org.apache.phoenix.schema.types.PTime;
+import org.apache.phoenix.schema.types.PTimestamp;
+import org.apache.phoenix.thirdparty.com.google.common.collect.Maps;
 import org.apache.phoenix.util.DateUtil;
 import org.apache.phoenix.util.NumberUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 
-import org.apache.phoenix.thirdparty.com.google.common.collect.Maps;
-
 
 /**
  *
@@ -59,17 +61,11 @@ import 
org.apache.phoenix.thirdparty.com.google.common.collect.Maps;
  */
 public class StatementContext {
     private ColumnResolver resolver;
+    private final PhoenixConnection connection;
     private final BindManager binds;
     private final Scan scan;
     private final ExpressionManager expressions;
     private final AggregationManager aggregates;
-    private final String dateFormat;
-    private final Format dateFormatter;
-    private final String timeFormat;
-    private final Format timeFormatter;
-    private final String timestampFormat;
-    private final Format timestampFormatter;
-    private final TimeZone dateFormatTimeZone;
     private final String numberFormat;
     private final ImmutableBytesWritable tempPtr;
     private final PhoenixStatement statement;
@@ -124,17 +120,8 @@ public class StatementContext {
         this.binds = binds;
         this.aggregates = new AggregationManager();
         this.expressions = new ExpressionManager();
-        PhoenixConnection connection = statement.getConnection();
+        this.connection = statement.getConnection();
         ReadOnlyProps props = connection.getQueryServices().getProps();
-        String timeZoneID = 
props.get(QueryServices.DATE_FORMAT_TIMEZONE_ATTRIB,
-                DateUtil.DEFAULT_TIME_ZONE_ID);
-        this.dateFormat = props.get(QueryServices.DATE_FORMAT_ATTRIB, 
DateUtil.DEFAULT_DATE_FORMAT);
-        this.dateFormatter = DateUtil.getDateFormatter(dateFormat, timeZoneID);
-        this.timeFormat = props.get(QueryServices.TIME_FORMAT_ATTRIB, 
DateUtil.DEFAULT_TIME_FORMAT);
-        this.timeFormatter = DateUtil.getTimeFormatter(timeFormat, timeZoneID);
-        this.timestampFormat = 
props.get(QueryServices.TIMESTAMP_FORMAT_ATTRIB, 
DateUtil.DEFAULT_TIMESTAMP_FORMAT);
-        this.timestampFormatter = 
DateUtil.getTimestampFormatter(timestampFormat, timeZoneID);
-        this.dateFormatTimeZone = DateUtil.getTimeZone(timeZoneID);
         this.numberFormat = props.get(QueryServices.NUMBER_FORMAT_ATTRIB, 
NumberUtil.DEFAULT_NUMBER_FORMAT);
         this.tempPtr = new ImmutableBytesWritable();
         this.currentTable = resolver != null && 
!resolver.getTables().isEmpty() ? resolver.getTables().get(0) : null;
@@ -176,32 +163,32 @@ public class StatementContext {
         return dataColumns;
     }
 
-    public String getDateFormat() {
-        return dateFormat;
+    public String getDateFormatTimeZoneId() {
+        return connection.getDateFormatTimeZoneId();
     }
 
-    public TimeZone getDateFormatTimeZone() {
-        return dateFormatTimeZone;
+    public String getDateFormat() {
+        return connection.getDatePattern();
     }
 
     public Format getDateFormatter() {
-        return dateFormatter;
+        return connection.getFormatter(PDate.INSTANCE);
     }
 
     public String getTimeFormat() {
-        return timeFormat;
+        return connection.getTimePattern();
     }
 
     public Format getTimeFormatter() {
-        return timeFormatter;
+        return connection.getFormatter(PTime.INSTANCE);
     }
 
     public String getTimestampFormat() {
-        return timestampFormat;
+        return connection.getTimestampPattern();
     }
 
     public Format getTimestampFormatter() {
-        return timestampFormatter;
+        return connection.getFormatter(PTimestamp.INSTANCE);
     }
 
     public String getNumberFormat() {
@@ -255,7 +242,7 @@ public class StatementContext {
     }
 
     public PhoenixConnection getConnection() {
-        return statement.getConnection();
+        return connection;
     }
 
     public PhoenixStatement getStatement() {
@@ -281,12 +268,20 @@ public class StatementContext {
          * purely to bind the current time based on the server time.
          */
         PTable table = this.getCurrentTable().getTable();
-        PhoenixConnection connection = getConnection();
         MetaDataClient client = new MetaDataClient(connection);
         currentTime = client.getCurrentTime(table.getSchemaName().getString(), 
table.getTableName().getString());
         return currentTime;
     }
 
+    public long getCurrentTimeWithDisplacement() throws SQLException {
+        if (connection.isApplyTimeZoneDisplacement()) {
+            return DateUtil.applyInputDisplacement(new 
java.sql.Date(getCurrentTime()),
+                statement.getLocalCalendar().getTimeZone()).getTime();
+        } else {
+            return getCurrentTime();
+        }
+    }
+
     public SequenceManager getSequenceManager(){
         return sequences;
     }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CurrentDateFunction.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CurrentDateFunction.java
index 2549c6a774..0c30e81032 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CurrentDateFunction.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CurrentDateFunction.java
@@ -52,7 +52,9 @@ public class CurrentDateFunction extends 
CurrentDateTimeFunction {
     }
 
     public CurrentDateFunction(List<Expression> children, StatementContext 
context) throws SQLException {
-        this(context.getCurrentTime());
+        // Note that according to the standard Date is always WITHOUT 
TIMEZONE, but we don't
+        // implement real dates
+        this(context.getCurrentTimeWithDisplacement());
     }
 
     public CurrentDateFunction(long timeStamp) {
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CurrentTimeFunction.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CurrentTimeFunction.java
index 1b083bf8d3..ef76cc027f 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CurrentTimeFunction.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CurrentTimeFunction.java
@@ -52,7 +52,7 @@ public class CurrentTimeFunction extends 
CurrentDateTimeFunction {
     }
 
     public CurrentTimeFunction(List<Expression> children, StatementContext 
context) throws SQLException {
-        this(context.getCurrentTime());
+        this(context.getCurrentTimeWithDisplacement());
     }
 
     public CurrentTimeFunction(long timeStamp) {
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToDateFunction.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToDateFunction.java
index 3df09bf4d3..31d6fd998c 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToDateFunction.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToDateFunction.java
@@ -70,7 +70,7 @@ public class ToDateFunction extends ScalarFunction {
             dateFormat = context.getDateFormat();
         }
         if (timeZoneId == null) {
-            timeZoneId = context.getDateFormatTimeZone().getID();
+            timeZoneId = context.getDateFormatTimeZoneId();
         }
         init(dateFormat, timeZoneId);
     }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
index 65983b61e9..09b31b2bee 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
@@ -156,6 +156,7 @@ public class PhoenixConnection implements Connection, 
MetaDataMutated, SQLClosea
     private boolean isAutoFlush = false;
     private boolean isAutoCommit = false;
     private final PName tenantId;
+    private final String dateFormatTimeZoneId;
     private final String datePattern;
     private final String timePattern;
     private final String timestampPattern;
@@ -185,6 +186,7 @@ public class PhoenixConnection implements Connection, 
MetaDataMutated, SQLClosea
     //For now just the copy constructor paths will have this as true as I 
don't want to change the
     //public interfaces.
     private final boolean isInternalConnection;
+    private boolean isApplyTimeZoneDisplacement;
 
     static {
         Tracing.addTraceMetricsSource();
@@ -346,11 +348,14 @@ public class PhoenixConnection implements Connection, 
MetaDataMutated, SQLClosea
         long maxSizeBytes = this.services.getProps().getLong(
                 QueryServices.MAX_MUTATION_SIZE_BYTES_ATTRIB,
                 QueryServicesOptions.DEFAULT_MAX_MUTATION_SIZE_BYTES);
-        String timeZoneID = 
this.services.getProps().get(QueryServices.DATE_FORMAT_TIMEZONE_ATTRIB,
+        this.isApplyTimeZoneDisplacement = this.services.getProps().getBoolean(
+            QueryServices.APPLY_TIME_ZONE_DISPLACMENT_ATTRIB,
+            QueryServicesOptions.DEFAULT_APPLY_TIME_ZONE_DISPLACMENT);
+        this.dateFormatTimeZoneId = 
this.services.getProps().get(QueryServices.DATE_FORMAT_TIMEZONE_ATTRIB,
                 DateUtil.DEFAULT_TIME_ZONE_ID);
-        Format dateFormat = DateUtil.getDateFormatter(datePattern, timeZoneID);
-        Format timeFormat = DateUtil.getDateFormatter(timePattern, timeZoneID);
-        Format timestampFormat = DateUtil.getDateFormatter(timestampPattern, 
timeZoneID);
+        Format dateFormat = DateUtil.getDateFormatter(datePattern, 
dateFormatTimeZoneId);
+        Format timeFormat = DateUtil.getDateFormatter(timePattern, 
dateFormatTimeZoneId);
+        Format timestampFormat = DateUtil.getDateFormatter(timestampPattern, 
dateFormatTimeZoneId);
         formatters.put(PDate.INSTANCE, dateFormat);
         formatters.put(PTime.INSTANCE, timeFormat);
         formatters.put(PTimestamp.INSTANCE, timestampFormat);
@@ -654,6 +659,14 @@ public class PhoenixConnection implements Connection, 
MetaDataMutated, SQLClosea
         return datePattern;
     }
 
+    public String getTimePattern() {
+        return timePattern;
+    }
+
+    public String getTimestampPattern() {
+        return timestampPattern;
+    }
+
     public Format getFormatter(PDataType type) {
         return formatters.get(type);
     }
@@ -1362,4 +1375,12 @@ public class PhoenixConnection implements Connection, 
MetaDataMutated, SQLClosea
     public String getSourceOfOperation() {
         return sourceOfOperation;
     }
+
+    public String getDateFormatTimeZoneId() {
+        return dateFormatTimeZoneId;
+    }
+
+    public boolean isApplyTimeZoneDisplacement() {
+        return isApplyTimeZoneDisplacement;
+    }
 }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixPreparedStatement.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixPreparedStatement.java
index b153d93d09..d1a775d9b5 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixPreparedStatement.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixPreparedStatement.java
@@ -56,6 +56,7 @@ import 
org.apache.phoenix.schema.ExecuteQueryNotApplicableException;
 import org.apache.phoenix.schema.ExecuteUpdateNotApplicableException;
 import org.apache.phoenix.schema.Sequence;
 import org.apache.phoenix.schema.types.PDataType;
+import org.apache.phoenix.util.DateUtil;
 import org.apache.phoenix.util.SQLCloseable;
 
 /**
@@ -363,19 +364,20 @@ public class PhoenixPreparedStatement extends 
PhoenixStatement implements Prepar
 
     @Override
     public void setDate(int parameterIndex, Date x) throws SQLException {
-        if (x != null) { // Since Date is mutable, make a copy
-            x = new Date(x.getTime());
-        }
-        setParameter(parameterIndex, x);
+        setDate(parameterIndex, x, localCalendar);
     }
 
     @Override
     public void setDate(int parameterIndex, Date x, Calendar cal) throws 
SQLException {
         if (x != null) { // Since Date is mutable, make a copy
-            x = new Date(x.getTime());
+            if (connection.isApplyTimeZoneDisplacement()) {
+                x = DateUtil.applyInputDisplacement(x, cal.getTimeZone());
+            } else {
+                // Since Date is mutable, make a copy
+                x = new Date(x.getTime());
+            }
         }
-        cal.setTime(x);
-        setParameter(parameterIndex, new Date(cal.getTimeInMillis()));
+        setParameter(parameterIndex, x);
     }
 
     @Override
@@ -440,6 +442,25 @@ public class PhoenixPreparedStatement extends 
PhoenixStatement implements Prepar
 
     @Override
     public void setObject(int parameterIndex, Object o) throws SQLException {
+        if (o instanceof java.util.Date) {
+            // TODO add java.time when implemented
+            if (connection.isApplyTimeZoneDisplacement()) {
+                if (o instanceof java.sql.Timestamp) {
+                    o =
+                            
DateUtil.applyInputDisplacement((java.sql.Timestamp) o,
+                                localCalendar.getTimeZone());
+                } else if (o instanceof java.sql.Time) {
+                    o =
+                            DateUtil.applyInputDisplacement((java.sql.Time) o,
+                                localCalendar.getTimeZone());
+                } else if (o instanceof java.sql.Date) {
+                    o =
+                            DateUtil.applyInputDisplacement((java.sql.Date) o,
+                                localCalendar.getTimeZone());
+                }
+            }
+            // FIXME if we make a copy in setDate() from temporals, why not 
here ?
+        }
         setParameter(parameterIndex, o);
     }
 
@@ -485,39 +506,39 @@ public class PhoenixPreparedStatement extends 
PhoenixStatement implements Prepar
 
     @Override
     public void setTime(int parameterIndex, Time x) throws SQLException {
-        if (x != null) { // Since Time is mutable, make a copy
-            x = new Time(x.getTime());
-        }
-        setParameter(parameterIndex, x);
+        setTime(parameterIndex, x, localCalendar);
     }
 
     @Override
     public void setTime(int parameterIndex, Time x, Calendar cal) throws 
SQLException {
-        if (x != null) { // Since Time is mutable, make a copy
-            x = new Time(x.getTime());
-        }
-        cal.setTime(x);
-        setParameter(parameterIndex, new Time(cal.getTimeInMillis()));
-    }
-
-    private void setTimestampParameter(int parameterIndex, Timestamp x, 
Calendar cal) throws SQLException {
-        if (x != null) { // Since Timestamp is mutable, make a copy
-            int nanos = x.getNanos();
-            x = new Timestamp(x.getTime());
-            x.setNanos(nanos);
+        if (x != null) {
+            if (connection.isApplyTimeZoneDisplacement()) {
+                x = DateUtil.applyInputDisplacement(x, cal.getTimeZone());
+            } else {
+                // Since Date is mutable, make a copy
+                x = new Time(x.getTime());
+            }
         }
-        // TODO: deal with Calendar
         setParameter(parameterIndex, x);
     }
-    
+
     @Override
     public void setTimestamp(int parameterIndex, Timestamp x) throws 
SQLException {
-        setTimestampParameter(parameterIndex, x, null);
+        setTimestamp(parameterIndex, x, localCalendar);
     }
 
     @Override
     public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) 
throws SQLException {
-        setTimestampParameter(parameterIndex, x, cal);
+        if (x != null) {
+            if (connection.isApplyTimeZoneDisplacement()) {
+                x = DateUtil.applyInputDisplacement(x, cal.getTimeZone());
+            } else {
+                int nanos = x.getNanos();
+                x = new Timestamp(x.getTime());
+                x.setNanos(nanos);
+            }
+        }
+        setParameter(parameterIndex, x);
     }
 
     @Override
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixResultSet.java 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixResultSet.java
index 95ed713095..55a6de250b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixResultSet.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixResultSet.java
@@ -89,8 +89,12 @@ import org.apache.phoenix.schema.types.PSmallint;
 import org.apache.phoenix.schema.types.PTime;
 import org.apache.phoenix.schema.types.PTimestamp;
 import org.apache.phoenix.schema.types.PTinyint;
+import org.apache.phoenix.schema.types.PUnsignedDate;
+import org.apache.phoenix.schema.types.PUnsignedTime;
+import org.apache.phoenix.schema.types.PUnsignedTimestamp;
 import org.apache.phoenix.schema.types.PVarbinary;
 import org.apache.phoenix.schema.types.PVarchar;
+import org.apache.phoenix.util.DateUtil;
 import org.apache.phoenix.util.SQLCloseable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -144,6 +148,7 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
     private final boolean wildcardIncludesDynamicCols;
     private final List<PColumn> staticColumns;
     private final int startPositionForDynamicCols;
+    private final boolean isApplyTimeZoneDisplacement;
 
     private RowProjector rowProjectorWithDynamicCols;
     private Tuple currentRow = BEFORE_FIRST;
@@ -156,6 +161,7 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
     private Long count = 0L;
 
     private Object exception;
+    private final Calendar localCalendar;
 
     public PhoenixResultSet(ResultIterator resultIterator, RowProjector 
rowProjector,
             StatementContext ctx) throws SQLException {
@@ -177,6 +183,8 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
             this.staticColumns = null;
             this.startPositionForDynamicCols = 0;
         }
+        this.isApplyTimeZoneDisplacement = 
statement.getConnection().isApplyTimeZoneDisplacement();
+        this.localCalendar = statement.getLocalCalendar();
     }
     
     @Override
@@ -341,7 +349,7 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
         PDataType type = colProjector.getExpression().getDataType();
         Object value = colProjector.getValue(currentRow, type, ptr);
         wasNull = (value == null);
-        if (value == null) {
+        if (wasNull) {
             return false;
         }
         if (type == PBoolean.INSTANCE) {
@@ -427,14 +435,7 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
 
     @Override
     public Date getDate(int columnIndex) throws SQLException {
-        checkCursorState();
-        Date value = 
(Date)getRowProjector().getColumnProjector(columnIndex-1).getValue(currentRow,
-            PDate.INSTANCE, ptr);
-        wasNull = (value == null);
-        if (value == null) {
-            return null;
-        }
-        return value;
+        return getDate(columnIndex, localCalendar);
     }
 
     @Override
@@ -445,14 +446,18 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
     @Override
     public Date getDate(int columnIndex, Calendar cal) throws SQLException {
         checkCursorState();
-        Date value = 
(Date)getRowProjector().getColumnProjector(columnIndex-1).getValue(currentRow,
-            PDate.INSTANCE, ptr);
+        Date value =
+                (Date) getRowProjector().getColumnProjector(columnIndex - 
1).getValue(currentRow,
+                    PDate.INSTANCE, ptr);
         wasNull = (value == null);
         if (wasNull) {
             return null;
         }
-        cal.setTime(value);
-        return new Date(cal.getTimeInMillis());
+        if (isApplyTimeZoneDisplacement) {
+            return DateUtil.applyOutputDisplacement(value, cal.getTimeZone());
+        } else {
+            return value;
+        }
     }
 
     @Override
@@ -466,7 +471,7 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
         Double value = 
(Double)getRowProjector().getColumnProjector(columnIndex-1)
                 .getValue(currentRow, PDouble.INSTANCE, ptr);
         wasNull = (value == null);
-        if (value == null) {
+        if (wasNull) {
             return 0;
         }
         return value;
@@ -493,7 +498,7 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
         Float value = 
(Float)getRowProjector().getColumnProjector(columnIndex-1)
                 .getValue(currentRow, PFloat.INSTANCE, ptr);
         wasNull = (value == null);
-        if (value == null) {
+        if (wasNull) {
             return 0;
         }
         return value;
@@ -515,7 +520,7 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
         Integer value = 
(Integer)getRowProjector().getColumnProjector(columnIndex-1)
                 .getValue(currentRow, PInteger.INSTANCE, ptr);
         wasNull = (value == null);
-        if (value == null) {
+        if (wasNull) {
             return 0;
         }
         return value;
@@ -532,7 +537,7 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
         Long value = 
(Long)getRowProjector().getColumnProjector(columnIndex-1).getValue(currentRow,
             PLong.INSTANCE, ptr);
         wasNull = (value == null);
-        if (value == null) {
+        if (wasNull) {
             return 0;
         }
         return value;
@@ -581,9 +586,25 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
     @Override
     public Object getObject(int columnIndex) throws SQLException {
         checkCursorState();
-        ColumnProjector projector = 
getRowProjector().getColumnProjector(columnIndex-1);
+        ColumnProjector projector = 
getRowProjector().getColumnProjector(columnIndex - 1);
         Object value = projector.getValue(currentRow, 
projector.getExpression().getDataType(), ptr);
         wasNull = (value == null);
+        if (isApplyTimeZoneDisplacement) {
+            PDataType type = projector.getExpression().getDataType();
+            if (type == PDate.INSTANCE || type == PUnsignedDate.INSTANCE) {
+                value =
+                        DateUtil.applyOutputDisplacement((java.sql.Date) value,
+                            localCalendar.getTimeZone());
+            } else if (type == PTime.INSTANCE || type == 
PUnsignedTime.INSTANCE) {
+                value =
+                        DateUtil.applyOutputDisplacement((java.sql.Time) value,
+                            localCalendar.getTimeZone());
+            } else if (type == PTimestamp.INSTANCE || type == 
PUnsignedTimestamp.INSTANCE) {
+                value =
+                        DateUtil.applyOutputDisplacement((java.sql.Timestamp) 
value,
+                            localCalendar.getTimeZone());
+            }
+        }
         return value;
     }
 
@@ -644,7 +665,7 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
         Short value = 
(Short)getRowProjector().getColumnProjector(columnIndex-1)
                 .getValue(currentRow, PSmallint.INSTANCE, ptr);
         wasNull = (value == null);
-        if (value == null) {
+        if (wasNull) {
             return 0;
         }
         return value;
@@ -668,7 +689,8 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
         ColumnProjector projector = 
getRowProjector().getColumnProjector(columnIndex-1);
         PDataType type = projector.getExpression().getDataType();
         Object value = projector.getValue(currentRow,type, ptr);
-        if (wasNull = (value == null)) {
+        wasNull = (value == null);
+        if (wasNull) {
             return null;
         }
         // Run Object through formatter to get String.
@@ -685,11 +707,7 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
 
     @Override
     public Time getTime(int columnIndex) throws SQLException {
-        checkCursorState();
-        Time value = 
(Time)getRowProjector().getColumnProjector(columnIndex-1).getValue(currentRow,
-            PTime.INSTANCE, ptr);
-        wasNull = (value == null);
-        return value;
+        return getTime(columnIndex, localCalendar);
     }
 
     @Override
@@ -703,12 +721,14 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
         Time value = 
(Time)getRowProjector().getColumnProjector(columnIndex-1).getValue(currentRow,
             PTime.INSTANCE, ptr);
         wasNull = (value == null);
-        if (value == null) {
+        if (wasNull) {
             return null;
         }
-        cal.setTime(value);
-        value.setTime(cal.getTimeInMillis());
-        return value;
+        if (isApplyTimeZoneDisplacement) {
+            return DateUtil.applyOutputDisplacement(value, cal.getTimeZone());
+        } else {
+            return value;
+        }
     }
 
     @Override
@@ -718,11 +738,7 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
 
     @Override
     public Timestamp getTimestamp(int columnIndex) throws SQLException {
-        checkCursorState();
-        Timestamp value = 
(Timestamp)getRowProjector().getColumnProjector(columnIndex-1)
-                .getValue(currentRow, PTimestamp.INSTANCE, ptr);
-        wasNull = (value == null);
-        return value;
+        return getTimestamp(columnIndex, localCalendar);
     }
 
     @Override
@@ -732,8 +748,18 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
 
     @Override
     public Timestamp getTimestamp(int columnIndex, Calendar cal) throws 
SQLException {
-        return getTimestamp(columnIndex);
-    }
+        checkCursorState();
+        Timestamp value = 
(Timestamp)getRowProjector().getColumnProjector(columnIndex-1)
+                .getValue(currentRow, PTimestamp.INSTANCE, ptr);
+        wasNull = (value == null);
+        if (wasNull) {
+            return null;
+        }
+        if (isApplyTimeZoneDisplacement) {
+            return DateUtil.applyOutputDisplacement(value, cal.getTimeZone());
+        } else {
+            return value;
+        }    }
 
     @Override
     public Timestamp getTimestamp(String columnLabel, Calendar cal) throws 
SQLException {
@@ -751,7 +777,7 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
         String value = 
(String)getRowProjector().getColumnProjector(columnIndex-1)
                 .getValue(currentRow, PVarchar.INSTANCE, ptr);
         wasNull = (value == null);
-        if (value == null) {
+        if (wasNull) {
             return null;
         }
         try {
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
index a20dd5c007..5562c8d5c5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
@@ -34,6 +34,7 @@ import java.sql.Statement;
 import java.text.Format;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -257,7 +258,9 @@ public class PhoenixStatement implements Statement, 
SQLCloseable {
     private int maxRows;
     private int fetchSize = -1;
     private int queryTimeoutMillis;
-    
+    // Caching per Statement
+    protected final Calendar localCalendar = Calendar.getInstance();
+
     public PhoenixStatement(PhoenixConnection connection) {
         this.connection = connection;
         this.queryTimeoutMillis = getDefaultQueryTimeoutMillis();
@@ -2328,4 +2331,9 @@ public class PhoenixStatement implements Statement, 
SQLCloseable {
             }
         }
     }
+
+    public Calendar getLocalCalendar() {
+        return localCalendar;
+    }
+
 }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/parse/CurrentDateParseNode.java 
b/phoenix-core/src/main/java/org/apache/phoenix/parse/CurrentDateParseNode.java
index 39f24afa75..05e66b66be 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/parse/CurrentDateParseNode.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/parse/CurrentDateParseNode.java
@@ -34,6 +34,6 @@ public class CurrentDateParseNode extends FunctionParseNode {
 
     @Override
     public FunctionExpression create(List<Expression> children, 
StatementContext context) throws SQLException {
-        return new CurrentDateFunction(context.getCurrentTime());
+        return new 
CurrentDateFunction(context.getCurrentTimeWithDisplacement());
     }
 }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/parse/CurrentTimeParseNode.java 
b/phoenix-core/src/main/java/org/apache/phoenix/parse/CurrentTimeParseNode.java
index 4f8815bf96..680b7efebb 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/parse/CurrentTimeParseNode.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/parse/CurrentTimeParseNode.java
@@ -34,6 +34,6 @@ public class CurrentTimeParseNode extends FunctionParseNode {
 
     @Override
     public FunctionExpression create(List<Expression> children, 
StatementContext context) throws SQLException {
-        return new CurrentTimeFunction(context.getCurrentTime());
+        return new 
CurrentTimeFunction(context.getCurrentTimeWithDisplacement());
     }
 }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/parse/ToDateParseNode.java 
b/phoenix-core/src/main/java/org/apache/phoenix/parse/ToDateParseNode.java
index fd4d16a9d0..b84dfc33d2 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ToDateParseNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ToDateParseNode.java
@@ -41,7 +41,7 @@ public class ToDateParseNode extends FunctionParseNode {
             dateFormat = context.getDateFormat();
         }
         if (timeZoneId == null) {
-            timeZoneId = context.getDateFormatTimeZone().getID();
+            timeZoneId = context.getDateFormatTimeZoneId();
         }
         return new ToDateFunction(children, dateFormat, timeZoneId);
     }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/parse/ToTimeParseNode.java 
b/phoenix-core/src/main/java/org/apache/phoenix/parse/ToTimeParseNode.java
index 5f0a72db1d..eb84008a60 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ToTimeParseNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ToTimeParseNode.java
@@ -41,7 +41,7 @@ public class ToTimeParseNode extends FunctionParseNode {
             dateFormat = context.getTimeFormat();
         }
         if (timeZoneId == null) {
-            timeZoneId = context.getDateFormatTimeZone().getID();
+            timeZoneId = context.getDateFormatTimeZoneId();
         }
         return new ToTimeFunction(children, dateFormat, timeZoneId);
     }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/parse/ToTimestampParseNode.java 
b/phoenix-core/src/main/java/org/apache/phoenix/parse/ToTimestampParseNode.java
index 2a3f5ecfab..771c812d41 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/parse/ToTimestampParseNode.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/parse/ToTimestampParseNode.java
@@ -41,7 +41,7 @@ public class ToTimestampParseNode extends FunctionParseNode {
             dateFormat = context.getTimestampFormat();
         }
         if (timeZoneId == null) {
-            timeZoneId = context.getDateFormatTimeZone().getID();
+            timeZoneId = context.getDateFormatTimeZoneId();
         }
         return new ToTimestampFunction(children, dateFormat, timeZoneId);
     }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java 
b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
index 387051003b..3b6661b04e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
@@ -87,6 +87,7 @@ public interface QueryServices extends SQLCloseable {
     public static final String MAX_MEMORY_PERC_ATTRIB = 
"phoenix.query.maxGlobalMemoryPercentage";
     public static final String MAX_TENANT_MEMORY_PERC_ATTRIB = 
"phoenix.query.maxTenantMemoryPercentage";
     public static final String MAX_SERVER_CACHE_SIZE_ATTRIB = 
"phoenix.query.maxServerCacheBytes";
+    public static final String APPLY_TIME_ZONE_DISPLACMENT_ATTRIB = 
"phoenix.query.applyTimeZoneDisplacement";
     public static final String DATE_FORMAT_TIMEZONE_ATTRIB = 
"phoenix.query.dateFormatTimeZone";
     public static final String DATE_FORMAT_ATTRIB = "phoenix.query.dateFormat";
     public static final String TIME_FORMAT_ATTRIB = "phoenix.query.timeFormat";
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java 
b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
index 21bcf9a771..7c73e3d342 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
@@ -379,6 +379,7 @@ public class QueryServicesOptions {
     public static final boolean DEFAULT_PENDING_MUTATIONS_DDL_THROW = false;
 
     public static final boolean DEFAULT_SKIP_SYSTEM_TABLES_EXISTENCE_CHECK = 
false;
+    public static final boolean DEFAULT_APPLY_TIME_ZONE_DISPLACMENT = false;
 
     private final Configuration config;
 
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/DateUtil.java 
b/phoenix-core/src/main/java/org/apache/phoenix/util/DateUtil.java
index 9122f6195b..fefdc8d3ad 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/DateUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/DateUtil.java
@@ -54,6 +54,7 @@ import edu.umd.cs.findbugs.annotations.NonNull;
 
 @SuppressWarnings({ "serial", "deprecation" })
 public class DateUtil {
+
     public static final String DEFAULT_TIME_ZONE_ID = "GMT";
     public static final String LOCAL_TIME_ZONE_ID = "LOCAL";
     private static final TimeZone DEFAULT_TIME_ZONE = 
TimeZone.getTimeZone(DEFAULT_TIME_ZONE_ID);
@@ -71,6 +72,9 @@ public class DateUtil {
     public static final String DEFAULT_TIMESTAMP_FORMAT = 
DEFAULT_MS_DATE_FORMAT;
     public static final Format DEFAULT_TIMESTAMP_FORMATTER = 
DEFAULT_MS_DATE_FORMATTER;
 
+    //Caching for performance. We don't expect the default TZ to changed after 
startup
+    //private static final java.util.TimeZone LOCAL_TIME_ZONE = 
TimeZone.getDefault();
+
     private static final DateTimeFormatter JULIAN_DATE_TIME_FORMATTER = new 
DateTimeFormatterBuilder()
         .append(ISODateTimeFormat.dateParser())
         .appendOptional(new DateTimeFormatterBuilder()
@@ -187,18 +191,42 @@ public class DateUtil {
                 : FastDateFormat.getInstance(pattern, getTimeZone(timeZoneID));
     }
 
+    /**
+     * Parses a datetime string in the UTC time zone.
+     *
+     * @param dateValue datetime string in UTC
+     * @return epoch ms
+     */
     private static long parseDateTime(String dateTimeValue) {
         return 
JulianDateFormatParser.getInstance().parseDateTime(dateTimeValue);
     }
 
+    /**
+     * Parses a date string in the UTC time zone.
+     *
+     * @param dateValue date string in UTC
+     * @return epoch ms
+     */
     public static Date parseDate(String dateValue) {
         return new Date(parseDateTime(dateValue));
     }
 
+    /**
+     * Parses a time string in the UTC time zone.
+     *
+     * @param dateValue time string in UTC
+     * @return epoch ms
+     */
     public static Time parseTime(String timeValue) {
         return new Time(parseDateTime(timeValue));
     }
 
+    /**
+     * Parses the timestsamp string in the UTC time zone.
+     *
+     * @param timestampValue timestamp string in UTC
+     * @return Timestamp parsed in UTC
+     */
     public static Timestamp parseTimestamp(String timestampValue) {
         Timestamp timestamp = new Timestamp(parseDateTime(timestampValue));
         int period = timestampValue.indexOf('.');
@@ -392,4 +420,93 @@ public class DateUtil {
             }
         }
     }
+
+    // These implementations favour speed over historical correctness, and use
+    // java.util.TimeZone#getOffset(epoch millis) and inherit its limitations.
+
+    // When we switch to java.time, we might want to revisit this, and add an 
option for
+    // slower but more correct conversions.
+    // However, any conversion for TZs with DST is best effort anyway.
+
+    /**
+     * Apply the time zone displacement to the input, so that the output 
represents the same
+     * LocalDateTime in the UTC time zone as the Input in the specified time 
zone.
+     * @param jdbc Date interpreted in timeZone
+     * @param timeZone for displacement calculation
+     * @return input with the TZ displacement applied
+     */
+    public static java.sql.Date applyInputDisplacement(java.sql.Date jdbc, 
TimeZone timeZone) {
+        long epoch = jdbc.getTime();
+        return new java.sql.Date(epoch + timeZone.getOffset(epoch));
+    }
+
+    /**
+     * Apply the time zone displacement to the input, so that the output 
represents the same
+     * LocalDateTime in the UTC time zone as the Input in the specified time 
zone.
+     * @param jdbc Time interpreted in timeZone
+     * @param timeZone for displacement calculation
+     * @return input with the TZ displacement applied
+     */
+    public static java.sql.Time applyInputDisplacement(java.sql.Time jdbc, 
TimeZone timeZone) {
+        long epoch = jdbc.getTime();
+        return new java.sql.Time(epoch + timeZone.getOffset(epoch));
+    }
+
+    /**
+     * Apply the time zone displacement to the input, so that the output 
represents the same
+     * LocalDateTime in the UTC time zone as the Input in the specified time 
zone.
+     * @param jdbc Timestamp interpreted in timeZone
+     * @param timeZone for displacement calculation
+     * @return input with the TZ displacement applied
+     */
+    public static java.sql.Timestamp applyInputDisplacement(java.sql.Timestamp 
jdbc, TimeZone timeZone) {
+        long epoch = jdbc.getTime();
+        java.sql.Timestamp ts = new java.sql.Timestamp(epoch + 
timeZone.getOffset(epoch));
+        ts.setNanos(jdbc.getNanos());
+        return ts;
+    }
+
+    /**
+     * Apply the time zone displacement to the input, so that the output 
represents the same
+     * LocalDateTime in the specified time zone as the Input in the UTC time 
zone.
+     * @param internal Date as UTC epoch
+     * @param timeZone for displacement calculation
+     * @return input with the TZ displacement applied
+     */
+    public static java.sql.Date applyOutputDisplacement(java.sql.Date 
internal, TimeZone timeZone) {
+        long epoch = internal.getTime();
+        return new java.sql.Date(epoch - getReverseOffset(epoch, timeZone));
+    }
+
+    /**
+     * Apply the time zone displacement to the input, so that the output 
represents the same
+     * LocalDateTime in the specified time zone as the Input in the UTC time 
zone.
+     * @param internal Date as UTC epoch
+     * @param timeZone for displacement calculation
+     * @return input with the TZ displacement applied
+     */
+    public static java.sql.Time applyOutputDisplacement(java.sql.Time 
internal, TimeZone timeZone) {
+        long epoch = internal.getTime();
+        return new java.sql.Time(epoch - getReverseOffset(epoch, timeZone));
+    }
+
+    /**
+     * Apply the time zone displacement to the input, so that the output 
represents the same
+     * LocalDateTime in the specified time zone as the Input in the UTC time 
zone.
+     * @param internal Timestamp as UTC epoch
+     * @param timeZone for displacement calculation
+     * @return input with the TZ displacement applied
+     */
+    public static java.sql.Timestamp 
applyOutputDisplacement(java.sql.Timestamp internal, TimeZone timeZone) {
+        long epoch = internal.getTime();
+        java.sql.Timestamp ts = new java.sql.Timestamp(epoch - 
getReverseOffset(epoch, timeZone));
+        ts.setNanos(internal.getNanos());
+        return ts;
+    }
+
+    private static int getReverseOffset(long epoch, TimeZone tz) {
+        return tz.getOffset(
+            epoch - tz.getRawOffset() - tz.getDSTSavings());
+    }
+
 }
diff --git 
a/phoenix-core/src/test/java/org/apache/phoenix/util/DateUtilTest.java 
b/phoenix-core/src/test/java/org/apache/phoenix/util/DateUtilTest.java
index 9ecce295f2..9a9408157a 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/util/DateUtilTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/util/DateUtilTest.java
@@ -19,12 +19,17 @@ package org.apache.phoenix.util;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
 import java.sql.Date;
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.text.ParseException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
 import java.util.TimeZone;
 
 import org.apache.phoenix.schema.IllegalDataException;
@@ -196,4 +201,170 @@ public class DateUtilTest {
     public void testParseTimestamp_InvalidTimestamp() {
         DateUtil.parseTimestamp("not-a-timestamp");
     }
+
+    // This test absolutely relies on JVM TZ being set to America/Los_Angeles,
+    // and is going to fail otherwise. Maven already sets this.
+    @Test
+    public void testTZCorrection() {
+
+        TimeZone tz = TimeZone.getDefault();
+
+        // First with the current time
+        LocalDateTime nowLDT = LocalDateTime.now();
+        Instant nowInstantLocal = 
nowLDT.atZone(ZoneId.systemDefault()).toInstant();
+        Instant nowInstantGMT = nowLDT.atZone(ZoneOffset.UTC).toInstant();
+
+        java.sql.Date sqlDateNowLocal = new 
java.sql.Date(nowInstantLocal.toEpochMilli());
+        java.sql.Time sqlTimeNowLocal = new 
java.sql.Time(nowInstantLocal.toEpochMilli());
+        java.sql.Timestamp sqlTimestampNowLocal =
+                new java.sql.Timestamp(nowInstantLocal.toEpochMilli());
+
+        java.sql.Date sqlDateNowGMT = new 
java.sql.Date(nowInstantGMT.toEpochMilli());
+        java.sql.Time sqlTimeNowGMT = new 
java.sql.Time(nowInstantGMT.toEpochMilli());
+        java.sql.Timestamp sqlTimestampNowGMT =
+                new java.sql.Timestamp(nowInstantGMT.toEpochMilli());
+
+        assertEquals(DateUtil.applyInputDisplacement(sqlDateNowLocal, tz), 
sqlDateNowGMT);
+        assertEquals(DateUtil.applyInputDisplacement(sqlTimeNowLocal, tz), 
sqlTimeNowGMT);
+        assertEquals(DateUtil.applyInputDisplacement(sqlTimestampNowLocal, 
tz), sqlTimestampNowGMT);
+
+        assertEquals(DateUtil.applyOutputDisplacement(sqlDateNowGMT, tz), 
sqlDateNowLocal);
+        assertEquals(DateUtil.applyOutputDisplacement(sqlTimeNowGMT, tz), 
sqlTimeNowLocal);
+        assertEquals(DateUtil.applyOutputDisplacement(sqlTimestampNowGMT, tz),
+            sqlTimestampNowLocal);
+
+        // Make sure that we don't use a fixed offset
+
+        LocalDateTime summerLDT = LocalDateTime.of(2023, 6, 01, 10, 10, 10);
+        LocalDateTime winterLDT = LocalDateTime.of(2023, 1, 01, 10, 10, 10);
+
+        Instant summerInstantLocal = 
summerLDT.atZone(ZoneId.systemDefault()).toInstant();
+        Instant summerInstantDisplaced = 
summerLDT.atZone(ZoneOffset.UTC).toInstant();
+
+        Instant winterInstantLocal = 
winterLDT.atZone(ZoneId.systemDefault()).toInstant();
+        Instant winterInstantDisplaced = 
winterLDT.atZone(ZoneOffset.UTC).toInstant();
+
+        java.sql.Date sqlDateSummerLocal = new 
java.sql.Date(summerInstantLocal.toEpochMilli());
+        java.sql.Time sqlTimeSummerLocal = new 
java.sql.Time(summerInstantLocal.toEpochMilli());
+        java.sql.Timestamp sqlTimestampSummerLocal =
+                new java.sql.Timestamp(summerInstantLocal.toEpochMilli());
+
+        java.sql.Date sqlDateSummerDisplaced =
+                new java.sql.Date(summerInstantDisplaced.toEpochMilli());
+        java.sql.Time sqlTimeSummerDisplaced =
+                new java.sql.Time(summerInstantDisplaced.toEpochMilli());
+        java.sql.Timestamp sqlTimestampSummerDisplaced =
+                new java.sql.Timestamp(summerInstantDisplaced.toEpochMilli());
+
+        java.sql.Date sqlDateWinterLocal = new 
java.sql.Date(winterInstantLocal.toEpochMilli());
+        java.sql.Time sqlTimeWinterLocal = new 
java.sql.Time(winterInstantLocal.toEpochMilli());
+        java.sql.Timestamp sqlTimestampWinterLocal =
+                new java.sql.Timestamp(winterInstantLocal.toEpochMilli());
+
+        java.sql.Date sqlDateWinterDisplaced =
+                new java.sql.Date(winterInstantDisplaced.toEpochMilli());
+        java.sql.Time sqlTimeWinterDisplaced =
+                new java.sql.Time(winterInstantDisplaced.toEpochMilli());
+        java.sql.Timestamp sqlTimestampWinterDisplaced =
+                new java.sql.Timestamp(winterInstantDisplaced.toEpochMilli());
+
+        assertEquals(DateUtil.applyInputDisplacement(sqlDateSummerLocal, tz),
+            sqlDateSummerDisplaced);
+        assertEquals(DateUtil.applyInputDisplacement(sqlTimeSummerLocal, tz),
+            sqlTimeSummerDisplaced);
+        assertEquals(DateUtil.applyInputDisplacement(sqlTimestampSummerLocal, 
tz),
+            sqlTimestampSummerDisplaced);
+
+        assertEquals(DateUtil.applyOutputDisplacement(sqlDateSummerDisplaced, 
tz),
+            sqlDateSummerLocal);
+        assertEquals(DateUtil.applyOutputDisplacement(sqlTimeSummerDisplaced, 
tz),
+            sqlTimeSummerLocal);
+        
assertEquals(DateUtil.applyOutputDisplacement(sqlTimestampSummerDisplaced, tz),
+            sqlTimestampSummerLocal);
+
+        assertEquals(DateUtil.applyInputDisplacement(sqlDateWinterLocal, tz),
+            sqlDateWinterDisplaced);
+        assertEquals(DateUtil.applyInputDisplacement(sqlTimeWinterLocal, tz),
+            sqlTimeWinterDisplaced);
+        assertEquals(DateUtil.applyInputDisplacement(sqlTimestampWinterLocal, 
tz),
+            sqlTimestampWinterDisplaced);
+
+        assertEquals(DateUtil.applyOutputDisplacement(sqlDateWinterDisplaced, 
tz),
+            sqlDateWinterLocal);
+        assertEquals(DateUtil.applyOutputDisplacement(sqlTimeWinterDisplaced, 
tz),
+            sqlTimeWinterLocal);
+        
assertEquals(DateUtil.applyOutputDisplacement(sqlTimestampWinterDisplaced, tz),
+            sqlTimestampWinterLocal);
+
+        // This also demonstrates why you SHOULD NOT use the java.sql. 
temporal types with
+        // WITHOUT TIMEZONE types.
+
+        // Check the dates around DST switch
+        ZoneId pacific = ZoneId.of("America/Los_Angeles");
+        assertEquals("Test must be run in America/Los_Angeles time zone", 
ZoneId.systemDefault(),
+            pacific);
+        LocalDateTime endOfWinter = LocalDateTime.of(2023, 3, 12, 1, 59, 59);
+        // There is no 2:00, the next time is 3:00
+        LocalDateTime nonExistent = LocalDateTime.of(2023, 3, 12, 2, 0, 0);
+        LocalDateTime startOfSummer = LocalDateTime.of(2023, 3, 12, 3, 0, 0);
+        LocalDateTime endOfSummer = LocalDateTime.of(2023, 1, 05, 00, 59, 59);
+        // Time warps back to 1:00 instead of reaching 2:00 the first time
+        LocalDateTime ambiguous = LocalDateTime.of(2023, 1, 05, 1, 30, 0);
+        LocalDateTime startOfWinter = LocalDateTime.of(2023, 1, 05, 2, 0, 0);
+
+        java.sql.Timestamp endOfWinterLocal =
+                
java.sql.Timestamp.from(endOfWinter.atZone(pacific).toInstant());
+        java.sql.Timestamp endOfWinterDisplaced =
+                
java.sql.Timestamp.from(endOfWinter.atZone(ZoneOffset.UTC).toInstant());
+        assertEquals(DateUtil.applyInputDisplacement(endOfWinterLocal, tz), 
endOfWinterDisplaced);
+        assertEquals(DateUtil.applyOutputDisplacement(endOfWinterDisplaced, 
tz), endOfWinterLocal);
+
+        java.sql.Timestamp startOfSummerLocal =
+                
java.sql.Timestamp.from(startOfSummer.atZone(pacific).toInstant());
+        java.sql.Timestamp startOfSummerDisplaced =
+                
java.sql.Timestamp.from(startOfSummer.atZone(ZoneOffset.UTC).toInstant());
+        assertEquals(DateUtil.applyInputDisplacement(startOfSummerLocal, tz),
+            startOfSummerDisplaced);
+        assertEquals(DateUtil.applyOutputDisplacement(startOfSummerDisplaced, 
tz),
+            startOfSummerLocal);
+
+        // This just gives us 3:00
+        java.sql.Timestamp nonExistentLocal =
+                
java.sql.Timestamp.from(nonExistent.atZone(pacific).toInstant());
+        assertEquals(nonExistentLocal, startOfSummerLocal);
+        java.sql.Timestamp nonExistentDisplaced =
+                
java.sql.Timestamp.from(nonExistent.atZone(ZoneOffset.UTC).toInstant());
+        // we get a valid date
+        assertEquals(DateUtil.applyInputDisplacement(nonExistentLocal, tz), 
startOfSummerDisplaced);
+        // This conversion is ambigiuous, but in this direction we get one 
Local date for two
+        // different displaced dates
+        assertNotEquals(nonExistentDisplaced, startOfSummerDisplaced);
+        assertEquals(DateUtil.applyOutputDisplacement(nonExistentDisplaced, 
tz), nonExistentLocal);
+        assertEquals(DateUtil.applyOutputDisplacement(startOfSummerDisplaced, 
tz),
+            nonExistentLocal);
+
+        java.sql.Timestamp endOfSummerLocal =
+                
java.sql.Timestamp.from(endOfSummer.atZone(pacific).toInstant());
+        java.sql.Timestamp endOfSummerDisplaced =
+                
java.sql.Timestamp.from(endOfSummer.atZone(ZoneOffset.UTC).toInstant());
+        assertEquals(DateUtil.applyInputDisplacement(endOfSummerLocal, tz), 
endOfSummerDisplaced);
+        assertEquals(DateUtil.applyOutputDisplacement(endOfSummerDisplaced, 
tz), endOfSummerLocal);
+
+        // Confirm that we do the same thing as Java
+        java.sql.Timestamp ambiguousLocal =
+                java.sql.Timestamp.from(ambiguous.atZone(pacific).toInstant());
+        java.sql.Timestamp ambiguousDisplaced =
+                
java.sql.Timestamp.from(ambiguous.atZone(ZoneOffset.UTC).toInstant());
+        assertEquals(DateUtil.applyInputDisplacement(ambiguousLocal, tz), 
ambiguousDisplaced);
+        assertEquals(DateUtil.applyOutputDisplacement(ambiguousDisplaced, tz), 
ambiguousLocal);
+
+        java.sql.Timestamp startOfWinterLocal =
+                
java.sql.Timestamp.from(startOfWinter.atZone(pacific).toInstant());
+        java.sql.Timestamp startOfWinterDisplaced =
+                
java.sql.Timestamp.from(startOfWinter.atZone(ZoneOffset.UTC).toInstant());
+        assertEquals(DateUtil.applyInputDisplacement(startOfWinterLocal, tz),
+            startOfWinterDisplaced);
+        assertEquals(DateUtil.applyOutputDisplacement(startOfWinterDisplaced, 
tz),
+            startOfWinterLocal);
+    }
 }
diff --git a/pom.xml b/pom.xml
index f7217b0820..628bef3eed 100644
--- a/pom.xml
+++ b/pom.xml
@@ -329,7 +329,7 @@
               <id>ParallelStatsEnabledTest</id>
               <configuration>
                 <reuseForks>true</reuseForks>
-                <argLine>@{jacocoArgLine} -Xmx3000m 
-Djava.security.egd=file:/dev/./urandom 
"-Djava.library.path=${hadoop.library.path}${path.separator}${java.library.path}"
 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./target/ -XX:NewRatio=4 
-XX:SurvivorRatio=8 -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC 
-XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly 
-XX:+CMSClassUnloadingEnabled -XX:+CMSScavengeBeforeRemark 
-XX:CMSInitiatingOccupancyFraction=68</argLine>
+                <argLine>@{jacocoArgLine} 
-Duser.timezone="America/Los_Angeles" -Xmx3000m 
-Djava.security.egd=file:/dev/./urandom 
"-Djava.library.path=${hadoop.library.path}${path.separator}${java.library.path}"
 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./target/ -XX:NewRatio=4 
-XX:SurvivorRatio=8 -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC 
-XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly 
-XX:+CMSClassUnloadingEnabled -XX:+CMSScavengeBeforeRemark 
-XX:CMSInitiatingOccup [...]
                 
<groups>org.apache.phoenix.end2end.ParallelStatsEnabledTest</groups>
               </configuration>
               <goals>
@@ -341,7 +341,7 @@
               <id>ParallelStatsDisabledTest</id>
               <configuration>
                 <reuseForks>true</reuseForks>
-                <argLine>@{jacocoArgLine} -Xmx3000m 
-Djava.security.egd=file:/dev/./urandom 
"-Djava.library.path=${hadoop.library.path}${path.separator}${java.library.path}"
 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./target/ -XX:NewRatio=4 
-XX:SurvivorRatio=8 -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC 
-XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly 
-XX:+CMSClassUnloadingEnabled -XX:+CMSScavengeBeforeRemark 
-XX:CMSInitiatingOccupancyFraction=68</argLine>
+                <argLine>@{jacocoArgLine} 
-Duser.timezone="America/Los_Angeles" -Xmx3000m 
-Djava.security.egd=file:/dev/./urandom 
"-Djava.library.path=${hadoop.library.path}${path.separator}${java.library.path}"
 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./target/ -XX:NewRatio=4 
-XX:SurvivorRatio=8 -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC 
-XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly 
-XX:+CMSClassUnloadingEnabled -XX:+CMSScavengeBeforeRemark 
-XX:CMSInitiatingOccup [...]
                 
<groups>org.apache.phoenix.end2end.ParallelStatsDisabledTest</groups>
               </configuration>
               <goals>
@@ -353,7 +353,7 @@
               <id>NeedTheirOwnClusterTests</id>
               <configuration>
                  <reuseForks>false</reuseForks>
-                 <argLine>@{jacocoArgLine} -enableassertions -Xmx3000m 
-Djava.security.egd=file:/dev/./urandom 
"-Djava.library.path=${hadoop.library.path}${path.separator}${java.library.path}"
 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./target/ -XX:NewRatio=4 
-XX:SurvivorRatio=8 -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC 
-XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly 
-XX:+CMSClassUnloadingEnabled -XX:+CMSScavengeBeforeRemark 
-XX:CMSInitiatingOccupancyFraction=68</argLine>
+                 <argLine>@{jacocoArgLine} 
-Duser.timezone="America/Los_Angeles" -enableassertions -Xmx3000m 
-Djava.security.egd=file:/dev/./urandom 
"-Djava.library.path=${hadoop.library.path}${path.separator}${java.library.path}"
 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./target/ -XX:NewRatio=4 
-XX:SurvivorRatio=8 -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC 
-XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly 
-XX:+CMSClassUnloadingEnabled -XX:+CMSScavengeBeforeRemark -XX [...]
                  
<groups>org.apache.phoenix.end2end.NeedsOwnMiniClusterTest</groups>
               </configuration>
               <goals>
@@ -574,7 +574,7 @@
         <configuration>
           <forkCount>${numForkedUT}</forkCount>
           <reuseForks>true</reuseForks>
-          <argLine>@{jacocoArgLine} -enableassertions -Xmx2250m
+          <argLine>@{jacocoArgLine} -enableassertions -Xmx2250m 
-Duser.timezone="America/Los_Angeles"
             -Djava.security.egd=file:/dev/./urandom 
"-Djava.library.path=${hadoop.library.path}${path.separator}${java.library.path}"
 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./target/</argLine>
           
<redirectTestOutputToFile>${test.output.tofile}</redirectTestOutputToFile>
           <shutdown>exit</shutdown>

Reply via email to