Repository: phoenix
Updated Branches:
  refs/heads/4.0 429acbab7 -> 3969ed5c1


PHOENIX-1485 Add timezone awareness

* Add tz-aware methods to DateUtil
* Add timezone parameter to TO_DATE
* Add a configuration parameter to allow specifying the time zone
  to be used internally on a connection when parsing dates.


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/ea10e1f7
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/ea10e1f7
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/ea10e1f7

Branch: refs/heads/4.0
Commit: ea10e1f7bd657e0f8b890571f3f1903f07eb43dd
Parents: 429acba
Author: Gabriel Reid <gr...@apache.org>
Authored: Tue Dec 2 19:25:41 2014 +0100
Committer: Gabriel Reid <gabri...@ngdata.com>
Committed: Wed Dec 3 17:28:33 2014 +0100

----------------------------------------------------------------------
 .../phoenix/end2end/ToDateFunctionIT.java       | 117 +++++++++++++++++++
 .../phoenix/compile/StatementContext.java       |  44 +++----
 .../expression/function/ToDateFunction.java     |  16 ++-
 .../apache/phoenix/parse/ToDateParseNode.java   |  15 ++-
 .../org/apache/phoenix/query/QueryServices.java |   1 +
 .../phoenix/query/QueryServicesOptions.java     |   3 +
 .../java/org/apache/phoenix/util/DateUtil.java  |  37 ++++--
 .../expression/SortOrderExpressionTest.java     |   3 +-
 .../org/apache/phoenix/util/DateUtilTest.java   |  78 ++++++++++++-
 9 files changed, 268 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/ea10e1f7/phoenix-core/src/it/java/org/apache/phoenix/end2end/ToDateFunctionIT.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ToDateFunctionIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ToDateFunctionIT.java
