diff options
author | jiang-wu <jwu@alumni.stanford.edu> | 2018-05-01 14:48:26 -0700 |
---|---|---|
committer | Aman Sinha <asinha@maprtech.com> | 2018-05-11 15:58:36 -0700 |
commit | c1f0adc9276ab8314ce9c5dff2d9c92066c71530 (patch) | |
tree | 9d8c13bb25ae76af3b0b2cc6c73fa53ce66dd1ae /exec/vector | |
parent | 16659ab30fd9fca5af39edd22aaab36409a50930 (diff) |
DRILL-6242 Use java.time.Local{Date|Time|DateTime} for Drill Date, Time, Timestamp types. (#3)
close apache/drill#1247
* DRILL-6242 - Use java.time.Local{Date|Time|DateTime} classes to hold values from corresponding Drill date, time, and timestamp types.
Conflicts:
exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/fn/ExtendedJsonOutput.java
Fix merge conflicts and check style.
Diffstat (limited to 'exec/vector')
11 files changed, 196 insertions, 35 deletions
diff --git a/exec/vector/src/main/codegen/data/ValueVectorTypes.tdd b/exec/vector/src/main/codegen/data/ValueVectorTypes.tdd index f6e1f1d09..d4851ca65 100644 --- a/exec/vector/src/main/codegen/data/ValueVectorTypes.tdd +++ b/exec/vector/src/main/codegen/data/ValueVectorTypes.tdd @@ -68,7 +68,7 @@ { class: "UInt4", valueHolder: "UInt4Holder" }, { class: "Float4", javaType: "float" , boxedType: "Float", accessorType: "double", accessorCast: "set", fields: [{name: "value", type: "float"}]}, - { class: "Time", javaType: "int", friendlyType: "DateTime", accessorType: "int" }, + { class: "Time", javaType: "int", friendlyType: "LocalTime", accessorType: "int" }, { class: "IntervalYear", javaType: "int", friendlyType: "Period" } { class: "Decimal9", maxPrecisionDigits: 9, friendlyType: "BigDecimal", fields: [{name:"value", type:"int"}, {name: "scale", type: "int", include: false}, @@ -85,8 +85,8 @@ { class: "BigInt"}, { class: "UInt8" }, { class: "Float8", javaType: "double" , boxedType: "Double", fields: [{name: "value", type: "double"}], }, - { class: "Date", javaType: "long", friendlyType: "DateTime", accessorType: "long" }, - { class: "TimeStamp", javaType: "long", friendlyType: "DateTime", accessorType: "long" } + { class: "Date", javaType: "long", friendlyType: "LocalDate", accessorType: "long" }, + { class: "TimeStamp", javaType: "long", friendlyType: "LocalDateTime", accessorType: "long" } { class: "Decimal18", maxPrecisionDigits: 18, friendlyType: "BigDecimal", fields: [{name:"value", type:"long"}, {name: "scale", type: "int", include: false}, {name: "precision", type: "int", include: false}] }, diff --git a/exec/vector/src/main/codegen/includes/vv_imports.ftl b/exec/vector/src/main/codegen/includes/vv_imports.ftl index 058621c94..34fe9c54c 100644 --- a/exec/vector/src/main/codegen/includes/vv_imports.ftl +++ b/exec/vector/src/main/codegen/includes/vv_imports.ftl @@ -65,9 +65,11 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.nio.ByteBuffer; -import java.sql.Date; -import java.sql.Time; -import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.Instant; import java.math.BigDecimal; import java.math.BigInteger; diff --git a/exec/vector/src/main/codegen/templates/AbstractFieldReader.java b/exec/vector/src/main/codegen/templates/AbstractFieldReader.java index 2ed5a3b6d..4e5856b22 100644 --- a/exec/vector/src/main/codegen/templates/AbstractFieldReader.java +++ b/exec/vector/src/main/codegen/templates/AbstractFieldReader.java @@ -43,7 +43,7 @@ abstract class AbstractFieldReader extends AbstractBaseReader implements FieldRe } <#list ["Object", "BigDecimal", "Integer", "Long", "Boolean", - "Character", "DateTime", "Period", "Double", "Float", + "Character", "LocalDate", "LocalTime", "LocalDateTime", "Period", "Double", "Float", "Text", "String", "Byte", "Short", "byte[]"] as friendlyType> <#assign safeType=friendlyType /> <#if safeType=="byte[]"><#assign safeType="ByteArray" /></#if> diff --git a/exec/vector/src/main/codegen/templates/FixedValueVectors.java b/exec/vector/src/main/codegen/templates/FixedValueVectors.java index ddd69253a..68ee33bb0 100644 --- a/exec/vector/src/main/codegen/templates/FixedValueVectors.java +++ b/exec/vector/src/main/codegen/templates/FixedValueVectors.java @@ -27,6 +27,7 @@ package org.apache.drill.exec.vector; <#include "/@includes/vv_imports.ftl" /> + import org.apache.drill.exec.util.DecimalUtility; /** @@ -506,17 +507,13 @@ public final class ${minor.class}Vector extends BaseDataValueVector implements F @Override public ${friendlyType} getObject(int index) { - org.joda.time.DateTime date = new org.joda.time.DateTime(get(index), org.joda.time.DateTimeZone.UTC); - date = date.withZoneRetainFields(org.joda.time.DateTimeZone.getDefault()); - return date; + return LocalDateTime.ofInstant(Instant.ofEpochMilli(get(index)), ZoneOffset.UTC).toLocalDate(); } <#elseif minor.class == "TimeStamp"> @Override public ${friendlyType} getObject(int index) { - org.joda.time.DateTime date = new org.joda.time.DateTime(get(index), org.joda.time.DateTimeZone.UTC); - date = date.withZoneRetainFields(org.joda.time.DateTimeZone.getDefault()); - return date; + return LocalDateTime.ofInstant(Instant.ofEpochMilli(get(index)), ZoneOffset.UTC); } <#elseif minor.class == "IntervalYear"> @@ -531,10 +528,8 @@ public final class ${minor.class}Vector extends BaseDataValueVector implements F <#elseif minor.class == "Time"> @Override - public DateTime getObject(int index) { - org.joda.time.DateTime time = new org.joda.time.DateTime(get(index), org.joda.time.DateTimeZone.UTC); - time = time.withZoneRetainFields(org.joda.time.DateTimeZone.getDefault()); - return time; + public ${friendlyType} getObject(int index) { + return LocalDateTime.ofInstant(Instant.ofEpochMilli(get(index)), ZoneOffset.UTC).toLocalTime(); } <#elseif minor.class == "Decimal9" || minor.class == "Decimal18"> diff --git a/exec/vector/src/main/codegen/templates/HolderReaderImpl.java b/exec/vector/src/main/codegen/templates/HolderReaderImpl.java index 4b7be4fd7..1d17a2839 100644 --- a/exec/vector/src/main/codegen/templates/HolderReaderImpl.java +++ b/exec/vector/src/main/codegen/templates/HolderReaderImpl.java @@ -206,6 +206,12 @@ public class ${holderMode}${name}HolderReaderImpl extends AbstractFieldReader { <#elseif minor.class == "Bit" > return Boolean.valueOf(holder.value != 0); +<#elseif minor.class == "Time"> + return LocalDateTime.ofInstant(Instant.ofEpochMilli(this.holder.value), ZoneOffset.UTC).toLocalTime(); +<#elseif minor.class == "Date"> + return LocalDateTime.ofInstant(Instant.ofEpochMilli(this.holder.value), ZoneOffset.UTC).toLocalDate(); +<#elseif minor.class == "TimeStamp"> + return LocalDateTime.ofInstant(Instant.ofEpochMilli(this.holder.value), ZoneOffset.UTC); <#else> ${friendlyType} value = new ${friendlyType}(this.holder.value); return value; @@ -281,6 +287,12 @@ public class ${holderMode}${name}HolderReaderImpl extends AbstractFieldReader { <#elseif minor.class == "Bit" > return Boolean.valueOf(holder.value != 0); +<#elseif minor.class == "Time"> + return LocalDateTime.ofInstant(Instant.ofEpochMilli(this.holder.value), ZoneOffset.UTC).toLocalTime(); +<#elseif minor.class == "Date"> + return LocalDateTime.ofInstant(Instant.ofEpochMilli(this.holder.value), ZoneOffset.UTC).toLocalDate(); +<#elseif minor.class == "TimeStamp"> + return LocalDateTime.ofInstant(Instant.ofEpochMilli(this.holder.value), ZoneOffset.UTC); <#else> ${friendlyType} value = new ${friendlyType}(this.holder.value); return value; diff --git a/exec/vector/src/main/codegen/templates/NullReader.java b/exec/vector/src/main/codegen/templates/NullReader.java index 4d867baa4..28d48b750 100644 --- a/exec/vector/src/main/codegen/templates/NullReader.java +++ b/exec/vector/src/main/codegen/templates/NullReader.java @@ -123,7 +123,7 @@ public class NullReader extends AbstractBaseReader implements FieldReader { } <#list ["Object", "BigDecimal", "Integer", "Long", "Boolean", - "Character", "DateTime", "Period", "Double", "Float", + "Character", "LocalDate", "LocalTime", "LocalDateTime", "Period", "Double", "Float", "Text", "String", "Byte", "Short", "byte[]"] as friendlyType> <#assign safeType=friendlyType /> <#if safeType=="byte[]"><#assign safeType="ByteArray" /></#if> diff --git a/exec/vector/src/main/codegen/templates/UnionReader.java b/exec/vector/src/main/codegen/templates/UnionReader.java index 54276f573..84a2327b7 100644 --- a/exec/vector/src/main/codegen/templates/UnionReader.java +++ b/exec/vector/src/main/codegen/templates/UnionReader.java @@ -124,7 +124,7 @@ public class UnionReader extends AbstractFieldReader { } <#list ["Object", "BigDecimal", "Integer", "Long", "Boolean", - "Character", "DateTime", "Period", "Double", "Float", + "Character", "LocalDate", "LocalTime", "LocalDateTime", "Period", "Double", "Float", "Text", "String", "Byte", "Short", "byte[]"] as friendlyType> <#assign safeType=friendlyType /> <#if safeType=="byte[]"><#assign safeType="ByteArray" /></#if> diff --git a/exec/vector/src/main/java/org/apache/drill/exec/expr/fn/impl/DateUtility.java b/exec/vector/src/main/java/org/apache/drill/exec/expr/fn/impl/DateUtility.java index a52c95af9..21a4352fa 100644 --- a/exec/vector/src/main/java/org/apache/drill/exec/expr/fn/impl/DateUtility.java +++ b/exec/vector/src/main/java/org/apache/drill/exec/expr/fn/impl/DateUtility.java @@ -17,10 +17,15 @@ */ package org.apache.drill.exec.expr.fn.impl; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.DateTimeFormatterBuilder; -import org.joda.time.format.DateTimeParser; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; import com.carrotsearch.hppc.ObjectIntHashMap; @@ -621,10 +626,14 @@ public class DateUtility { } } - public static final DateTimeFormatter formatDate = DateTimeFormat.forPattern("yyyy-MM-dd"); - public static final DateTimeFormatter formatTimeStamp = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS"); - public static final DateTimeFormatter formatTimeStampTZ = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS ZZZ"); - public static final DateTimeFormatter formatTime = DateTimeFormat.forPattern("HH:mm:ss.SSS"); + public static final DateTimeFormatter formatDate = buildFormatter("yyyy-MM-dd"); + public static final DateTimeFormatter formatTimeStamp = buildFormatter("yyyy-MM-dd HH:mm:ss.SSS"); + public static final DateTimeFormatter formatTimeStampTZ = buildFormatter("yyyy-MM-dd HH:mm:ss.SSS VV"); + public static final DateTimeFormatter formatTime = buildFormatter("HH:mm:ss.SSS"); + + public static final DateTimeFormatter isoFormatDate = formatDate; + public static final DateTimeFormatter isoFormatTimeStamp= buildFormatter("yyyy-MM-dd'T'HH:mm:ss.SSSXX"); + public static final DateTimeFormatter isoFormatTime = buildFormatter("HH:mm:ss.SSSXX"); public static DateTimeFormatter dateTimeTZFormat = null; public static DateTimeFormatter timeFormat = null; @@ -639,29 +648,95 @@ public class DateUtility { return timezoneList[index]; } + /** + * Parse given string into a LocalDate + */ + public static LocalDate parseLocalDate(final String value) { + return LocalDate.parse(value, formatDate); + } + + /** + * Parse given string into a LocalTime + */ + public static LocalTime parseLocalTime(final String value) { + return LocalTime.parse(value, formatTime); + } + + /** + * Parse the given string into a LocalDateTime. + */ + public static LocalDateTime parseLocalDateTime(final String value) { + return LocalDateTime.parse(value, formatTimeStamp); + } + // Returns the date time formatter used to parse date strings public static DateTimeFormatter getDateTimeFormatter() { if (dateTimeTZFormat == null) { - DateTimeFormatter dateFormatter = DateTimeFormat.forPattern("yyyy-MM-dd"); - DateTimeParser optionalTime = DateTimeFormat.forPattern(" HH:mm:ss").getParser(); - DateTimeParser optionalSec = DateTimeFormat.forPattern(".SSS").getParser(); - DateTimeParser optionalZone = DateTimeFormat.forPattern(" ZZZ").getParser(); + DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + DateTimeFormatter optionalTime = DateTimeFormatter.ofPattern(" HH:mm:ss"); + DateTimeFormatter optionalSec = DateTimeFormatter.ofPattern(".SSS"); + DateTimeFormatter optionalZone = DateTimeFormatter.ofPattern(" ZZZ"); - dateTimeTZFormat = new DateTimeFormatterBuilder().append(dateFormatter).appendOptional(optionalTime).appendOptional(optionalSec).appendOptional(optionalZone).toFormatter(); + dateTimeTZFormat = new DateTimeFormatterBuilder().parseLenient() + .append(dateFormatter) + .appendOptional(optionalTime) + .appendOptional(optionalSec) + .appendOptional(optionalZone) + .toFormatter(); } return dateTimeTZFormat; } + /** + * Best effort parsing of the given value + */ + public static LocalDateTime parseBest(String value) { + TemporalAccessor parsed = getDateTimeFormatter().parse(value); + LocalDate datePart; + LocalTime timePart; + ZoneOffset zoneOffset; + + long epochDay = 0, nanoSeconds = 0; + int offsetSeconds = 0; + + // get different parsed parts + if (parsed.isSupported(ChronoField.EPOCH_DAY)) { + epochDay = parsed.getLong(ChronoField.EPOCH_DAY); + } + if (parsed.isSupported(ChronoField.NANO_OF_DAY)) { + nanoSeconds = parsed.getLong(ChronoField.NANO_OF_DAY); + } + if (parsed.isSupported(ChronoField.OFFSET_SECONDS)) { + offsetSeconds = parsed.get(ChronoField.OFFSET_SECONDS); + } + + zoneOffset = ZoneOffset.ofTotalSeconds(offsetSeconds); + datePart = LocalDate.ofEpochDay(epochDay); + timePart = LocalTime.ofNanoOfDay(nanoSeconds); + + return OffsetDateTime.of(datePart, timePart, zoneOffset).toLocalDateTime(); + } + // Returns time formatter used to parse time strings public static DateTimeFormatter getTimeFormatter() { if (timeFormat == null) { - DateTimeFormatter timeFormatter = DateTimeFormat.forPattern("HH:mm:ss"); - DateTimeParser optionalSec = DateTimeFormat.forPattern(".SSS").getParser(); - timeFormat = new DateTimeFormatterBuilder().append(timeFormatter).appendOptional(optionalSec).toFormatter(); + DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss"); + DateTimeFormatter optionalSec = DateTimeFormatter.ofPattern(".SSS"); + timeFormat = new DateTimeFormatterBuilder().parseLenient() + .append(timeFormatter) + .appendOptional(optionalSec) + .toFormatter(); } return timeFormat; } + // return a formatter that is lenient in its parsing of fields. e.g. + // if the month specification is "MM", a lenient version of the formatter + // will accept a single digit month number as well as the 2-digit month + // number. + public static DateTimeFormatter buildFormatter(String pattern) { + return new DateTimeFormatterBuilder().parseLenient().append(DateTimeFormatter.ofPattern(pattern)).toFormatter(); + } } diff --git a/exec/vector/src/main/java/org/apache/drill/exec/util/JsonStringArrayList.java b/exec/vector/src/main/java/org/apache/drill/exec/util/JsonStringArrayList.java index 216c5cc73..695befdb8 100644 --- a/exec/vector/src/main/java/org/apache/drill/exec/util/JsonStringArrayList.java +++ b/exec/vector/src/main/java/org/apache/drill/exec/util/JsonStringArrayList.java @@ -29,6 +29,7 @@ public class JsonStringArrayList<E> extends ArrayList<E> { static { mapper = new ObjectMapper(); + mapper.registerModule(SerializationModule.getModule()); } @Override diff --git a/exec/vector/src/main/java/org/apache/drill/exec/util/JsonStringHashMap.java b/exec/vector/src/main/java/org/apache/drill/exec/util/JsonStringHashMap.java index e4de6d884..f4352d114 100644 --- a/exec/vector/src/main/java/org/apache/drill/exec/util/JsonStringHashMap.java +++ b/exec/vector/src/main/java/org/apache/drill/exec/util/JsonStringHashMap.java @@ -34,6 +34,7 @@ public class JsonStringHashMap<K, V> extends LinkedHashMap<K, V> { static { mapper = new ObjectMapper(); + mapper.registerModule(SerializationModule.getModule()); } @Override diff --git a/exec/vector/src/main/java/org/apache/drill/exec/util/SerializationModule.java b/exec/vector/src/main/java/org/apache/drill/exec/util/SerializationModule.java new file mode 100644 index 000000000..04f4144c8 --- /dev/null +++ b/exec/vector/src/main/java/org/apache/drill/exec/util/SerializationModule.java @@ -0,0 +1,75 @@ +/* + * 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.drill.exec.util; + +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; + +/** + * This helper class holds any custom Jackson serializers used when outputing + * the data in JSON format. + */ +public class SerializationModule { + + // copied from DateUtility. Added here for inclusion into drill-jdbc-all + public static final DateTimeFormatter formatDate = DateTimeFormatter.ofPattern("uuuu-MM-dd") + .withZone(ZoneOffset.UTC); + public static final DateTimeFormatter formatTimeStamp = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSS") + .withZone(ZoneOffset.UTC); + public static final DateTimeFormatter formatTime = DateTimeFormatter.ofPattern("HH:mm:ss.SSS") + .withZone(ZoneOffset.UTC); + + public static final SimpleModule drillModule = new SimpleModule("DrillModule"); + + static { + drillModule.addSerializer(LocalTime.class, new JsonSerializer<LocalTime>() { + @Override + public void serialize(LocalTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { + gen.writeString(formatTime.format(value)); + } + }); + + drillModule.addSerializer(LocalDate.class, new JsonSerializer<LocalDate>() { + @Override + public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { + gen.writeString(formatDate.format(value)); + } + }); + + drillModule.addSerializer(LocalDateTime.class, new JsonSerializer<LocalDateTime>() { + @Override + public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { + gen.writeString(formatTimeStamp.format(value)); + } + }); + } + + public static final SimpleModule getModule() { + return drillModule; + } +} |