This is an automated email from the ASF dual-hosted git repository. richardantal pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/master by this push: new 67cef07 PHOENIX-6486 Phoenix uses inconsistent chronologies internally, breaking pre-Gregorian date handling 67cef07 is described below commit 67cef07011426bbf78fa271530fbebd6491935ff Author: Richard Antal <antal97rich...@gmail.com> AuthorDate: Wed Aug 25 11:48:36 2021 +0200 PHOENIX-6486 Phoenix uses inconsistent chronologies internally, breaking pre-Gregorian date handling --- .../org/apache/phoenix/end2end/DateTimeIT.java | 212 ++++++++++++++++++++- .../expression/function/DayOfMonthFunction.java | 3 +- .../expression/function/DayOfWeekFunction.java | 3 +- .../expression/function/DayOfYearFunction.java | 3 +- .../phoenix/expression/function/HourFunction.java | 5 +- .../expression/function/MinuteFunction.java | 5 +- .../phoenix/expression/function/MonthFunction.java | 3 +- .../function/RoundJodaDateExpression.java | 2 +- .../expression/function/SecondFunction.java | 5 +- .../phoenix/expression/function/WeekFunction.java | 3 +- .../phoenix/expression/function/YearFunction.java | 3 +- .../java/org/apache/phoenix/util/DateUtil.java | 31 +-- 12 files changed, 253 insertions(+), 25 deletions(-) diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java index 98c0e30..a0d017f 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java @@ -35,6 +35,7 @@ import static org.apache.phoenix.util.TestUtil.ROW9; import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -52,10 +53,13 @@ import java.sql.Timestamp; import java.sql.Types; import java.text.Format; import java.time.LocalDate; +import java.time.ZoneOffset; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Properties; import java.util.TimeZone; +import java.util.List; +import java.util.Arrays; import org.apache.commons.lang3.time.FastDateFormat; import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; @@ -560,7 +564,7 @@ public class DateTimeIT extends ParallelStatsDisabledIT { @Test public void testYearFunctionDate() throws SQLException { - assertEquals(2008, callYearFunction("\"YEAR\"(TO_DATE('2008-01-01', 'yyyy-MM-dd', 'local'))")); + assertEquals(2008, callYearFunction("\"YEAR\"(TO_DATE('2008-01-01', 'yyyy-MM-dd'))")); assertEquals(2004, callYearFunction("\"YEAR\"(TO_DATE('2004-12-13 10:13:18', 'yyyy-MM-dd hh:mm:ss'))")); @@ -1939,6 +1943,212 @@ public class DateTimeIT extends ParallelStatsDisabledIT { } } + private String getFormattedDate(List<String> dateList) { + return String.join("-", dateList.subList(0, 3)) + " " + + String.join(":", dateList.subList(3, 6)) + "." + dateList.get(6); + } + + @Test + public void testAncientDates() throws Exception { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(url, props); + Statement stmt = conn.createStatement(); + String tableName = generateUniqueName(); + + List<String> date1list = Arrays.asList("0010", "10", "10", "10", "10", "10", "111"); + List<String> date2list = Arrays.asList("1001", "02", "03", "04", "05", "06", "000"); + List<String> date3list = Arrays.asList("0001", "12", "31", "23", "59", "59", "000"); + List<List<String>> dateLists = Arrays.asList(date1list, date2list, date3list, date2list); + + String date1 = getFormattedDate(date1list); // 0010-10-10 10:10:10.111 + String date2 = getFormattedDate(date2list); // 1000-02-03 04:05:06.000 + String date3 = getFormattedDate(date3list); // 0001-12-31 23:59:59.000 + List<String> dates = Arrays.asList(date1, date2, date3, date2); + + + stmt.execute("CREATE TABLE " + tableName + " ( id INTEGER not null PRIMARY KEY," + + " date DATE, time TIME, timestamp TIMESTAMP)"); + + stmt.execute("UPSERT INTO " + tableName + " VALUES(1, TO_DATE('" + date1 + + "'), TO_TIME('" + date1 + "'), TO_TIMESTAMP('" + date1 + "'))"); + + PreparedStatement pstmt = conn.prepareStatement("UPSERT INTO " + tableName + " values (?, ?, ?, ?)"); + pstmt.setInt(1, 2); + Timestamp t = new Timestamp(DateUtil.parseDate(date2).getTime()); + pstmt.setDate(2, new Date(t.getTime())); + pstmt.setTime(3, new Time(t.getTime())); + pstmt.setTimestamp(4, t); + pstmt.execute(); + + pstmt.setInt(1, 3); + t = new Timestamp(DateUtil.parseDate(date3).getTime()); + pstmt.setDate(2, new Date(t.getTime())); + pstmt.setTime(3, new Time(t.getTime())); + pstmt.setTimestamp(4, t); + pstmt.execute(); + + String f = " GMT', 'yyyy-MM-dd HH:mm:ss.SSS z', 'UTC"; + stmt.execute("UPSERT INTO " + tableName + " VALUES(4, TO_DATE('" + date2 + f + + "'), TO_TIME('" + date2 + f + "'), TO_TIMESTAMP('" + date2 + f + "'))"); + conn.commit(); + + ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName + " ORDER BY id"); + + for (int i = 0; i < dates.size(); i++) { + assertTrue(rs.next()); + String actualDate = dates.get(i); + Timestamp expectedTimestamp = DateUtil.parseTimestamp(actualDate); + + assertEquals(i + 1, rs.getInt(1)); + Date expectedDate = new Date(expectedTimestamp.getTime()); + assertEquals(expectedDate, rs.getDate(2)); + assertEquals(rs.getDate(2), rs.getObject(2)); + assertEquals(actualDate, rs.getString(2)); + + Time expectedTime = new Time(expectedTimestamp.getTime()); + assertEquals(new Timestamp(expectedTime.getTime()), new Timestamp(rs.getTime(3).getTime())); + assertEquals(expectedTime, rs.getTime(3)); + assertEquals(rs.getTime(3), rs.getObject(3)); + assertEquals(actualDate, rs.getString(3)); + + assertEquals(expectedTimestamp, rs.getTimestamp(4)); + assertEquals(rs.getTimestamp(4), rs.getObject(4)); + assertEquals(actualDate, rs.getString(4)); + } + + String query = "SELECT year(timestamp), month(timestamp), dayofmonth(timestamp)," + + " hour(timestamp), minute(timestamp), second(timestamp) FROM " + tableName + + " ORDER BY id"; + rs = stmt.executeQuery(query); + + for (int i = 0; i < dates.size(); i++) { + assertTrue(rs.next()); + List<String> dateList = dateLists.get(i); + for (int j = 0; j < 6; j++) { + int expected = Integer.parseInt(dateList.get(j)); + int value = rs.getInt(j + 1); + String readFunc = query.split("\\s+")[j + 1]; + assertTrue("Expected for " + readFunc.substring(0, readFunc.length() - 1) + ": " + expected + ", got: " + value, + expected == value); + } + } + + pstmt = conn.prepareStatement("UPSERT INTO " + tableName + " values (?, ?, ?, ?)"); + pstmt.setInt(1, 5); + long l = -123456789100000L; + Timestamp inserted = new Timestamp(l); + pstmt.setDate(2, new Date(inserted.getTime())); + pstmt.setTime(3, new Time(inserted.getTime())); + pstmt.setTimestamp(4, inserted); + pstmt.execute(); + conn.commit(); + + rs = stmt.executeQuery("SELECT * FROM " + tableName + " WHERE id=5 ORDER BY id"); + assertTrue(rs.next()); + Timestamp read = rs.getTimestamp(4); + assertEquals(inserted, read); + } + + @Test + public void testAncientDatesWithJavaTime() throws Exception { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(url, props); + Statement stmt = conn.createStatement(); + String tableName = generateUniqueName(); + + stmt.execute("CREATE TABLE " + tableName + " ( id INTEGER not null PRIMARY KEY," + + "timestamp TIMESTAMP)"); + + String date = "0010-10-10T10:10:10.111Z"; + + java.time.Instant instant = java.time.Instant.parse(date); + // 0010-10-10T10:10:10.111Z parsed in ISO chronology + + PreparedStatement pstmt = conn.prepareStatement("UPSERT INTO " + tableName + " values (?, ?)"); + pstmt.setInt(1, 1); + Timestamp inserted = new Timestamp(instant.toEpochMilli()); + // java.sql.timestamp value will be 0010-10-12 because of GJ chronology used by java.sql.timestamp + + pstmt.setTimestamp(2, inserted); + pstmt.execute(); + conn.commit(); + + ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName + " WHERE id=1"); + assertTrue(rs.next()); + Timestamp read = rs.getTimestamp(2); // read back the 0010-10-12 in GJ + assertEquals(inserted, read); + // java.time.Instant value will be 0010-10-10 in ISO + assertEquals(instant, java.time.Instant.ofEpochMilli(read.getTime())); + + assertEquals(instant.toEpochMilli(), read.getTime()); + // same long value interpreted in different chronology results different date. + // Note that the string output is inconsistent but it is expected because of the different chronologies + assertEquals(read.toString().split("\\s+")[0], "0010-10-12"); + assertEquals(instant.toString().split("T")[0], "0010-10-10"); + + // test for getdayofMonth will be broken see below because we stored the 0010-10-12 data + + java.time.LocalDateTime localDateTime = java.time.LocalDateTime.of(10, 10, 10, 10, 10, 10); + pstmt.setInt(1, 2); + inserted = java.sql.Timestamp.valueOf(localDateTime); + + pstmt.setTimestamp(2, inserted); + pstmt.execute(); + conn.commit(); + + rs = stmt.executeQuery("SELECT * FROM " + tableName + " WHERE id=2"); + assertTrue(rs.next()); + read = rs.getTimestamp(2); + assertEquals(inserted, read); + assertEquals(localDateTime, read.toLocalDateTime()); + assertEquals(localDateTime.toEpochSecond(ZoneOffset.UTC), + read.toLocalDateTime().toEpochSecond(ZoneOffset.UTC)); + + // Inserting via java.sql.Timestamp.valueOf(localDateTime) will result the same toString result + + assertEquals(read.toString().split("\\s+")[0], "0010-10-10"); + assertEquals(instant.toString().split("T")[0], "0010-10-10"); + + // the long value that represents the the Date will be different thought + // can be expected because it resulted the same output string using different chronology + assertNotEquals(instant.toEpochMilli(), read.getTime()); + + // Converting Instant to LocalDateTime will behave like the LocalDateTime example + pstmt.setInt(1, 3); + java.time.LocalDateTime localDateTime1 = java.time.LocalDateTime.ofInstant(instant, java.time.ZoneId.of("GMT")); + inserted = java.sql.Timestamp.valueOf(localDateTime1); + + pstmt.setTimestamp(2, inserted); + pstmt.execute(); + conn.commit(); + + rs = stmt.executeQuery("SELECT * FROM " + tableName + " WHERE id=3"); + assertTrue(rs.next()); + read = rs.getTimestamp(2); + assertEquals(inserted, read); + assertEquals(localDateTime1, read.toLocalDateTime()); + assertEquals(read.toString().split("\\s+")[0], "0010-10-10"); + assertEquals(localDateTime1.toString().split("T")[0], "0010-10-10"); + + java.time.Instant inst = localDateTime1.toInstant(ZoneOffset.UTC); + assertEquals(instant, inst); + assertNotEquals(inst.toEpochMilli(), inserted.getTime()); + + + String query = "SELECT dayofmonth(timestamp) FROM " + tableName; + rs = stmt.executeQuery(query); + assertTrue(rs.next()); + // Timestamp inserted = new Timestamp(instant.toEpochMilli()); + assertEquals(12, rs.getInt(1)); + assertTrue(rs.next()); + // inserted = java.sql.Timestamp.valueOf(localDateTime); + assertEquals(10, rs.getInt(1)); + assertTrue(rs.next()); + // inserted = java.sql.Timestamp.valueOf(java.time.LocalDateTime.ofInstant(instant, java.time.ZoneId.of("GMT"))); + assertEquals(10, rs.getInt(1)); + assertFalse(rs.next()); + } + public void testDateFormatTimeZone(String timeZoneId) throws Exception { Properties props = new Properties(); props.setProperty("phoenix.query.dateFormatTimeZone", timeZoneId); diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/DayOfMonthFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/DayOfMonthFunction.java index fb5a68a..721f8e6 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/DayOfMonthFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/DayOfMonthFunction.java @@ -29,6 +29,7 @@ import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.schema.types.PInteger; import org.apache.phoenix.schema.types.PTimestamp; import org.joda.time.DateTime; +import org.joda.time.chrono.GJChronology; /** * @@ -58,7 +59,7 @@ public class DayOfMonthFunction extends DateScalarFunction { return true; //means null } long dateTime = inputCodec.decodeLong(ptr, expression.getSortOrder()); - DateTime dt = new DateTime(dateTime); + DateTime dt = new DateTime(dateTime, GJChronology.getInstanceUTC()); int day = dt.getDayOfMonth(); PDataType returnType = getDataType(); byte[] byteValue = new byte[returnType.getByteSize()]; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/DayOfWeekFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/DayOfWeekFunction.java index e4d3d5f..1dbcc10 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/DayOfWeekFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/DayOfWeekFunction.java @@ -28,6 +28,7 @@ import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.schema.types.PInteger; import org.apache.phoenix.schema.types.PTimestamp; import org.joda.time.DateTime; +import org.joda.time.chrono.GJChronology; /** * Implementation of DayOfWeekFunction(Date/Timestamp) @@ -69,7 +70,7 @@ public class DayOfWeekFunction extends DateScalarFunction { return true; } long dateTime = inputCodec.decodeLong(ptr, arg.getSortOrder()); - DateTime jodaDT = new DateTime(dateTime); + DateTime jodaDT = new DateTime(dateTime, GJChronology.getInstanceUTC()); int day = jodaDT.getDayOfWeek(); PDataType returnDataType = getDataType(); byte[] byteValue = new byte[returnDataType.getByteSize()]; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/DayOfYearFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/DayOfYearFunction.java index 8da51d7..2cde41e 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/DayOfYearFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/DayOfYearFunction.java @@ -28,6 +28,7 @@ import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.schema.types.PInteger; import org.apache.phoenix.schema.types.PTimestamp; import org.joda.time.DateTime; +import org.joda.time.chrono.GJChronology; /** * Implementation of DayOfYearFunction(Date/Timestamp) @@ -62,7 +63,7 @@ public class DayOfYearFunction extends DateScalarFunction { return true; } long dateTime = inputCodec.decodeLong(ptr, arg.getSortOrder()); - DateTime jodaDT = new DateTime(dateTime); + DateTime jodaDT = new DateTime(dateTime, GJChronology.getInstanceUTC()); int day = jodaDT.getDayOfYear(); PDataType returnDataType = getDataType(); byte[] byteValue = new byte[returnDataType.getByteSize()]; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/HourFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/HourFunction.java index 7b9c8c0..f9ceb68 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/HourFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/HourFunction.java @@ -28,6 +28,8 @@ import org.apache.phoenix.schema.tuple.Tuple; import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.schema.types.PInteger; import org.apache.phoenix.schema.types.PTimestamp; +import org.joda.time.DateTime; +import org.joda.time.chrono.GJChronology; /** * @@ -57,7 +59,8 @@ public class HourFunction extends DateScalarFunction { return true; //means null } long dateTime = inputCodec.decodeLong(ptr, expression.getSortOrder()); - int hour = (int)(((dateTime/1000) % (24*3600))/3600); + DateTime dt = new DateTime(dateTime, GJChronology.getInstanceUTC()); + int hour = dt.getHourOfDay(); PDataType returnType = getDataType(); byte[] byteValue = new byte[returnType.getByteSize()]; returnType.getCodec().encodeInt(hour, byteValue, 0); diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/MinuteFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/MinuteFunction.java index 6766e35..1ba6729 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/MinuteFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/MinuteFunction.java @@ -28,6 +28,8 @@ import org.apache.phoenix.schema.tuple.Tuple; import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.schema.types.PInteger; import org.apache.phoenix.schema.types.PTimestamp; +import org.joda.time.DateTime; +import org.joda.time.chrono.GJChronology; /** * @@ -57,7 +59,8 @@ public class MinuteFunction extends DateScalarFunction { return true; //means null } long dateTime = inputCodec.decodeLong(ptr, expression.getSortOrder()); - int minute = (int)(((dateTime/1000) % 3600)/60); + DateTime dt = new DateTime(dateTime, GJChronology.getInstanceUTC()); + int minute = dt.getMinuteOfHour(); PDataType returnType = getDataType(); byte[] byteValue = new byte[returnType.getByteSize()]; returnType.getCodec().encodeInt(minute, byteValue, 0); diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/MonthFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/MonthFunction.java index bd6ff3e..cb6ce3b 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/MonthFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/MonthFunction.java @@ -29,6 +29,7 @@ import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.schema.types.PInteger; import org.apache.phoenix.schema.types.PTimestamp; import org.joda.time.DateTime; +import org.joda.time.chrono.GJChronology; /** * @@ -58,7 +59,7 @@ public class MonthFunction extends DateScalarFunction { return true; //means null } long dateTime = inputCodec.decodeLong(ptr, expression.getSortOrder()); - DateTime dt = new DateTime(dateTime); + DateTime dt = new DateTime(dateTime, GJChronology.getInstanceUTC()); int month = dt.getMonthOfYear(); PDataType returnType = getDataType(); byte[] byteValue = new byte[returnType.getByteSize()]; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundJodaDateExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundJodaDateExpression.java index 356c85f..aa9ef07 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundJodaDateExpression.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundJodaDateExpression.java @@ -48,7 +48,7 @@ public abstract class RoundJodaDateExpression extends RoundDateExpression{ } PDataType dataType = getDataType(); long time = dataType.getCodec().decodeLong(ptr, children.get(0).getSortOrder()); - DateTime dt = new DateTime(time,ISOChronology.getInstanceUTC()); + DateTime dt = new DateTime(time, ISOChronology.getInstanceUTC()); long value = roundDateTime(dt); Date d = new Date(value); byte[] byteValue = dataType.toBytes(d); diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SecondFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SecondFunction.java index 02bc455..09e3058 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SecondFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SecondFunction.java @@ -28,6 +28,8 @@ import org.apache.phoenix.schema.tuple.Tuple; import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.schema.types.PInteger; import org.apache.phoenix.schema.types.PTimestamp; +import org.joda.time.DateTime; +import org.joda.time.chrono.GJChronology; /** * @@ -57,7 +59,8 @@ public class SecondFunction extends DateScalarFunction { return true; //means null } long dateTime = inputCodec.decodeLong(ptr, expression.getSortOrder()); - int sec = (int)((dateTime/1000) % 60); + DateTime dt = new DateTime(dateTime, GJChronology.getInstanceUTC()); + int sec = dt.getSecondOfMinute(); PDataType returnType = getDataType(); byte[] byteValue = new byte[returnType.getByteSize()]; returnType.getCodec().encodeInt(sec, byteValue, 0); diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/WeekFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/WeekFunction.java index 3f6d772..f3032bf 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/WeekFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/WeekFunction.java @@ -29,6 +29,7 @@ import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.schema.types.PInteger; import org.apache.phoenix.schema.types.PTimestamp; import org.joda.time.DateTime; +import org.joda.time.chrono.GJChronology; /** * @@ -58,7 +59,7 @@ public class WeekFunction extends DateScalarFunction { return true; //means null } long dateTime = inputCodec.decodeLong(ptr, expression.getSortOrder()); - DateTime dt = new DateTime(dateTime); + DateTime dt = new DateTime(dateTime, GJChronology.getInstanceUTC()); int week = dt.getWeekOfWeekyear(); PDataType returnType = getDataType(); byte[] byteValue = new byte[returnType.getByteSize()]; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/YearFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/YearFunction.java index fcab8d2..ee10ead 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/YearFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/YearFunction.java @@ -29,6 +29,7 @@ import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.schema.types.PInteger; import org.apache.phoenix.schema.types.PTimestamp; import org.joda.time.DateTime; +import org.joda.time.chrono.GJChronology; /** * @@ -57,7 +58,7 @@ public class YearFunction extends DateScalarFunction { return true; //means null } long dateTime = inputCodec.decodeLong(ptr, expression.getSortOrder()); - DateTime dt = new DateTime(dateTime); + DateTime dt = new DateTime(dateTime, GJChronology.getInstanceUTC()); int year = dt.getYear(); PDataType returnType = getDataType(); byte[] byteValue = new byte[returnType.getByteSize()]; 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 9eee9dd..cc16eb6 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 @@ -40,8 +40,10 @@ import org.apache.phoenix.schema.types.PDate; import org.apache.phoenix.schema.types.PTimestamp; import org.apache.phoenix.schema.types.PUnsignedDate; import org.apache.phoenix.schema.types.PUnsignedTimestamp; +import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.chrono.ISOChronology; +import org.joda.time.chrono.GJChronology; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatterBuilder; import org.joda.time.format.ISODateTimeFormat; @@ -70,13 +72,13 @@ 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; - private static final DateTimeFormatter ISO_DATE_TIME_FORMATTER = new DateTimeFormatterBuilder() + private static final DateTimeFormatter JULIAN_DATE_TIME_FORMATTER = new DateTimeFormatterBuilder() .append(ISODateTimeFormat.dateParser()) .appendOptional(new DateTimeFormatterBuilder() .appendLiteral(' ').toParser()) .appendOptional(new DateTimeFormatterBuilder() .append(ISODateTimeFormat.timeParser()).toParser()) - .toFormatter().withChronology(ISOChronology.getInstanceUTC()); + .toFormatter().withChronology(GJChronology.getInstanceUTC()); private DateUtil() { } @@ -154,7 +156,7 @@ public class DateUtil { pattern = defaultPattern; } if(defaultPattern.equals(pattern)) { - return ISODateFormatParserFactory.getParser(timeZone); + return JulianDateFormatParserFactory.getParser(timeZone); } else { return new SimpleDateFormatParser(pattern, timeZone); } @@ -187,7 +189,7 @@ public class DateUtil { } private static long parseDateTime(String dateTimeValue) { - return ISODateFormatParser.getInstance().parseDateTime(dateTimeValue); + return JulianDateFormatParser.getInstance().parseDateTime(dateTimeValue); } public static Date parseDate(String dateValue) { @@ -287,17 +289,17 @@ public class DateUtil { } } - private static class ISODateFormatParserFactory { - private ISODateFormatParserFactory() {} - + private static class JulianDateFormatParserFactory { + private JulianDateFormatParserFactory() {} + public static DateTimeParser getParser(final TimeZone timeZone) { // If timeZone matches default, get singleton DateTimeParser if (timeZone.equals(DEFAULT_TIME_ZONE)) { - return ISODateFormatParser.getInstance(); + return JulianDateFormatParser.getInstance(); } // Otherwise, create new DateTimeParser return new DateTimeParser() { - private final DateTimeFormatter formatter = ISO_DATE_TIME_FORMATTER + private final DateTimeFormatter formatter = JULIAN_DATE_TIME_FORMATTER .withZone(DateTimeZone.forTimeZone(timeZone)); @Override @@ -316,19 +318,20 @@ public class DateUtil { }; } } + /** * This class is our default DateTime string parser */ - private static class ISODateFormatParser implements DateTimeParser { - private static final ISODateFormatParser INSTANCE = new ISODateFormatParser(); + private static class JulianDateFormatParser implements DateTimeParser { + private static final JulianDateFormatParser INSTANCE = new JulianDateFormatParser(); - public static ISODateFormatParser getInstance() { + public static JulianDateFormatParser getInstance() { return INSTANCE; } - private final DateTimeFormatter formatter = ISO_DATE_TIME_FORMATTER.withZone(DateTimeZone.UTC); + private final DateTimeFormatter formatter = JULIAN_DATE_TIME_FORMATTER.withZone(DateTimeZone.UTC); - private ISODateFormatParser() {} + private JulianDateFormatParser() {} @Override public long parseDateTime(String dateTimeString) throws IllegalDataException {