new file mode 100644
index 0000000..19257c1
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ToDateFunctionIT.java
@@ -0,0 +1,117 @@
+/*
+ * 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 java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+
+import org.apache.phoenix.query.QueryServices;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+public class ToDateFunctionIT extends BaseHBaseManagedTimeIT {
+
+    private static final long ONE_HOUR_IN_MILLIS = 1000L * 60L * 60L;
+
+    private Connection conn;
+
+    @Before
+    public void setUp() throws SQLException {
+        conn = DriverManager.getConnection(getUrl());
+    }
+
+    @After
+    public void tearDown() throws SQLException {
+        conn.close();
+    }
+
+    private static Date callToDateFunction(Connection conn, String invocation) 
throws SQLException {
+        Statement stmt = conn.createStatement();
+        ResultSet rs = stmt.executeQuery(String.format("SELECT %s FROM 
SYSTEM.CATALOG", invocation));
+        assertTrue(rs.next());
+        Date returnValue = rs.getDate(1);
+        rs.close();
+        stmt.close();
+        return returnValue;
+    }
+
+    private Date callToDateFunction(String invocation) throws SQLException {
+        return callToDateFunction(conn, invocation);
+    }
+
+    @Test
+    public void testToDate_Default() throws SQLException {
+        // Default time zone is GMT, so this is timestamp 0
+        assertEquals(0L, callToDateFunction("TO_DATE('1970-01-01 
00:00:00')").getTime());
+    }
+
+    @Test
+    public void testToDate_CustomDateFormat() throws SQLException {
+        // A date without time component is at midnight
+        assertEquals(0L, callToDateFunction("TO_DATE('1970-01-01', 
'yyyy-MM-dd')").getTime());
+    }
+
+    @Test
+    public void testToDate_CustomTimeZone() throws SQLException {
+        // We're using GMT+1, so that's an hour before the Java epoch
+        assertEquals(
+                -ONE_HOUR_IN_MILLIS,
+                callToDateFunction("TO_DATE('1970-01-01', 'yyyy-MM-dd', 
'GMT+1')").getTime());
+    }
+
+    @Test
+    public void testToDate_LocalTimeZone() throws SQLException {
+        assertEquals(
+                Date.valueOf("1970-01-01"),
+                callToDateFunction("TO_DATE('1970-01-01', 'yyyy-MM-dd', 
'local')"));
+    }
+
+    @Test
+    public void testToDate_CustomTimeZoneViaQueryServices() throws 
SQLException {
+        Properties props = new Properties();
+        props.setProperty(QueryServices.DATE_FORMAT_TIMEZONE_ATTRIB, "GMT+1");
+        Connection customTimeZoneConn = DriverManager.getConnection(getUrl(), 
props);
+
+        assertEquals(
+                -ONE_HOUR_IN_MILLIS,
+                callToDateFunction(customTimeZoneConn, "TO_DATE('1970-01-01 
00:00:00')").getTime());
+    }
+
+    @Test
+    public void testToDate_CustomTimeZoneViaQueryServicesAndCustomFormat() 
throws SQLException {
+        Properties props = new Properties();
+        props.setProperty(QueryServices.DATE_FORMAT_TIMEZONE_ATTRIB, "GMT+1");
+        Connection customTimeZoneConn = DriverManager.getConnection(getUrl(), 
props);
+
+        assertEquals(
+                -ONE_HOUR_IN_MILLIS,
+                callToDateFunction(
+                        customTimeZoneConn, "TO_DATE('1970-01-01', 
'yyyy-MM-dd')").getTime());
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ea10e1f7/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
----------------------------------------------------------------------
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 5a36907..f48f613 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
@@ -24,6 +24,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TimeZone;
 
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
@@ -49,7 +50,7 @@ import com.google.common.collect.Maps;
  * Class that keeps common state used across processing the various clauses in 
a
  * top level JDBC statement such as SELECT, UPSERT, DELETE, etc.
  *
- * 
+ *
  * @since 0.1
  */
 public class StatementContext {
@@ -59,27 +60,27 @@ public class StatementContext {
     private final ExpressionManager expressions;
     private final AggregationManager aggregates;
     private final String dateFormat;
+    private final TimeZone dateFormatTimeZone;
     private final Format dateFormatter;
-    private final Format dateParser;
     private final String numberFormat;
     private final ImmutableBytesWritable tempPtr;
     private final PhoenixStatement statement;
     private final Map<PColumn, Integer> dataColumns;
-    
+
     private long currentTime = QueryConstants.UNSET_TIMESTAMP;
     private ScanRanges scanRanges = ScanRanges.EVERYTHING;
-    private final SequenceManager sequences; 
+    private final SequenceManager sequences;
 
     private TableRef currentTable;
     private List<Pair<byte[], byte[]>> whereConditionColumns;
     private TimeRange scanTimeRange = null;
-    
+
     private Map<SelectStatement, Object> subqueryResults;
-    
+
     public StatementContext(PhoenixStatement statement) {
         this(statement, new Scan());
     }
-    
+
     public StatementContext(PhoenixStatement statement, Scan scan) {
         this(statement, FromCompiler.EMPTY_TABLE_RESOLVER, new Scan(), new 
SequenceManager(statement));
     }
@@ -98,8 +99,9 @@ public class StatementContext {
         this.expressions = new ExpressionManager();
         PhoenixConnection connection = statement.getConnection();
         this.dateFormat = 
connection.getQueryServices().getProps().get(QueryServices.DATE_FORMAT_ATTRIB, 
DateUtil.DEFAULT_DATE_FORMAT);
+        this.dateFormatTimeZone = TimeZone.getTimeZone(
+                
connection.getQueryServices().getProps().get(QueryServices.DATE_FORMAT_TIMEZONE_ATTRIB,
 DateUtil.DEFAULT_TIME_ZONE_ID));
         this.dateFormatter = DateUtil.getDateFormatter(dateFormat);
-        this.dateParser = DateUtil.getDateParser(dateFormat);
         this.numberFormat = 
connection.getQueryServices().getProps().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;
@@ -131,7 +133,7 @@ public class StatementContext {
     }
 
     /**
-     * @return map of data columns and their positions. 
+     * @return map of data columns and their positions.
      */
     public Map<PColumn, Integer> getDataColumnsMap() {
         return dataColumns;
@@ -141,18 +143,18 @@ public class StatementContext {
         return dateFormat;
     }
 
+    public TimeZone getDateFormatTimeZone() {
+        return dateFormatTimeZone;
+    }
+
     public Format getDateFormatter() {
         return dateFormatter;
     }
 
-    public Format getDateParser() {
-        return dateParser;
-    }
-    
     public String getNumberFormat() {
         return numberFormat;
     }
-    
+
     public Scan getScan() {
         return scan;
     }
@@ -160,11 +162,11 @@ public class StatementContext {
     public BindManager getBindManager() {
         return binds;
     }
-    
+
     public TableRef getCurrentTable() {
         return currentTable;
     }
-    
+
     public void setCurrentTable(TableRef table) {
         this.currentTable = table;
     }
@@ -193,12 +195,12 @@ public class StatementContext {
     public ScanRanges getScanRanges() {
         return this.scanRanges;
     }
-    
+
     public void setScanRanges(ScanRanges scanRanges) {
         this.scanRanges = scanRanges;
         scanRanges.initializeScan(scan);
     }
-    
+
     public PhoenixConnection getConnection() {
         return statement.getConnection();
     }
@@ -243,11 +245,11 @@ public class StatementContext {
     public void setScanTimeRange(TimeRange value){
        this.scanTimeRange = value;
     }
-    
+
     public TimeRange getScanTimeRange() {
        return this.scanTimeRange;
     }
-    
+
     public boolean isSubqueryResultAvailable(SelectStatement select) {
         return subqueryResults.containsKey(select);
     }
@@ -255,7 +257,7 @@ public class StatementContext {
     public Object getSubqueryResult(SelectStatement select) {
         return subqueryResults.get(select);
     }
-    
+
     public void setSubqueryResult(SelectStatement select, Object result) {
         subqueryResults.put(select, result);
     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ea10e1f7/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToDateFunction.java
----------------------------------------------------------------------
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 e14cb1d..28d1206 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
@@ -36,15 +36,19 @@ import org.apache.phoenix.util.DateUtil;
 
 
 /**
- * 
- * Implementation of the TO_DATE(<string>,[<format-string>]) built-in function.
+ *
+ * Implementation of the {@code 
TO_DATE(<string>,[<format-string>,[<timezone-string>]])} built-in function.
  * The second argument is optional and defaults to the 
phoenix.query.dateFormat value
- * from the HBase config. If present it must be a constant string.
+ * from the HBase config. If present it must be a constant string. The third 
argument is either a
+ * valid (constant) timezone id, or the string "local". The third argument is 
also optional, and
+ * it defaults to GMT.
  *
- * 
  * @since 0.1
  */
-@BuiltInFunction(name=ToDateFunction.NAME, nodeClass=ToDateParseNode.class, 
args= 
{@Argument(allowedTypes={PDataType.VARCHAR}),@Argument(allowedTypes={PDataType.VARCHAR},isConstant=true,defaultValue="null")}
 )
+@BuiltInFunction(name=ToDateFunction.NAME, nodeClass=ToDateParseNode.class,
+        args={@Argument(allowedTypes={PDataType.VARCHAR}),
+                
@Argument(allowedTypes={PDataType.VARCHAR},isConstant=true,defaultValue="null"),
+                @Argument(allowedTypes={PDataType.VARCHAR}, isConstant=true, 
defaultValue = "null") } )
 public class ToDateFunction extends ScalarFunction {
     public static final String NAME = "TO_DATE";
     private Format dateParser;
@@ -58,7 +62,7 @@ public class ToDateFunction extends ScalarFunction {
         this.dateFormat = dateFormat;
         this.dateParser = dateParser;
     }
-    
+
     @Override
     public int hashCode() {
         final int prime = 31;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ea10e1f7/phoenix-core/src/main/java/org/apache/phoenix/parse/ToDateParseNode.java
----------------------------------------------------------------------
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 41f8b43..46bca63 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
@@ -20,6 +20,7 @@ package org.apache.phoenix.parse;
 import java.sql.SQLException;
 import java.text.Format;
 import java.util.List;
+import java.util.TimeZone;
 
 import org.apache.phoenix.compile.StatementContext;
 import org.apache.phoenix.expression.Expression;
@@ -30,6 +31,7 @@ import org.apache.phoenix.util.DateUtil;
 
 
 public class ToDateParseNode extends FunctionParseNode {
+
     public ToDateParseNode(String name, List<ParseNode> children, 
BuiltInFunctionInfo info) {
         super(name, children, info);
     }
@@ -37,13 +39,20 @@ public class ToDateParseNode extends FunctionParseNode {
     @Override
     public FunctionExpression create(List<Expression> children, 
StatementContext context) throws SQLException {
         Format dateParser;
-        String dateFormat = 
(String)((LiteralExpression)children.get(1)).getValue();
+        String dateFormat = (String) ((LiteralExpression) 
children.get(1)).getValue();
+        String timeZoneId = (String) ((LiteralExpression) 
children.get(2)).getValue();
+        TimeZone parserTimeZone = context.getDateFormatTimeZone();
         if (dateFormat == null) {
             dateFormat = context.getDateFormat();
-            dateParser = context.getDateParser();
+        }
+        if (timeZoneId == null) {
+            parserTimeZone = context.getDateFormatTimeZone();
+        } else if ("LOCAL".equalsIgnoreCase(timeZoneId)) {
+            parserTimeZone = TimeZone.getDefault();
         } else {
-            dateParser = DateUtil.getDateParser(dateFormat);
+            parserTimeZone = TimeZone.getTimeZone(timeZoneId);
         }
+        dateParser = DateUtil.getDateParser(dateFormat, parserTimeZone);
         return new ToDateFunction(children, dateFormat, dateParser);
     }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ea10e1f7/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
----------------------------------------------------------------------
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 eb72a83..225e5bb 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
@@ -68,6 +68,7 @@ public interface QueryServices extends SQLCloseable {
     public static final String MAX_MEMORY_WAIT_MS_ATTRIB = 
"phoenix.query.maxGlobalMemoryWaitMs";
     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 DATE_FORMAT_TIMEZONE_ATTRIB = 
"phoenix.query.dateFormatTimeZone";
     public static final String DATE_FORMAT_ATTRIB = "phoenix.query.dateFormat";
     public static final String NUMBER_FORMAT_ATTRIB = 
"phoenix.query.numberFormat";
     public static final String CALL_QUEUE_ROUND_ROBIN_ATTRIB = 
"ipc.server.callqueue.roundrobin";

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ea10e1f7/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
----------------------------------------------------------------------
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 8088e2d..70824a5 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
@@ -21,6 +21,7 @@ import static 
org.apache.phoenix.query.QueryServices.ALLOW_ONLINE_TABLE_SCHEMA_U
 import static 
org.apache.phoenix.query.QueryServices.CALL_QUEUE_PRODUCER_ATTRIB_NAME;
 import static 
org.apache.phoenix.query.QueryServices.CALL_QUEUE_ROUND_ROBIN_ATTRIB;
 import static org.apache.phoenix.query.QueryServices.DATE_FORMAT_ATTRIB;
+import static 
org.apache.phoenix.query.QueryServices.DATE_FORMAT_TIMEZONE_ATTRIB;
 import static 
org.apache.phoenix.query.QueryServices.DELAY_FOR_SCHEMA_UPDATE_CHECK;
 import static org.apache.phoenix.query.QueryServices.DROP_METADATA_ATTRIB;
 import static 
org.apache.phoenix.query.QueryServices.EXPLAIN_CHUNK_COUNT_ATTRIB;
@@ -92,6 +93,7 @@ public class QueryServicesOptions {
     public static final int DEFAULT_TARGET_QUERY_CONCURRENCY = 32;
     public static final int DEFAULT_MAX_QUERY_CONCURRENCY = 64;
     public static final String DEFAULT_DATE_FORMAT = 
DateUtil.DEFAULT_DATE_FORMAT;
+    public static final String DEFAULT_DATE_FORMAT_TIMEZONE = 
DateUtil.DEFAULT_TIME_ZONE_ID;
     public static final boolean DEFAULT_CALL_QUEUE_ROUND_ROBIN = true; 
     public static final int DEFAULT_MAX_MUTATION_SIZE = 500000;
     public static final boolean DEFAULT_ROW_KEY_ORDER_SALTED_TABLE = true; // 
Merge sort on client to ensure salted tables are row key ordered
@@ -207,6 +209,7 @@ public class QueryServicesOptions {
             .setIfUnset(MAX_SERVER_CACHE_SIZE_ATTRIB, 
DEFAULT_MAX_SERVER_CACHE_SIZE)
             .setIfUnset(SCAN_CACHE_SIZE_ATTRIB, DEFAULT_SCAN_CACHE_SIZE)
             .setIfUnset(DATE_FORMAT_ATTRIB, DEFAULT_DATE_FORMAT)
+            .setIfUnset(DATE_FORMAT_TIMEZONE_ATTRIB, 
DEFAULT_DATE_FORMAT_TIMEZONE)
             .setIfUnset(STATS_UPDATE_FREQ_MS_ATTRIB, 
DEFAULT_STATS_UPDATE_FREQ_MS)
             .setIfUnset(CALL_QUEUE_ROUND_ROBIN_ATTRIB, 
DEFAULT_CALL_QUEUE_ROUND_ROBIN)
             .setIfUnset(MAX_MUTATION_SIZE_ATTRIB, DEFAULT_MAX_MUTATION_SIZE)

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ea10e1f7/phoenix-core/src/main/java/org/apache/phoenix/util/DateUtil.java
----------------------------------------------------------------------
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 62d892f..c940067 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
@@ -35,17 +35,20 @@ import org.apache.phoenix.schema.IllegalDataException;
 
 @SuppressWarnings("serial")
 public class DateUtil {
-    public static final TimeZone DATE_TIME_ZONE = TimeZone.getTimeZone("GMT");
+    public static final String DEFAULT_TIME_ZONE_ID = "GMT";
+    private static final TimeZone DEFAULT_TIME_ZONE = 
TimeZone.getTimeZone(DEFAULT_TIME_ZONE_ID);
     public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; // 
This is the format the app sets in NLS settings for every connection.
-    public static final Format DEFAULT_DATE_FORMATTER = 
FastDateFormat.getInstance(DEFAULT_DATE_FORMAT, DATE_TIME_ZONE);
+    public static final Format DEFAULT_DATE_FORMATTER = 
FastDateFormat.getInstance(
+            DEFAULT_DATE_FORMAT, TimeZone.getTimeZone(DEFAULT_TIME_ZONE_ID));
 
     public static final String DEFAULT_MS_DATE_FORMAT = "yyyy-MM-dd 
HH:mm:ss.SSS";
-    public static final Format DEFAULT_MS_DATE_FORMATTER = 
FastDateFormat.getInstance(DEFAULT_MS_DATE_FORMAT, DATE_TIME_ZONE);
+    public static final Format DEFAULT_MS_DATE_FORMATTER = 
FastDateFormat.getInstance(
+            DEFAULT_MS_DATE_FORMAT, 
TimeZone.getTimeZone(DEFAULT_TIME_ZONE_ID));
 
     private DateUtil() {
     }
 
-    public static Format getDateParser(String pattern) {
+    public static Format getDateParser(String pattern, TimeZone timeZone) {
         SimpleDateFormat format = new SimpleDateFormat(pattern) {
             @Override
             public java.util.Date parseObject(String source) throws 
ParseException {
@@ -53,11 +56,15 @@ public class DateUtil {
                 return new java.sql.Date(date.getTime());
             }
         };
-        format.setTimeZone(DateUtil.DATE_TIME_ZONE);
+        format.setTimeZone(timeZone);
         return format;
     }
 
-    public static Format getTimeParser(String pattern) {
+    public static Format getDateParser(String pattern) {
+        return getDateParser(pattern, DEFAULT_TIME_ZONE);
+    }
+
+    public static Format getTimeParser(String pattern, TimeZone timeZone) {
         SimpleDateFormat format = new SimpleDateFormat(pattern) {
             @Override
             public java.util.Date parseObject(String source) throws 
ParseException {
@@ -65,11 +72,15 @@ public class DateUtil {
                 return new java.sql.Time(date.getTime());
             }
         };
-        format.setTimeZone(DateUtil.DATE_TIME_ZONE);
+        format.setTimeZone(timeZone);
         return format;
     }
 
-    public static Format getTimestampParser(String pattern) {
+    public static Format getTimeParser(String pattern) {
+        return getTimeParser(pattern, DEFAULT_TIME_ZONE);
+    }
+
+    public static Format getTimestampParser(String pattern, TimeZone timeZone) 
{
         SimpleDateFormat format = new SimpleDateFormat(pattern) {
             @Override
             public java.util.Date parseObject(String source) throws 
ParseException {
@@ -77,12 +88,18 @@ public class DateUtil {
                 return new java.sql.Timestamp(date.getTime());
             }
         };
-        format.setTimeZone(DateUtil.DATE_TIME_ZONE);
+        format.setTimeZone(timeZone);
         return format;
     }
 
+    public static Format getTimestampParser(String pattern) {
+        return getTimestampParser(pattern, DEFAULT_TIME_ZONE);
+    }
+
     public static Format getDateFormatter(String pattern) {
-        return DateUtil.DEFAULT_DATE_FORMAT.equals(pattern) ? 
DateUtil.DEFAULT_DATE_FORMATTER : FastDateFormat.getInstance(pattern, 
DateUtil.DATE_TIME_ZONE);
+        return DateUtil.DEFAULT_DATE_FORMAT.equals(pattern)
+                ? DateUtil.DEFAULT_DATE_FORMATTER
+                : FastDateFormat.getInstance(pattern, 
DateUtil.DEFAULT_TIME_ZONE);
     }
 
     private static ThreadLocal<Format> dateFormat =

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ea10e1f7/phoenix-core/src/test/java/org/apache/phoenix/expression/SortOrderExpressionTest.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/test/java/org/apache/phoenix/expression/SortOrderExpressionTest.java
 
b/phoenix-core/src/test/java/org/apache/phoenix/expression/SortOrderExpressionTest.java
index 2c532aa..d7f4172 100644
--- 
a/phoenix-core/src/test/java/org/apache/phoenix/expression/SortOrderExpressionTest.java
+++ 
b/phoenix-core/src/test/java/org/apache/phoenix/expression/SortOrderExpressionTest.java
@@ -26,6 +26,7 @@ import java.sql.Date;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.List;
+import java.util.TimeZone;
 
 import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
@@ -322,7 +323,7 @@ public class SortOrderExpressionTest {
         cal.set(Calendar.MINUTE, 0);
         cal.set(Calendar.SECOND, 0);
         cal.set(Calendar.MILLISECOND, 0);
-        cal.setTimeZone(DateUtil.DATE_TIME_ZONE);
+        cal.setTimeZone(TimeZone.getTimeZone(DateUtil.DEFAULT_TIME_ZONE_ID));
         Date d = new Date(cal.getTimeInMillis()); 
         return d;
     }    

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ea10e1f7/phoenix-core/src/test/java/org/apache/phoenix/util/DateUtilTest.java
----------------------------------------------------------------------
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 7746515..db65884 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
@@ -17,21 +17,26 @@
  */
 package org.apache.phoenix.util;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
+import java.sql.Date;
+import java.sql.Time;
 import java.sql.Timestamp;
+import java.text.ParseException;
+import java.util.TimeZone;
 
 import org.junit.Test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 /**
- * 
  * Test class for {@link DateUtil}
  *
- * 
  * @since 2.1.3
  */
 public class DateUtilTest {
+
+    private static final long ONE_HOUR_IN_MILLIS = 1000L * 60L * 60L;
     
     @Test
     public void testDemonstrateSetNanosOnTimestampLosingMillis() {
@@ -59,4 +64,67 @@ public class DateUtilTest {
         assertFalse(ts1.equals(ts2));
         assertTrue(ts2.after(ts1));
     }
+
+    @Test
+    public void testGetDateParser_DefaultTimeZone() throws ParseException {
+        Date date = (Date) 
DateUtil.getDateParser("yyyy-MM-dd").parseObject("1970-01-01");
+        assertEquals(0, date.getTime());
+    }
+
+    @Test
+    public void testGetDateParser_CustomTimeZone() throws ParseException {
+        Date date = (Date) DateUtil.getDateParser(
+                "yyyy-MM-dd", 
TimeZone.getTimeZone("GMT+1")).parseObject("1970-01-01");
+        assertEquals(-ONE_HOUR_IN_MILLIS, date.getTime());
+    }
+
+    @Test
+    public void testGetDateParser_LocalTimeZone() throws ParseException {
+        Date date = (Date) DateUtil.getDateParser(
+                "yyyy-MM-dd", TimeZone.getDefault()).parseObject("1970-01-01");
+        assertEquals(Date.valueOf("1970-01-01"), date);
+    }
+
+    @Test
+    public void testGetTimestampParser_DefaultTimeZone() throws ParseException 
{
+        Timestamp ts = (Timestamp) DateUtil.getTimestampParser("yyyy-MM-dd 
HH:mm:ss")
+                .parseObject("1970-01-01 00:00:00");
+        assertEquals(0, ts.getTime());
+    }
+
+    @Test
+    public void testGetTimestampParser_CustomTimeZone() throws ParseException {
+        Timestamp ts = (Timestamp) DateUtil.getTimestampParser("yyyy-MM-dd 
HH:mm:ss", TimeZone.getTimeZone("GMT+1"))
+                .parseObject("1970-01-01 00:00:00");
+        assertEquals(-ONE_HOUR_IN_MILLIS, ts.getTime());
+    }
+
+    @Test
+    public void testGetTimestampParser_LocalTimeZone() throws ParseException {
+        Timestamp ts = (Timestamp) DateUtil.getTimestampParser(
+                "yyyy-MM-dd HH:mm:ss",
+                TimeZone.getDefault()).parseObject("1970-01-01 00:00:00");
+        assertEquals(Timestamp.valueOf("1970-01-01 00:00:00"), ts);
+    }
+
+    @Test
+    public void testGetTimeParser_DefaultTimeZone() throws ParseException {
+        Time time = (Time) 
DateUtil.getTimeParser("HH:mm:ss").parseObject("00:00:00");
+        assertEquals(0, time.getTime());
+    }
+
+    @Test
+    public void testGetTimeParser_CustomTimeZone() throws ParseException {
+        Time time = (Time) DateUtil.getTimeParser(
+                "HH:mm:ss",
+                TimeZone.getTimeZone("GMT+1")).parseObject("00:00:00");
+        assertEquals(-ONE_HOUR_IN_MILLIS, time.getTime());
+    }
+
+    @Test
+    public void testGetTimeParser_LocalTimeZone() throws ParseException {
+        Time time = (Time) DateUtil.getTimeParser(
+                "HH:mm:ss", TimeZone.getDefault()).parseObject("00:00:00");
+        assertEquals(Time.valueOf("00:00:00"), time);
+    }
 }

Reply via email to