You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by nk...@apache.org on 2018/10/01 10:05:07 UTC

[avro] branch master updated: AVRO-2079: Add ability to generate Java8 native date/time classes (new) (#309)

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

nkollar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/master by this push:
     new b814cd0  AVRO-2079: Add ability to generate Java8 native date/time classes (new) (#309)
b814cd0 is described below

commit b814cd0b76b63b08cd7772f43c3cb904a1dcbff5
Author: Paul Vorbach <pa...@vorba.ch>
AuthorDate: Mon Oct 1 12:04:50 2018 +0200

    AVRO-2079: Add ability to generate Java8 native date/time classes (new) (#309)
    
    AVRO-2079: Add ability to use Java 8 date/time types instead of Joda time.
    
    Add compiler option to allow generating Java 8 date/time classes instead of Joda time. This commit adds a new optional compiler option -dateTimeLogicalTypeImpl to specify which date/time implementation to use. It could be joda or jsr310, if not specified by, then the default is joda.
---
 .../apache/avro/data/Jsr310TimeConversions.java    | 184 +++++++++
 .../avro/data/TestJsr310TimeConversions.java       | 241 ++++++++++++
 ....java => TestRecordWithJsr310LogicalTypes.java} | 428 +++++++++++++--------
 .../avro/specific/TestRecordWithLogicalTypes.java  |   7 +-
 .../avro/specific/TestSpecificLogicalTypes.java    | 117 +++++-
 .../test/resources/record_with_logical_types.avsc  |   2 +-
 .../avro/compiler/specific/ProtocolTask.java       |  14 +-
 .../apache/avro/compiler/specific/SchemaTask.java  |   2 +-
 .../avro/compiler/specific/SpecificCompiler.java   |  80 +++-
 .../specific/templates/java/classic/record.vm      |   8 +-
 .../compiler/specific/TestSpecificCompiler.java    |  55 ++-
 .../org/apache/avro/mojo/AbstractAvroMojo.java     |  26 +-
 .../java/org/apache/avro/mojo/IDLProtocolMojo.java |   2 +-
 .../java/org/apache/avro/mojo/ProtocolMojo.java    |   2 +-
 .../main/java/org/apache/avro/mojo/SchemaMojo.java |   2 +-
 lang/java/maven-plugin/src/test/avro/User.avdl     |   1 +
 lang/java/maven-plugin/src/test/avro/User.avpr     |   7 +
 lang/java/maven-plugin/src/test/avro/User.avsc     |   7 +
 .../org/apache/avro/mojo/AbstractAvroMojoTest.java |   3 +-
 .../org/apache/avro/mojo/TestIDLProtocolMojo.java  |  33 +-
 .../org/apache/avro/mojo/TestProtocolMojo.java     |  33 +-
 .../java/org/apache/avro/mojo/TestSchemaMojo.java  |  33 +-
 .../resources/unit/idl/{pom.xml => pom-joda.xml}   |  12 +-
 .../resources/unit/idl/{pom.xml => pom-jsr310.xml} |  12 +-
 .../unit/protocol/{pom.xml => pom-joda.xml}        |  10 +-
 .../unit/protocol/{pom.xml => pom-jsr310.xml}      |  11 +-
 .../unit/schema/{pom.xml => pom-joda.xml}          |  11 +-
 .../unit/schema/{pom.xml => pom-jsr310.xml}        |  12 +-
 .../org/apache/avro/tool/SpecificCompilerTool.java |  43 ++-
 pom.xml                                            |   1 +
 30 files changed, 1145 insertions(+), 254 deletions(-)

diff --git a/lang/java/avro/src/main/java/org/apache/avro/data/Jsr310TimeConversions.java b/lang/java/avro/src/main/java/org/apache/avro/data/Jsr310TimeConversions.java
new file mode 100644
index 0000000..a717bbc
--- /dev/null
+++ b/lang/java/avro/src/main/java/org/apache/avro/data/Jsr310TimeConversions.java
@@ -0,0 +1,184 @@
+/*
+ * 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.avro.data;
+
+import org.apache.avro.Conversion;
+import org.apache.avro.LogicalType;
+import org.apache.avro.LogicalTypes;
+import org.apache.avro.Schema;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.concurrent.TimeUnit;
+
+public class Jsr310TimeConversions {
+  public static class DateConversion extends Conversion<LocalDate> {
+
+    @Override
+    public Class<LocalDate> getConvertedType() {
+      return LocalDate.class;
+    }
+
+    @Override
+    public String getLogicalTypeName() {
+      return "date";
+    }
+
+    @Override
+    public LocalDate fromInt(Integer daysFromEpoch, Schema schema, LogicalType type) {
+      return LocalDate.ofEpochDay(daysFromEpoch);
+    }
+
+    @Override
+    public Integer toInt(LocalDate date, Schema schema, LogicalType type) {
+      long epochDays = date.toEpochDay();
+
+      return (int) epochDays;
+    }
+
+    @Override
+    public Schema getRecommendedSchema() {
+      return LogicalTypes.date().addToSchema(Schema.create(Schema.Type.INT));
+    }
+  }
+
+  public static class TimeMillisConversion extends Conversion<LocalTime> {
+    @Override
+    public Class<LocalTime> getConvertedType() {
+      return LocalTime.class;
+    }
+
+    @Override
+    public String getLogicalTypeName() {
+      return "time-millis";
+    }
+
+    @Override
+    public LocalTime fromInt(Integer millisFromMidnight, Schema schema, LogicalType type) {
+      return LocalTime.ofNanoOfDay(TimeUnit.MILLISECONDS.toNanos(millisFromMidnight));
+    }
+
+    @Override
+    public Integer toInt(LocalTime time, Schema schema, LogicalType type) {
+      return (int) TimeUnit.NANOSECONDS.toMillis(time.toNanoOfDay());
+    }
+
+    @Override
+    public Schema getRecommendedSchema() {
+      return LogicalTypes.timeMillis().addToSchema(Schema.create(Schema.Type.INT));
+    }
+  }
+
+  public static class TimeMicrosConversion extends Conversion<LocalTime> {
+    @Override
+    public Class<LocalTime> getConvertedType() {
+      return LocalTime.class;
+    }
+
+    @Override
+    public String getLogicalTypeName() {
+      return "time-micros";
+    }
+
+    @Override
+    public LocalTime fromLong(Long microsFromMidnight, Schema schema, LogicalType type) {
+      return LocalTime.ofNanoOfDay(TimeUnit.MICROSECONDS.toNanos(microsFromMidnight));
+    }
+
+    @Override
+    public Long toLong(LocalTime time, Schema schema, LogicalType type) {
+      return TimeUnit.NANOSECONDS.toMicros(time.toNanoOfDay());
+    }
+
+    @Override
+    public Schema getRecommendedSchema() {
+      return LogicalTypes.timeMicros().addToSchema(Schema.create(Schema.Type.LONG));
+    }
+  }
+
+  public static class TimestampMillisConversion extends Conversion<Instant> {
+    @Override
+    public Class<Instant> getConvertedType() {
+      return Instant.class;
+    }
+
+    @Override
+    public String getLogicalTypeName() {
+      return "timestamp-millis";
+    }
+
+    @Override
+    public Instant fromLong(Long millisFromEpoch, Schema schema, LogicalType type) {
+      return Instant.ofEpochMilli(millisFromEpoch);
+    }
+
+    @Override
+    public Long toLong(Instant timestamp, Schema schema, LogicalType type) {
+      return timestamp.toEpochMilli();
+    }
+
+    @Override
+    public Schema getRecommendedSchema() {
+      return LogicalTypes.timestampMillis().addToSchema(Schema.create(Schema.Type.LONG));
+    }
+  }
+
+  public static class TimestampMicrosConversion extends Conversion<Instant> {
+    @Override
+    public Class<Instant> getConvertedType() {
+      return Instant.class;
+    }
+
+    @Override
+    public String getLogicalTypeName() {
+      return "timestamp-micros";
+    }
+
+    @Override
+    public Instant fromLong(Long microsFromEpoch, Schema schema, LogicalType type) {
+      long epochSeconds = microsFromEpoch / (1_000_000);
+      long nanoAdjustment = (microsFromEpoch % (1_000_000)) * 1_000;
+
+      return Instant.ofEpochSecond(epochSeconds, nanoAdjustment);
+    }
+
+    @Override
+    public Long toLong(Instant instant, Schema schema, LogicalType type) {
+      long seconds = instant.getEpochSecond();
+      int nanos = instant.getNano();
+
+      if (seconds < 0 && nanos > 0) {
+        long micros = Math.multiplyExact(seconds + 1, 1_000_000);
+        long adjustment = (nanos / 1_000L) - 1_000_000;
+
+        return Math.addExact(micros, adjustment);
+      } else {
+        long micros = Math.multiplyExact(seconds, 1_000_000);
+
+        return Math.addExact(micros, nanos / 1_000);
+      }
+    }
+
+    @Override
+    public Schema getRecommendedSchema() {
+      return LogicalTypes.timestampMicros().addToSchema(Schema.create(Schema.Type.LONG));
+    }
+  }
+}
diff --git a/lang/java/avro/src/test/java/org/apache/avro/data/TestJsr310TimeConversions.java b/lang/java/avro/src/test/java/org/apache/avro/data/TestJsr310TimeConversions.java
new file mode 100644
index 0000000..caf699e
--- /dev/null
+++ b/lang/java/avro/src/test/java/org/apache/avro/data/TestJsr310TimeConversions.java
@@ -0,0 +1,241 @@
+/*
+ * 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.avro.data;
+
+import org.apache.avro.LogicalTypes;
+import org.apache.avro.Schema;
+import org.apache.avro.data.Jsr310TimeConversions.DateConversion;
+import org.apache.avro.data.Jsr310TimeConversions.TimeMillisConversion;
+import org.apache.avro.data.Jsr310TimeConversions.TimeMicrosConversion;
+import org.apache.avro.data.Jsr310TimeConversions.TimestampMicrosConversion;
+import org.apache.avro.data.Jsr310TimeConversions.TimestampMillisConversion;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZonedDateTime;
+import java.time.ZoneOffset;
+
+public class TestJsr310TimeConversions {
+
+  public static Schema DATE_SCHEMA;
+  public static Schema TIME_MILLIS_SCHEMA;
+  public static Schema TIME_MICROS_SCHEMA;
+  public static Schema TIMESTAMP_MILLIS_SCHEMA;
+  public static Schema TIMESTAMP_MICROS_SCHEMA;
+
+  @BeforeClass
+  public static void createSchemas() {
+    TestJsr310TimeConversions.DATE_SCHEMA = LogicalTypes.date()
+        .addToSchema(Schema.create(Schema.Type.INT));
+    TestJsr310TimeConversions.TIME_MILLIS_SCHEMA = LogicalTypes.timeMillis()
+        .addToSchema(Schema.create(Schema.Type.INT));
+    TestJsr310TimeConversions.TIME_MICROS_SCHEMA = LogicalTypes.timeMicros()
+        .addToSchema(Schema.create(Schema.Type.LONG));
+    TestJsr310TimeConversions.TIMESTAMP_MILLIS_SCHEMA = LogicalTypes.timestampMillis()
+        .addToSchema(Schema.create(Schema.Type.LONG));
+    TestJsr310TimeConversions.TIMESTAMP_MICROS_SCHEMA = LogicalTypes.timestampMicros()
+        .addToSchema(Schema.create(Schema.Type.LONG));
+  }
+
+  @Test
+  public void testDateConversion() throws Exception {
+    DateConversion conversion = new DateConversion();
+    LocalDate Jan_6_1970 = LocalDate.of(1970, 1, 6);    //  5
+    LocalDate Jan_1_1970 = LocalDate.of(1970, 1, 1);    //  0
+    LocalDate Dec_27_1969 = LocalDate.of(1969, 12, 27); // -5
+
+    Assert.assertEquals("6 Jan 1970 should be 5", 5,
+        (int) conversion.toInt(Jan_6_1970, DATE_SCHEMA, LogicalTypes.date()));
+    Assert.assertEquals("1 Jan 1970 should be 0", 0,
+        (int) conversion.toInt(Jan_1_1970, DATE_SCHEMA, LogicalTypes.date()));
+    Assert.assertEquals("27 Dec 1969 should be -5", -5,
+        (int) conversion.toInt(Dec_27_1969, DATE_SCHEMA, LogicalTypes.date()));
+
+    Assert.assertEquals("6 Jan 1970 should be 5",
+        conversion.fromInt(5, DATE_SCHEMA, LogicalTypes.date()), Jan_6_1970);
+    Assert.assertEquals("1 Jan 1970 should be 0",
+        conversion.fromInt(0, DATE_SCHEMA, LogicalTypes.date()), Jan_1_1970);
+    Assert.assertEquals("27 Dec 1969 should be -5",
+        conversion.fromInt(-5, DATE_SCHEMA, LogicalTypes.date()), Dec_27_1969);
+  }
+
+  @Test
+  public void testTimeMillisConversion() throws Exception {
+    TimeMillisConversion conversion = new TimeMillisConversion();
+    LocalTime oneAM = LocalTime.of(1, 0);
+    LocalTime afternoon = LocalTime.of(15, 14, 15, 926_000_000);
+    int afternoonMillis = ((15 * 60 + 14) * 60 + 15) * 1000 + 926;
+
+    Assert.assertEquals("Midnight should be 0", 0,
+        (int) conversion.toInt(
+            LocalTime.MIDNIGHT, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis()));
+    Assert.assertEquals("01:00 should be 3,600,000", 3_600_000,
+        (int) conversion.toInt(
+            oneAM, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis()));
+    Assert.assertEquals("15:14:15.926 should be " + afternoonMillis,
+        afternoonMillis,
+        (int) conversion.toInt(
+            afternoon, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis()));
+
+    Assert.assertEquals("Midnight should be 0",
+        LocalTime.MIDNIGHT,
+        conversion.fromInt(0, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis()));
+    Assert.assertEquals("01:00 should be 3,600,000",
+        oneAM,
+        conversion.fromInt(
+            3600000, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis()));
+    Assert.assertEquals("15:14:15.926 should be " + afternoonMillis,
+        afternoon,
+        conversion.fromInt(
+            afternoonMillis, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis()));
+  }
+
+  @Test
+  public void testTimeMicrosConversion() throws Exception {
+    TimeMicrosConversion conversion = new TimeMicrosConversion();
+    LocalTime oneAM = LocalTime.of(1, 0);
+    LocalTime afternoon = LocalTime.of(15, 14, 15, 926_551_000);
+    long afternoonMicros = ((long) (15 * 60 + 14) * 60 + 15) * 1_000_000 + 926_551;
+
+    Assert.assertEquals("Midnight should be 0",
+        LocalTime.MIDNIGHT,
+        conversion.fromLong(0L, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
+    Assert.assertEquals("01:00 should be 3,600,000,000",
+        oneAM,
+        conversion.fromLong(
+            3_600_000_000L, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
+    Assert.assertEquals("15:14:15.926551 should be " + afternoonMicros,
+        afternoon,
+        conversion.fromLong(
+            afternoonMicros, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
+
+    Assert.assertEquals("Midnight should be 0", 0,
+        (long) conversion.toLong(
+            LocalTime.MIDNIGHT, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
+    Assert.assertEquals("01:00 should be 3,600,000,000", 3_600_000_000L,
+        (long) conversion.toLong(
+            oneAM, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
+    Assert.assertEquals("15:14:15.926551 should be " + afternoonMicros,
+        afternoonMicros,
+        (long) conversion.toLong(
+            afternoon, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
+  }
+
+  @Test
+  public void testTimestampMillisConversion() throws Exception {
+    TimestampMillisConversion conversion = new TimestampMillisConversion();
+    long nowInstant = Instant.now().toEpochMilli(); // ms precision
+
+    // round trip
+    Instant now = conversion.fromLong(
+        nowInstant, TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis());
+    long roundTrip = conversion.toLong(
+        now, TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis());
+    Assert.assertEquals("Round-trip conversion should work",
+        nowInstant, roundTrip);
+
+    long May_28_2015_21_46_53_221_instant = 1432849613221L;
+    Instant May_28_2015_21_46_53_221 =
+        ZonedDateTime.of(2015, 5, 28, 21, 46, 53, 221_000_000, ZoneOffset.UTC).toInstant();
+
+    // known dates from https://www.epochconverter.com/
+    // > Epoch
+    Assert.assertEquals("Known date should be correct",
+        May_28_2015_21_46_53_221,
+        conversion.fromLong(May_28_2015_21_46_53_221_instant,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+    Assert.assertEquals("Known date should be correct",
+        May_28_2015_21_46_53_221_instant,
+        (long) conversion.toLong(May_28_2015_21_46_53_221,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+
+    // Epoch
+    Assert.assertEquals("1970-01-01 should be 0",
+        Instant.EPOCH,
+        conversion.fromLong(0L,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+    Assert.assertEquals("1970-01-01 should be 0",
+        0L,
+        (long) conversion.toLong(ZonedDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC).toInstant(),
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+
+    // < Epoch
+    long Jul_01_1969_12_00_00_123_instant = -15854400000L + 123;
+    Instant Jul_01_1969_12_00_00_123 =
+        ZonedDateTime.of(1969, 7, 1, 12, 0, 0, 123_000_000, ZoneOffset.UTC).toInstant();
+
+    Assert.assertEquals("Pre 1970 date should be correct",
+        Jul_01_1969_12_00_00_123,
+        conversion.fromLong(Jul_01_1969_12_00_00_123_instant,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+    Assert.assertEquals("Pre 1970 date should be correct",
+        Jul_01_1969_12_00_00_123_instant,
+        (long) conversion.toLong(Jul_01_1969_12_00_00_123,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+  }
+
+  @Test
+  public void testTimestampMicrosConversion() throws Exception {
+    TimestampMicrosConversion conversion = new TimestampMicrosConversion();
+
+    // known dates from https://www.epochconverter.com/
+    // > Epoch
+    long May_28_2015_21_46_53_221_843_instant = 1432849613221L * 1000 + 843;
+    Instant May_28_2015_21_46_53_221_843 =
+        ZonedDateTime.of(2015, 5, 28, 21, 46, 53, 221_843_000, ZoneOffset.UTC).toInstant();
+
+    Assert.assertEquals("Known date should be correct",
+        May_28_2015_21_46_53_221_843,
+        conversion.fromLong(May_28_2015_21_46_53_221_843_instant,
+            TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMicros()));
+
+    Assert.assertEquals("Known date should be correct",
+        May_28_2015_21_46_53_221_843_instant,
+        (long) conversion.toLong(May_28_2015_21_46_53_221_843,
+            TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMillis()));
+
+    // Epoch
+    Assert.assertEquals("1970-01-01 should be 0",
+        Instant.EPOCH,
+        conversion.fromLong(0L,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+    Assert.assertEquals("1970-01-01 should be 0",
+        0L,
+        (long) conversion.toLong(ZonedDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC).toInstant(),
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+
+    // < Epoch
+    long Jul_01_1969_12_00_00_000_123_instant = -15854400000L * 1000 + 123;
+    Instant Jul_01_1969_12_00_00_000_123 =
+        ZonedDateTime.of(1969, 7, 1, 12, 0, 0, 123_000, ZoneOffset.UTC).toInstant();
+
+    Assert.assertEquals("Pre 1970 date should be correct",
+        Jul_01_1969_12_00_00_000_123,
+        conversion.fromLong(Jul_01_1969_12_00_00_000_123_instant,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+    Assert.assertEquals("Pre 1970 date should be correct",
+        Jul_01_1969_12_00_00_000_123_instant,
+        (long) conversion.toLong(Jul_01_1969_12_00_00_000_123,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+  }
+}
diff --git a/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java b/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJsr310LogicalTypes.java
similarity index 52%
copy from lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java
copy to lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJsr310LogicalTypes.java
index 493a8e6..c42b3ca 100644
--- a/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java
+++ b/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJsr310LogicalTypes.java
@@ -5,33 +5,47 @@
  */
 package org.apache.avro.specific;
 
-import org.apache.avro.message.BinaryMessageDecoder;
 import org.apache.avro.message.BinaryMessageEncoder;
-
-import java.math.BigDecimal;
+import org.apache.avro.message.BinaryMessageDecoder;
+import org.apache.avro.message.SchemaStore;
 
 @SuppressWarnings("all")
 @org.apache.avro.specific.AvroGenerated
-public class TestRecordWithLogicalTypes extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
-  private static final long serialVersionUID = -4211233492739285532L;
-  public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"TestRecordWithLogicalTypes\",\"namespace\":\"org.apache.avro.specific\",\"fields\":[{\"name\":\"b\",\"type\":\"boolean\"},{\"name\":\"i32\",\"type\":\"int\"},{\"name\":\"i64\",\"type\":\"long\"},{\"name\":\"f32\",\"type\":\"float\"},{\"name\":\"f64\",\"type\":\"double\"},{\"name\":\"s\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"d\",\"type\": [...]
+public class TestRecordWithJsr310LogicalTypes extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
+  private static final long serialVersionUID = 3313339903648295220L;
+  public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"TestRecordWithJsr310LogicalTypes\",\"namespace\":\"org.apache.avro.specific\",\"fields\":[{\"name\":\"b\",\"type\":\"boolean\"},{\"name\":\"i32\",\"type\":\"int\"},{\"name\":\"i64\",\"type\":\"long\"},{\"name\":\"f32\",\"type\":\"float\"},{\"name\":\"f64\",\"type\":\"double\"},{\"name\":\"s\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"d\",\"t [...]
   public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }
 
   private static SpecificData MODEL$ = new SpecificData();
 
-  private static final BinaryMessageEncoder<TestRecordWithLogicalTypes> ENCODER =
-      new BinaryMessageEncoder<TestRecordWithLogicalTypes>(MODEL$, SCHEMA$);
+  private static final BinaryMessageEncoder<TestRecordWithJsr310LogicalTypes> ENCODER =
+      new BinaryMessageEncoder<TestRecordWithJsr310LogicalTypes>(MODEL$, SCHEMA$);
+
+  private static final BinaryMessageDecoder<TestRecordWithJsr310LogicalTypes> DECODER =
+      new BinaryMessageDecoder<TestRecordWithJsr310LogicalTypes>(MODEL$, SCHEMA$);
+
+  /**
+   * Return the BinaryMessageDecoder instance used by this class.
+   */
+  public static BinaryMessageDecoder<TestRecordWithJsr310LogicalTypes> getDecoder() {
+    return DECODER;
+  }
 
-  private static final BinaryMessageDecoder<TestRecordWithLogicalTypes> DECODER =
-      new BinaryMessageDecoder<TestRecordWithLogicalTypes>(MODEL$, SCHEMA$);
+  /**
+   * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}.
+   * @param resolver a {@link SchemaStore} used to find schemas by fingerprint
+   */
+  public static BinaryMessageDecoder<TestRecordWithJsr310LogicalTypes> createDecoder(SchemaStore resolver) {
+    return new BinaryMessageDecoder<TestRecordWithJsr310LogicalTypes>(MODEL$, SCHEMA$, resolver);
+  }
 
-  /** Serializes this ${schema.getName()} to a ByteBuffer. */
+  /** Serializes this TestRecordWithJsr310LogicalTypes to a ByteBuffer. */
   public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {
     return ENCODER.encode(this);
   }
 
-  /** Deserializes a ${schema.getName()} from a ByteBuffer. */
-  public static TestRecordWithLogicalTypes fromByteBuffer(
+  /** Deserializes a TestRecordWithJsr310LogicalTypes from a ByteBuffer. */
+  public static TestRecordWithJsr310LogicalTypes fromByteBuffer(
       java.nio.ByteBuffer b) throws java.io.IOException {
     return DECODER.decode(b);
   }
@@ -42,31 +56,32 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
   @Deprecated public float f32;
   @Deprecated public double f64;
   @Deprecated public java.lang.CharSequence s;
-  @Deprecated public org.joda.time.LocalDate d;
-  @Deprecated public org.joda.time.LocalTime t;
-  @Deprecated public org.joda.time.DateTime ts;
-  @Deprecated public BigDecimal dec;
+  @Deprecated public java.time.LocalDate d;
+  @Deprecated public java.time.LocalTime t;
+  @Deprecated public java.time.Instant ts;
+  @Deprecated public java.math.BigDecimal dec;
 
   /**
    * Default constructor.  Note that this does not initialize fields
    * to their default values from the schema.  If that is desired then
    * one should use <code>newBuilder()</code>.
    */
-  public TestRecordWithLogicalTypes() {}
+  public TestRecordWithJsr310LogicalTypes() {}
 
   /**
    * All-args constructor.
+   * @param b The new value for b
+   * @param i32 The new value for i32
+   * @param i64 The new value for i64
+   * @param f32 The new value for f32
+   * @param f64 The new value for f64
+   * @param s The new value for s
+   * @param d The new value for d
+   * @param t The new value for t
+   * @param ts The new value for ts
+   * @param dec The new value for dec
    */
-  public TestRecordWithLogicalTypes(java.lang.Boolean b,
-                                    java.lang.Integer i32,
-                                    java.lang.Long i64,
-                                    java.lang.Float f32,
-                                    java.lang.Double f64,
-                                    java.lang.CharSequence s,
-                                    org.joda.time.LocalDate d,
-                                    org.joda.time.LocalTime t,
-                                    org.joda.time.DateTime ts,
-                                    BigDecimal dec) {
+  public TestRecordWithJsr310LogicalTypes(java.lang.Boolean b, java.lang.Integer i32, java.lang.Long i64, java.lang.Float f32, java.lang.Double f64, java.lang.CharSequence s, java.time.LocalDate d, java.time.LocalTime t, java.time.Instant ts, java.math.BigDecimal dec) {
     this.b = b;
     this.i32 = i32;
     this.i64 = i64;
@@ -96,6 +111,32 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
     default: throw new org.apache.avro.AvroRuntimeException("Bad index");
     }
   }
+
+  protected static final org.apache.avro.Conversions.DecimalConversion DECIMAL_CONVERSION = new org.apache.avro.Conversions.DecimalConversion();
+  protected static final org.apache.avro.data.Jsr310TimeConversions.DateConversion DATE_CONVERSION = new org.apache.avro.data.Jsr310TimeConversions.DateConversion();
+  protected static final org.apache.avro.data.Jsr310TimeConversions.TimeMillisConversion TIME_CONVERSION = new org.apache.avro.data.Jsr310TimeConversions.TimeMillisConversion();
+  protected static final org.apache.avro.data.Jsr310TimeConversions.TimestampMillisConversion TIMESTAMP_CONVERSION = new org.apache.avro.data.Jsr310TimeConversions.TimestampMillisConversion();
+
+  private static final org.apache.avro.Conversion<?>[] conversions =
+      new org.apache.avro.Conversion<?>[] {
+      null,
+      null,
+      null,
+      null,
+      null,
+      null,
+      DATE_CONVERSION,
+      TIME_CONVERSION,
+      TIMESTAMP_CONVERSION,
+      DECIMAL_CONVERSION,
+      null
+  };
+
+  @Override
+  public org.apache.avro.Conversion<?> getConversion(int field) {
+    return conversions[field];
+  }
+
   // Used by DatumReader.  Applications should not call.
   @SuppressWarnings(value="unchecked")
   public void put(int field$, java.lang.Object value$) {
@@ -106,16 +147,17 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
     case 3: f32 = (java.lang.Float)value$; break;
     case 4: f64 = (java.lang.Double)value$; break;
     case 5: s = (java.lang.CharSequence)value$; break;
-    case 6: d = (org.joda.time.LocalDate)value$; break;
-    case 7: t = (org.joda.time.LocalTime)value$; break;
-    case 8: ts = (org.joda.time.DateTime)value$; break;
-    case 9: dec = (BigDecimal) value$; break;
+    case 6: d = (java.time.LocalDate)value$; break;
+    case 7: t = (java.time.LocalTime)value$; break;
+    case 8: ts = (java.time.Instant)value$; break;
+    case 9: dec = (java.math.BigDecimal)value$; break;
     default: throw new org.apache.avro.AvroRuntimeException("Bad index");
     }
   }
 
   /**
    * Gets the value of the 'b' field.
+   * @return The value of the 'b' field.
    */
   public java.lang.Boolean getB() {
     return b;
@@ -131,6 +173,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
   /**
    * Gets the value of the 'i32' field.
+   * @return The value of the 'i32' field.
    */
   public java.lang.Integer getI32() {
     return i32;
@@ -146,6 +189,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
   /**
    * Gets the value of the 'i64' field.
+   * @return The value of the 'i64' field.
    */
   public java.lang.Long getI64() {
     return i64;
@@ -161,6 +205,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
   /**
    * Gets the value of the 'f32' field.
+   * @return The value of the 'f32' field.
    */
   public java.lang.Float getF32() {
     return f32;
@@ -176,6 +221,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
   /**
    * Gets the value of the 'f64' field.
+   * @return The value of the 'f64' field.
    */
   public java.lang.Double getF64() {
     return f64;
@@ -191,6 +237,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
   /**
    * Gets the value of the 's' field.
+   * @return The value of the 's' field.
    */
   public java.lang.CharSequence getS() {
     return s;
@@ -206,8 +253,9 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
   /**
    * Gets the value of the 'd' field.
+   * @return The value of the 'd' field.
    */
-  public org.joda.time.LocalDate getD() {
+  public java.time.LocalDate getD() {
     return d;
   }
 
@@ -215,14 +263,15 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
    * Sets the value of the 'd' field.
    * @param value the value to set.
    */
-  public void setD(org.joda.time.LocalDate value) {
+  public void setD(java.time.LocalDate value) {
     this.d = value;
   }
 
   /**
    * Gets the value of the 't' field.
+   * @return The value of the 't' field.
    */
-  public org.joda.time.LocalTime getT() {
+  public java.time.LocalTime getT() {
     return t;
   }
 
@@ -230,84 +279,81 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
    * Sets the value of the 't' field.
    * @param value the value to set.
    */
-  public void setT(org.joda.time.LocalTime value) {
+  public void setT(java.time.LocalTime value) {
     this.t = value;
   }
 
   /**
    * Gets the value of the 'ts' field.
+   * @return The value of the 'ts' field.
    */
-  public org.joda.time.DateTime getTs() {
+  public java.time.Instant getTs() {
     return ts;
   }
 
   /**
-   * Sets the value of the 'dec' field.
+   * Sets the value of the 'ts' field.
    * @param value the value to set.
    */
-  public void setDec(BigDecimal value) {
-    this.dec = value;
+  public void setTs(java.time.Instant value) {
+    this.ts = value;
   }
 
   /**
-   * Gets the value of the 'ts' field.
+   * Gets the value of the 'dec' field.
+   * @return The value of the 'dec' field.
    */
-  public BigDecimal getDec() {
+  public java.math.BigDecimal getDec() {
     return dec;
   }
 
   /**
-   * Sets the value of the 'ts' field.
+   * Sets the value of the 'dec' field.
    * @param value the value to set.
    */
-  public void setTs(org.joda.time.DateTime value) {
-    this.ts = value;
-  }
-
-  protected static final org.apache.avro.data.TimeConversions.DateConversion DATE_CONVERSION = new org.apache.avro.data.TimeConversions.DateConversion();
-  protected static final org.apache.avro.data.TimeConversions.TimeConversion TIME_CONVERSION = new org.apache.avro.data.TimeConversions.TimeConversion();
-  protected static final org.apache.avro.data.TimeConversions.TimestampConversion TIMESTAMP_CONVERSION = new org.apache.avro.data.TimeConversions.TimestampConversion();
-  protected static final org.apache.avro.Conversions.DecimalConversion DECIMAL_CONVERSION = new org.apache.avro.Conversions.DecimalConversion();
-  private final org.apache.avro.Conversion<?>[] conversions =
-      new org.apache.avro.Conversion<?>[] {
-      null,
-      null,
-      null,
-      null,
-      null,
-      null,
-      DATE_CONVERSION,
-      TIME_CONVERSION,
-      TIMESTAMP_CONVERSION,
-      DECIMAL_CONVERSION,
-      null
-  };
-
-  @Override
-  public org.apache.avro.Conversion<?> getConversion(int field) {
-    return conversions[field];
+  public void setDec(java.math.BigDecimal value) {
+    this.dec = value;
   }
 
-  /** Creates a new TestRecordWithLogicalTypes RecordBuilder */
-  public static TestRecordWithLogicalTypes.Builder newBuilder() {
-    return new TestRecordWithLogicalTypes.Builder();
+  /**
+   * Creates a new TestRecordWithJsr310LogicalTypes RecordBuilder.
+   * @return A new TestRecordWithJsr310LogicalTypes RecordBuilder
+   */
+  public static TestRecordWithJsr310LogicalTypes.Builder newBuilder() {
+    return new TestRecordWithJsr310LogicalTypes.Builder();
   }
 
-  /** Creates a new TestRecordWithLogicalTypes RecordBuilder by copying an existing Builder */
-  public static TestRecordWithLogicalTypes.Builder newBuilder(TestRecordWithLogicalTypes.Builder other) {
-    return new TestRecordWithLogicalTypes.Builder(other);
+  /**
+   * Creates a new TestRecordWithJsr310LogicalTypes RecordBuilder by copying an existing Builder.
+   * @param other The existing builder to copy.
+   * @return A new TestRecordWithJsr310LogicalTypes RecordBuilder
+   */
+  public static TestRecordWithJsr310LogicalTypes.Builder newBuilder(TestRecordWithJsr310LogicalTypes.Builder other) {
+    if (other == null) {
+      return new TestRecordWithJsr310LogicalTypes.Builder();
+    } else {
+      return new TestRecordWithJsr310LogicalTypes.Builder(other);
+    }
   }
 
-  /** Creates a new TestRecordWithLogicalTypes RecordBuilder by copying an existing TestRecordWithLogicalTypes instance */
-  public static TestRecordWithLogicalTypes.Builder newBuilder(TestRecordWithLogicalTypes other) {
-    return new TestRecordWithLogicalTypes.Builder(other);
+  /**
+   * Creates a new TestRecordWithJsr310LogicalTypes RecordBuilder by copying an existing TestRecordWithJsr310LogicalTypes instance.
+   * @param other The existing instance to copy.
+   * @return A new TestRecordWithJsr310LogicalTypes RecordBuilder
+   */
+  public static TestRecordWithJsr310LogicalTypes.Builder newBuilder(TestRecordWithJsr310LogicalTypes other) {
+    if (other == null) {
+      return new TestRecordWithJsr310LogicalTypes.Builder();
+    } else {
+      return new TestRecordWithJsr310LogicalTypes.Builder(other);
+    }
   }
 
   /**
-   * RecordBuilder for TestRecordWithLogicalTypes instances.
+   * RecordBuilder for TestRecordWithJsr310LogicalTypes instances.
    */
-  public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase<TestRecordWithLogicalTypes>
-    implements org.apache.avro.data.RecordBuilder<TestRecordWithLogicalTypes> {
+  public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase<TestRecordWithJsr310LogicalTypes>
+    implements org.apache.avro.data.RecordBuilder<TestRecordWithJsr310LogicalTypes> {
 
     private boolean b;
     private int i32;
@@ -315,64 +361,70 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
     private float f32;
     private double f64;
     private java.lang.CharSequence s;
-    private org.joda.time.LocalDate d;
-    private org.joda.time.LocalTime t;
-    private org.joda.time.DateTime ts;
-    private BigDecimal dec;
+    private java.time.LocalDate d;
+    private java.time.LocalTime t;
+    private java.time.Instant ts;
+    private java.math.BigDecimal dec;
 
     /** Creates a new Builder */
     private Builder() {
-      super(TestRecordWithLogicalTypes.SCHEMA$);
+      super(SCHEMA$);
     }
 
-    /** Creates a Builder by copying an existing Builder */
-    private Builder(TestRecordWithLogicalTypes.Builder other) {
+    /**
+     * Creates a Builder by copying an existing Builder.
+     * @param other The existing Builder to copy.
+     */
+    private Builder(TestRecordWithJsr310LogicalTypes.Builder other) {
       super(other);
       if (isValidValue(fields()[0], other.b)) {
         this.b = data().deepCopy(fields()[0].schema(), other.b);
-        fieldSetFlags()[0] = true;
+        fieldSetFlags()[0] = other.fieldSetFlags()[0];
       }
       if (isValidValue(fields()[1], other.i32)) {
         this.i32 = data().deepCopy(fields()[1].schema(), other.i32);
-        fieldSetFlags()[1] = true;
+        fieldSetFlags()[1] = other.fieldSetFlags()[1];
       }
       if (isValidValue(fields()[2], other.i64)) {
         this.i64 = data().deepCopy(fields()[2].schema(), other.i64);
-        fieldSetFlags()[2] = true;
+        fieldSetFlags()[2] = other.fieldSetFlags()[2];
       }
       if (isValidValue(fields()[3], other.f32)) {
         this.f32 = data().deepCopy(fields()[3].schema(), other.f32);
-        fieldSetFlags()[3] = true;
+        fieldSetFlags()[3] = other.fieldSetFlags()[3];
       }
       if (isValidValue(fields()[4], other.f64)) {
         this.f64 = data().deepCopy(fields()[4].schema(), other.f64);
-        fieldSetFlags()[4] = true;
+        fieldSetFlags()[4] = other.fieldSetFlags()[4];
       }
       if (isValidValue(fields()[5], other.s)) {
         this.s = data().deepCopy(fields()[5].schema(), other.s);
-        fieldSetFlags()[5] = true;
+        fieldSetFlags()[5] = other.fieldSetFlags()[5];
       }
       if (isValidValue(fields()[6], other.d)) {
         this.d = data().deepCopy(fields()[6].schema(), other.d);
-        fieldSetFlags()[6] = true;
+        fieldSetFlags()[6] = other.fieldSetFlags()[6];
       }
       if (isValidValue(fields()[7], other.t)) {
         this.t = data().deepCopy(fields()[7].schema(), other.t);
-        fieldSetFlags()[7] = true;
+        fieldSetFlags()[7] = other.fieldSetFlags()[7];
       }
       if (isValidValue(fields()[8], other.ts)) {
         this.ts = data().deepCopy(fields()[8].schema(), other.ts);
-        fieldSetFlags()[8] = true;
+        fieldSetFlags()[8] = other.fieldSetFlags()[8];
       }
       if (isValidValue(fields()[9], other.dec)) {
         this.dec = data().deepCopy(fields()[9].schema(), other.dec);
-        fieldSetFlags()[9] = true;
+        fieldSetFlags()[9] = other.fieldSetFlags()[9];
       }
     }
 
-    /** Creates a Builder by copying an existing TestRecordWithLogicalTypes instance */
-    private Builder(TestRecordWithLogicalTypes other) {
-            super(TestRecordWithLogicalTypes.SCHEMA$);
+    /**
+     * Creates a Builder by copying an existing TestRecordWithJsr310LogicalTypes instance
+     * @param other The existing instance to copy.
+     */
+    private Builder(TestRecordWithJsr310LogicalTypes other) {
+      super(SCHEMA$);
       if (isValidValue(fields()[0], other.b)) {
         this.b = data().deepCopy(fields()[0].schema(), other.b);
         fieldSetFlags()[0] = true;
@@ -417,6 +469,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Gets the value of the 'b' field.
+      * @return The value.
       */
     public java.lang.Boolean getB() {
       return b;
@@ -424,9 +477,10 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Sets the value of the 'b' field.
-      * @param value the value to set.
+      * @param value The value of 'b'.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder setB(boolean value) {
+    public TestRecordWithJsr310LogicalTypes.Builder setB(boolean value) {
       validate(fields()[0], value);
       this.b = value;
       fieldSetFlags()[0] = true;
@@ -435,6 +489,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Checks whether the 'b' field has been set.
+      * @return True if the 'b' field has been set, false otherwise.
       */
     public boolean hasB() {
       return fieldSetFlags()[0];
@@ -443,14 +498,16 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Clears the value of the 'b' field.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder clearB() {
+    public TestRecordWithJsr310LogicalTypes.Builder clearB() {
       fieldSetFlags()[0] = false;
       return this;
     }
 
     /**
       * Gets the value of the 'i32' field.
+      * @return The value.
       */
     public java.lang.Integer getI32() {
       return i32;
@@ -458,9 +515,10 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Sets the value of the 'i32' field.
-      * @param value the value to set.
+      * @param value The value of 'i32'.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder setI32(int value) {
+    public TestRecordWithJsr310LogicalTypes.Builder setI32(int value) {
       validate(fields()[1], value);
       this.i32 = value;
       fieldSetFlags()[1] = true;
@@ -469,6 +527,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Checks whether the 'i32' field has been set.
+      * @return True if the 'i32' field has been set, false otherwise.
       */
     public boolean hasI32() {
       return fieldSetFlags()[1];
@@ -477,14 +536,16 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Clears the value of the 'i32' field.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder clearI32() {
+    public TestRecordWithJsr310LogicalTypes.Builder clearI32() {
       fieldSetFlags()[1] = false;
       return this;
     }
 
     /**
       * Gets the value of the 'i64' field.
+      * @return The value.
       */
     public java.lang.Long getI64() {
       return i64;
@@ -492,9 +553,10 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Sets the value of the 'i64' field.
-      * @param value the value to set.
+      * @param value The value of 'i64'.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder setI64(long value) {
+    public TestRecordWithJsr310LogicalTypes.Builder setI64(long value) {
       validate(fields()[2], value);
       this.i64 = value;
       fieldSetFlags()[2] = true;
@@ -503,6 +565,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Checks whether the 'i64' field has been set.
+      * @return True if the 'i64' field has been set, false otherwise.
       */
     public boolean hasI64() {
       return fieldSetFlags()[2];
@@ -511,14 +574,16 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Clears the value of the 'i64' field.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder clearI64() {
+    public TestRecordWithJsr310LogicalTypes.Builder clearI64() {
       fieldSetFlags()[2] = false;
       return this;
     }
 
     /**
       * Gets the value of the 'f32' field.
+      * @return The value.
       */
     public java.lang.Float getF32() {
       return f32;
@@ -526,9 +591,10 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Sets the value of the 'f32' field.
-      * @param value the value to set.
+      * @param value The value of 'f32'.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder setF32(float value) {
+    public TestRecordWithJsr310LogicalTypes.Builder setF32(float value) {
       validate(fields()[3], value);
       this.f32 = value;
       fieldSetFlags()[3] = true;
@@ -537,6 +603,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Checks whether the 'f32' field has been set.
+      * @return True if the 'f32' field has been set, false otherwise.
       */
     public boolean hasF32() {
       return fieldSetFlags()[3];
@@ -545,14 +612,16 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Clears the value of the 'f32' field.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder clearF32() {
+    public TestRecordWithJsr310LogicalTypes.Builder clearF32() {
       fieldSetFlags()[3] = false;
       return this;
     }
 
     /**
       * Gets the value of the 'f64' field.
+      * @return The value.
       */
     public java.lang.Double getF64() {
       return f64;
@@ -560,9 +629,10 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Sets the value of the 'f64' field.
-      * @param value the value to set.
+      * @param value The value of 'f64'.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder setF64(double value) {
+    public TestRecordWithJsr310LogicalTypes.Builder setF64(double value) {
       validate(fields()[4], value);
       this.f64 = value;
       fieldSetFlags()[4] = true;
@@ -571,6 +641,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Checks whether the 'f64' field has been set.
+      * @return True if the 'f64' field has been set, false otherwise.
       */
     public boolean hasF64() {
       return fieldSetFlags()[4];
@@ -579,14 +650,16 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Clears the value of the 'f64' field.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder clearF64() {
+    public TestRecordWithJsr310LogicalTypes.Builder clearF64() {
       fieldSetFlags()[4] = false;
       return this;
     }
 
     /**
       * Gets the value of the 's' field.
+      * @return The value.
       */
     public java.lang.CharSequence getS() {
       return s;
@@ -594,9 +667,10 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Sets the value of the 's' field.
-      * @param value the value to set.
+      * @param value The value of 's'.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder setS(java.lang.CharSequence value) {
+    public TestRecordWithJsr310LogicalTypes.Builder setS(java.lang.CharSequence value) {
       validate(fields()[5], value);
       this.s = value;
       fieldSetFlags()[5] = true;
@@ -605,6 +679,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Checks whether the 's' field has been set.
+      * @return True if the 's' field has been set, false otherwise.
       */
     public boolean hasS() {
       return fieldSetFlags()[5];
@@ -613,8 +688,9 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Clears the value of the 's' field.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder clearS() {
+    public TestRecordWithJsr310LogicalTypes.Builder clearS() {
       s = null;
       fieldSetFlags()[5] = false;
       return this;
@@ -622,16 +698,18 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Gets the value of the 'd' field.
+      * @return The value.
       */
-    public org.joda.time.LocalDate getD() {
+    public java.time.LocalDate getD() {
       return d;
     }
 
     /**
       * Sets the value of the 'd' field.
-      * @param value the value to set.
+      * @param value The value of 'd'.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder setD(org.joda.time.LocalDate value) {
+    public TestRecordWithJsr310LogicalTypes.Builder setD(java.time.LocalDate value) {
       validate(fields()[6], value);
       this.d = value;
       fieldSetFlags()[6] = true;
@@ -640,6 +718,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Checks whether the 'd' field has been set.
+      * @return True if the 'd' field has been set, false otherwise.
       */
     public boolean hasD() {
       return fieldSetFlags()[6];
@@ -648,24 +727,27 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Clears the value of the 'd' field.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder clearD() {
+    public TestRecordWithJsr310LogicalTypes.Builder clearD() {
       fieldSetFlags()[6] = false;
       return this;
     }
 
     /**
       * Gets the value of the 't' field.
+      * @return The value.
       */
-    public org.joda.time.LocalTime getT() {
+    public java.time.LocalTime getT() {
       return t;
     }
 
     /**
       * Sets the value of the 't' field.
-      * @param value the value to set.
+      * @param value The value of 't'.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder setT(org.joda.time.LocalTime value) {
+    public TestRecordWithJsr310LogicalTypes.Builder setT(java.time.LocalTime value) {
       validate(fields()[7], value);
       this.t = value;
       fieldSetFlags()[7] = true;
@@ -674,6 +756,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Checks whether the 't' field has been set.
+      * @return True if the 't' field has been set, false otherwise.
       */
     public boolean hasT() {
       return fieldSetFlags()[7];
@@ -682,24 +765,27 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Clears the value of the 't' field.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder clearT() {
+    public TestRecordWithJsr310LogicalTypes.Builder clearT() {
       fieldSetFlags()[7] = false;
       return this;
     }
 
     /**
       * Gets the value of the 'ts' field.
+      * @return The value.
       */
-    public org.joda.time.DateTime getTs() {
+    public java.time.Instant getTs() {
       return ts;
     }
 
     /**
       * Sets the value of the 'ts' field.
-      * @param value the value to set.
+      * @param value The value of 'ts'.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder setTs(org.joda.time.DateTime value) {
+    public TestRecordWithJsr310LogicalTypes.Builder setTs(java.time.Instant value) {
       validate(fields()[8], value);
       this.ts = value;
       fieldSetFlags()[8] = true;
@@ -708,6 +794,7 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Checks whether the 'ts' field has been set.
+      * @return True if the 'ts' field has been set, false otherwise.
       */
     public boolean hasTs() {
       return fieldSetFlags()[8];
@@ -716,81 +803,90 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
       * Clears the value of the 'ts' field.
+      * @return This builder.
       */
-    public TestRecordWithLogicalTypes.Builder clearTs() {
+    public TestRecordWithJsr310LogicalTypes.Builder clearTs() {
       fieldSetFlags()[8] = false;
       return this;
     }
 
     /**
-     * Gedec the value of the 'dec' field.
-     */
-    public BigDecimal getDec() {
+      * Gets the value of the 'dec' field.
+      * @return The value.
+      */
+    public java.math.BigDecimal getDec() {
       return dec;
     }
 
     /**
-     * Sedec the value of the 'dec' field.
-     * @param value the value to set.
-     */
-    public TestRecordWithLogicalTypes.Builder setDec(BigDecimal value) {
-      validate(fields()[8], value);
+      * Sets the value of the 'dec' field.
+      * @param value The value of 'dec'.
+      * @return This builder.
+      */
+    public TestRecordWithJsr310LogicalTypes.Builder setDec(java.math.BigDecimal value) {
+      validate(fields()[9], value);
       this.dec = value;
-      fieldSetFlags()[8] = true;
+      fieldSetFlags()[9] = true;
       return this;
     }
 
     /**
-     * Checks whether the 'dec' field has been set.
-     */
+      * Checks whether the 'dec' field has been set.
+      * @return True if the 'dec' field has been set, false otherwise.
+      */
     public boolean hasDec() {
-      return fieldSetFlags()[8];
+      return fieldSetFlags()[9];
     }
 
 
     /**
-     * Clears the value of the 'dec' field.
-     */
-    public TestRecordWithLogicalTypes.Builder clearDec() {
-      fieldSetFlags()[8] = false;
+      * Clears the value of the 'dec' field.
+      * @return This builder.
+      */
+    public TestRecordWithJsr310LogicalTypes.Builder clearDec() {
+      dec = null;
+      fieldSetFlags()[9] = false;
       return this;
     }
 
     @Override
-    public TestRecordWithLogicalTypes build() {
+    @SuppressWarnings("unchecked")
+    public TestRecordWithJsr310LogicalTypes build() {
       try {
-        TestRecordWithLogicalTypes record = new TestRecordWithLogicalTypes();
-        record.b = fieldSetFlags()[0] ? this.b : (java.lang.Boolean) defaultValue(fields()[0]);
-        record.i32 = fieldSetFlags()[1] ? this.i32 : (java.lang.Integer) defaultValue(fields()[1]);
-        record.i64 = fieldSetFlags()[2] ? this.i64 : (java.lang.Long) defaultValue(fields()[2]);
-        record.f32 = fieldSetFlags()[3] ? this.f32 : (java.lang.Float) defaultValue(fields()[3]);
-        record.f64 = fieldSetFlags()[4] ? this.f64 : (java.lang.Double) defaultValue(fields()[4]);
-        record.s = fieldSetFlags()[5] ? this.s : (java.lang.CharSequence) defaultValue(fields()[5]);
-        record.d = fieldSetFlags()[6] ? this.d : (org.joda.time.LocalDate) defaultValue(fields()[6]);
-        record.t = fieldSetFlags()[7] ? this.t : (org.joda.time.LocalTime) defaultValue(fields()[7]);
-        record.ts = fieldSetFlags()[8] ? this.ts : (org.joda.time.DateTime) defaultValue(fields()[8]);
-        record.dec = fieldSetFlags()[9] ? this.dec : (BigDecimal) defaultValue(fields()[9]);
+        TestRecordWithJsr310LogicalTypes record = new TestRecordWithJsr310LogicalTypes();
+        record.b = fieldSetFlags()[0] ? this.b : (java.lang.Boolean) defaultValue(fields()[0], record.getConversion(0));
+        record.i32 = fieldSetFlags()[1] ? this.i32 : (java.lang.Integer) defaultValue(fields()[1], record.getConversion(1));
+        record.i64 = fieldSetFlags()[2] ? this.i64 : (java.lang.Long) defaultValue(fields()[2], record.getConversion(2));
+        record.f32 = fieldSetFlags()[3] ? this.f32 : (java.lang.Float) defaultValue(fields()[3], record.getConversion(3));
+        record.f64 = fieldSetFlags()[4] ? this.f64 : (java.lang.Double) defaultValue(fields()[4], record.getConversion(4));
+        record.s = fieldSetFlags()[5] ? this.s : (java.lang.CharSequence) defaultValue(fields()[5], record.getConversion(5));
+        record.d = fieldSetFlags()[6] ? this.d : (java.time.LocalDate) defaultValue(fields()[6], record.getConversion(6));
+        record.t = fieldSetFlags()[7] ? this.t : (java.time.LocalTime) defaultValue(fields()[7], record.getConversion(7));
+        record.ts = fieldSetFlags()[8] ? this.ts : (java.time.Instant) defaultValue(fields()[8], record.getConversion(8));
+        record.dec = fieldSetFlags()[9] ? this.dec : (java.math.BigDecimal) defaultValue(fields()[9], record.getConversion(9));
         return record;
-      } catch (Exception e) {
+      } catch (java.lang.Exception e) {
         throw new org.apache.avro.AvroRuntimeException(e);
       }
     }
   }
 
-  private static final org.apache.avro.io.DatumWriter
-      WRITER$ = new org.apache.avro.specific.SpecificDatumWriter(SCHEMA$);
+  @SuppressWarnings("unchecked")
+  private static final org.apache.avro.io.DatumWriter<TestRecordWithJsr310LogicalTypes>
+    WRITER$ = (org.apache.avro.io.DatumWriter<TestRecordWithJsr310LogicalTypes>)MODEL$.createDatumWriter(SCHEMA$);
 
   @Override public void writeExternal(java.io.ObjectOutput out)
     throws java.io.IOException {
-    WRITER$.write(this, org.apache.avro.specific.SpecificData.getEncoder(out));
+    WRITER$.write(this, SpecificData.getEncoder(out));
   }
 
-  private static final org.apache.avro.io.DatumReader
-      READER$ = new org.apache.avro.specific.SpecificDatumReader(SCHEMA$);
+  @SuppressWarnings("unchecked")
+  private static final org.apache.avro.io.DatumReader<TestRecordWithJsr310LogicalTypes>
+    READER$ = (org.apache.avro.io.DatumReader<TestRecordWithJsr310LogicalTypes>)MODEL$.createDatumReader(SCHEMA$);
 
   @Override public void readExternal(java.io.ObjectInput in)
     throws java.io.IOException {
-    READER$.read(this, org.apache.avro.specific.SpecificData.getDecoder(in));
+    READER$.read(this, SpecificData.getDecoder(in));
   }
 
 }
diff --git a/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java b/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java
index 493a8e6..17e74ae 100644
--- a/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java
+++ b/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java
@@ -5,6 +5,7 @@
  */
 package org.apache.avro.specific;
 
+import org.apache.avro.data.TimeConversions;
 import org.apache.avro.message.BinaryMessageDecoder;
 import org.apache.avro.message.BinaryMessageEncoder;
 
@@ -264,9 +265,9 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
     this.ts = value;
   }
 
-  protected static final org.apache.avro.data.TimeConversions.DateConversion DATE_CONVERSION = new org.apache.avro.data.TimeConversions.DateConversion();
-  protected static final org.apache.avro.data.TimeConversions.TimeConversion TIME_CONVERSION = new org.apache.avro.data.TimeConversions.TimeConversion();
-  protected static final org.apache.avro.data.TimeConversions.TimestampConversion TIMESTAMP_CONVERSION = new org.apache.avro.data.TimeConversions.TimestampConversion();
+  protected static final TimeConversions.DateConversion DATE_CONVERSION = new TimeConversions.DateConversion();
+  protected static final TimeConversions.TimeConversion TIME_CONVERSION = new TimeConversions.TimeConversion();
+  protected static final TimeConversions.TimestampConversion TIMESTAMP_CONVERSION = new TimeConversions.TimestampConversion();
   protected static final org.apache.avro.Conversions.DecimalConversion DECIMAL_CONVERSION = new org.apache.avro.Conversions.DecimalConversion();
   private final org.apache.avro.Conversion<?>[] conversions =
       new org.apache.avro.Conversion<?>[] {
diff --git a/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java b/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java
index 933cd81..a07dec5 100644
--- a/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java
+++ b/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java
@@ -32,6 +32,7 @@ import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.joda.time.LocalTime;
+import org.joda.time.format.ISODateTimeFormat;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
@@ -42,6 +43,14 @@ import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 
+import static java.time.format.DateTimeFormatter.ISO_INSTANT;
+import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
+import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME;
+import static org.hamcrest.Matchers.comparesEqualTo;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+
 /**
  * This tests compatibility between classes generated before and after
  * AVRO-1684. TestRecordWithoutLogicalTypes and TestRecordWithLogicalTypes were
@@ -53,6 +62,10 @@ import java.util.List;
  * Avro with existing Avro-generated sources. When using classes generated
  * before AVRO-1684, logical types should not be applied by the read or write
  * paths. Those files should behave as they did before.
+ *
+ * For AVRO-2079 {@link TestRecordWithJsr310LogicalTypes} was generated from
+ * the same schema and tests were added to test compatibility between the
+ * two versions.
  */
 public class TestSpecificLogicalTypes {
 
@@ -80,6 +93,97 @@ public class TestSpecificLogicalTypes {
 
     Assert.assertEquals("Should match written record", record, actual.get(0));
   }
+  @Test
+  public void testRecordWithJsr310LogicalTypes() throws IOException {
+    TestRecordWithJsr310LogicalTypes record = new TestRecordWithJsr310LogicalTypes(
+        true,
+        34,
+        35L,
+        3.14F,
+        3019.34,
+        null,
+        java.time.LocalDate.now(),
+        java.time.LocalTime.now(),
+        java.time.Instant.now(),
+        new BigDecimal(123.45f).setScale(2, BigDecimal.ROUND_HALF_DOWN)
+    );
+
+    File data = write(TestRecordWithJsr310LogicalTypes.getClassSchema(), record);
+    List<TestRecordWithJsr310LogicalTypes> actual = read(
+        TestRecordWithJsr310LogicalTypes.getClassSchema(), data);
+
+    Assert.assertEquals("Should match written record", record, actual.get(0));
+  }
+
+  @Test
+  public void testAbilityToReadJsr310RecordWrittenAsJodaRecord() throws IOException {
+    TestRecordWithLogicalTypes withJoda = new TestRecordWithLogicalTypes(
+            true,
+            34,
+            35L,
+            3.14F,
+            3019.34,
+            null,
+            LocalDate.now(),
+            LocalTime.now(),
+            DateTime.now().withZone(DateTimeZone.UTC),
+            new BigDecimal(123.45f).setScale(2, BigDecimal.ROUND_HALF_DOWN)
+    );
+
+    File data = write(TestRecordWithLogicalTypes.getClassSchema(), withJoda);
+    List<TestRecordWithJsr310LogicalTypes> actual = read(
+        TestRecordWithJsr310LogicalTypes.getClassSchema(), data);
+
+    Assert.assertThat(actual, is(not(empty())));
+    TestRecordWithJsr310LogicalTypes withJsr310 = actual.get(0);
+
+    Assert.assertThat(withJsr310.getB(), is(withJoda.getB()));
+    Assert.assertThat(withJsr310.getI32(), is(withJoda.getI32()));
+    Assert.assertThat(withJsr310.getI64(), is(withJoda.getI64()));
+    Assert.assertThat(withJsr310.getF32(), is(withJoda.getF32()));
+    Assert.assertThat(withJsr310.getF64(), is(withJoda.getF64()));
+    Assert.assertThat(withJsr310.getS(), is(withJoda.getS()));
+
+    Assert.assertThat(ISO_LOCAL_DATE.format(withJsr310.getD()), is(ISODateTimeFormat.date().print(withJoda.getD())));
+    Assert.assertThat(ISO_LOCAL_TIME.format(withJsr310.getT()), is(ISODateTimeFormat.time().print(withJoda.getT())));
+    Assert.assertThat(ISO_INSTANT.format(withJsr310.getTs()), is(ISODateTimeFormat.dateTime().print(withJoda.getTs())));
+    Assert.assertThat(withJsr310.getDec(), comparesEqualTo(withJoda.getDec()));
+  }
+
+  @Test
+  public void testAbilityToReadJodaRecordWrittenAsJsr310Record() throws IOException {
+    TestRecordWithJsr310LogicalTypes withJsr310 = new TestRecordWithJsr310LogicalTypes(
+            true,
+            34,
+            35L,
+            3.14F,
+            3019.34,
+            null,
+            java.time.LocalDate.now(),
+            java.time.LocalTime.now(),
+            java.time.Instant.now(),
+            new BigDecimal(123.45f).setScale(2, BigDecimal.ROUND_HALF_DOWN)
+    );
+
+    File data = write(TestRecordWithJsr310LogicalTypes.getClassSchema(), withJsr310);
+    List<TestRecordWithLogicalTypes> actual = read(
+        TestRecordWithLogicalTypes.getClassSchema(), data);
+
+    Assert.assertThat(actual, is(not(empty())));
+    TestRecordWithLogicalTypes withJoda = actual.get(0);
+
+    Assert.assertThat(withJoda.getB(), is(withJsr310.getB()));
+    Assert.assertThat(withJoda.getI32(), is(withJsr310.getI32()));
+    Assert.assertThat(withJoda.getI64(), is(withJsr310.getI64()));
+    Assert.assertThat(withJoda.getF32(), is(withJsr310.getF32()));
+    Assert.assertThat(withJoda.getF64(), is(withJsr310.getF64()));
+    Assert.assertThat(withJoda.getS(), is(withJsr310.getS()));
+    // all of these print in the ISO-8601 format
+    Assert.assertThat(withJoda.getD().toString(), is(withJsr310.getD().toString()));
+    Assert.assertThat(withJoda.getT().toString(), is(withJsr310.getT().toString()));
+    Assert.assertThat(withJoda.getTs().toString(), is(withJsr310.getTs().toString()));
+    Assert.assertThat(withJoda.getDec(), comparesEqualTo(withJsr310.getDec()));
+  }
 
   @Test
   public void testRecordWithoutLogicalTypes() throws IOException {
@@ -198,17 +302,11 @@ public class TestSpecificLogicalTypes {
       throws IOException {
     DatumReader<D> reader = newReader(schema);
     List<D> data = new ArrayList<>();
-    FileReader<D> fileReader = null;
 
-    try {
-      fileReader = new DataFileReader<>(file, reader);
+    try (FileReader<D> fileReader = new DataFileReader<>(file, reader)) {
       for (D datum : fileReader) {
         data.add(datum);
       }
-    } finally {
-      if (fileReader != null) {
-        fileReader.close();
-      }
     }
 
     return data;
@@ -224,15 +322,12 @@ public class TestSpecificLogicalTypes {
       throws IOException {
     File file = temp.newFile();
     DatumWriter<D> writer = SpecificData.get().createDatumWriter(schema);
-    DataFileWriter<D> fileWriter = new DataFileWriter<>(writer);
 
-    try {
+    try (DataFileWriter<D> fileWriter = new DataFileWriter<>(writer)) {
       fileWriter.create(schema, file);
       for (D datum : data) {
         fileWriter.append(datum);
       }
-    } finally {
-      fileWriter.close();
     }
 
     return file;
diff --git a/lang/java/avro/src/test/resources/record_with_logical_types.avsc b/lang/java/avro/src/test/resources/record_with_logical_types.avsc
index 9932f95..f5d2129 100644
--- a/lang/java/avro/src/test/resources/record_with_logical_types.avsc
+++ b/lang/java/avro/src/test/resources/record_with_logical_types.avsc
@@ -1,7 +1,7 @@
 {
   "type" : "record",
   "name" : "TestRecordWithLogicalTypes",
-  "doc" : "Schema for TestRecordWithLogicalTypes and TestRecordWithoutLogicalTypes, see TestSpecificLogicalTypes"
+  "doc" : "Schema for TestRecordWithLogicalTypes and TestRecordWithoutLogicalTypes, see TestSpecificLogicalTypes",
   "namespace" : "org.apache.avro.specific",
   "fields" : [ {
     "name" : "b",
diff --git a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/ProtocolTask.java b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/ProtocolTask.java
index b7f9438..02a1af0 100644
--- a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/ProtocolTask.java
+++ b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/ProtocolTask.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
 
 import org.apache.avro.AvroRuntimeException;
 import org.apache.avro.Protocol;
+import org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeImplementation;
 import org.apache.avro.generic.GenericData.StringType;
 
 import org.apache.tools.ant.BuildException;
@@ -36,6 +37,7 @@ public class ProtocolTask extends Task {
   private File src;
   private File dest = new File(".");
   private StringType stringType = StringType.CharSequence;
+  private DateTimeLogicalTypeImplementation dateTimeLogicalTypeImplementation = DateTimeLogicalTypeImplementation.JODA;
 
   private final ArrayList<FileSet> filesets = new ArrayList<>();
 
@@ -51,6 +53,16 @@ public class ProtocolTask extends Task {
   /** Get the string type. */
   public StringType getStringType() { return this.stringType; }
 
+  /** Sets the date/time logical type type (either JODA or JSR310) */
+  public void setDateTimeLogicalTypeImplementation(DateTimeLogicalTypeImplementation dateTimeLogicalTypeImplementation) {
+    this.dateTimeLogicalTypeImplementation = dateTimeLogicalTypeImplementation;
+  }
+
+  /** Get the date/time logical type type (either JODA or JSR310) */
+  public DateTimeLogicalTypeImplementation getDateTimeLogicalTypeImplementation() {
+    return dateTimeLogicalTypeImplementation;
+  }
+
   /** Add a fileset. */
   public void addFileset(FileSet set) { filesets.add(set); }
 
@@ -77,7 +89,7 @@ public class ProtocolTask extends Task {
 
   protected void doCompile(File src, File dir) throws IOException {
     Protocol protocol = Protocol.parse(src);
-    SpecificCompiler compiler = new SpecificCompiler(protocol);
+    SpecificCompiler compiler = new SpecificCompiler(protocol, getDateTimeLogicalTypeImplementation());
     compiler.setStringType(getStringType());
     compiler.compileToDestination(src, dest);
   }
diff --git a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SchemaTask.java b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SchemaTask.java
index 306aff0..9d2c244 100644
--- a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SchemaTask.java
+++ b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SchemaTask.java
@@ -28,7 +28,7 @@ public class SchemaTask extends ProtocolTask {
   protected void doCompile(File src, File dest) throws IOException {
     Schema.Parser parser = new Schema.Parser();
     Schema schema = parser.parse(src);
-    SpecificCompiler compiler = new SpecificCompiler(schema);
+    SpecificCompiler compiler = new SpecificCompiler(schema, getDateTimeLogicalTypeImplementation());
     compiler.setStringType(getStringType());
     compiler.compileToDestination(src, dest);
   }
diff --git a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java
index d2fe6cc..575462e 100644
--- a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java
+++ b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java
@@ -37,9 +37,8 @@ import java.util.Set;
 import org.apache.avro.Conversion;
 import org.apache.avro.Conversions;
 import org.apache.avro.LogicalTypes;
-import org.apache.avro.data.TimeConversions.DateConversion;
-import org.apache.avro.data.TimeConversions.TimeConversion;
-import org.apache.avro.data.TimeConversions.TimestampConversion;
+import org.apache.avro.data.Jsr310TimeConversions;
+import org.apache.avro.data.TimeConversions;
 import org.apache.avro.specific.SpecificData;
 import org.codehaus.jackson.JsonNode;
 
@@ -88,18 +87,35 @@ public class SpecificCompiler {
    */
   protected static final int MAX_FIELD_PARAMETER_UNIT_COUNT = JVM_METHOD_ARG_LIMIT - 1;
 
-  public static enum FieldVisibility {
+  public enum FieldVisibility {
     PUBLIC, PUBLIC_DEPRECATED, PRIVATE
   }
 
-  private static final SpecificData SPECIFIC = new SpecificData();
-  static {
-    SPECIFIC.addLogicalTypeConversion(new DateConversion());
-    SPECIFIC.addLogicalTypeConversion(new TimeConversion());
-    SPECIFIC.addLogicalTypeConversion(new TimestampConversion());
-    SPECIFIC.addLogicalTypeConversion(new Conversions.DecimalConversion());
+  public enum DateTimeLogicalTypeImplementation {
+    JODA {
+      @Override
+      void addLogicalTypeConversions(SpecificData specificData) {
+        specificData.addLogicalTypeConversion(new TimeConversions.DateConversion());
+        specificData.addLogicalTypeConversion(new TimeConversions.TimeConversion());
+        specificData.addLogicalTypeConversion(new TimeConversions.TimestampConversion());
+      }
+    },
+    JSR310 {
+      @Override
+      void addLogicalTypeConversions(SpecificData specificData) {
+        specificData.addLogicalTypeConversion(new Jsr310TimeConversions.DateConversion());
+        specificData.addLogicalTypeConversion(new Jsr310TimeConversions.TimeMillisConversion());
+        specificData.addLogicalTypeConversion(new Jsr310TimeConversions.TimestampMillisConversion());
+      }
+    };
+
+    public static final DateTimeLogicalTypeImplementation DEFAULT = JODA;
+
+    abstract void addLogicalTypeConversions(SpecificData specificData);
   }
 
+  private final SpecificData specificData = new SpecificData();
+
   private final Set<Schema> queue = new HashSet<>();
   private Protocol protocol;
   private VelocityEngine velocityEngine;
@@ -109,6 +125,7 @@ public class SpecificCompiler {
   private boolean createAllArgsConstructor = true;
   private String outputCharacterEncoding;
   private boolean enableDecimalLogicalType = false;
+  private final DateTimeLogicalTypeImplementation dateTimeLogicalTypeImplementation;
   private String suffix = ".java";
 
   /*
@@ -144,7 +161,11 @@ public class SpecificCompiler {
       " */\n";
 
   public SpecificCompiler(Protocol protocol) {
-    this();
+    this(protocol, DateTimeLogicalTypeImplementation.JODA);
+  }
+
+  public SpecificCompiler(Protocol protocol, DateTimeLogicalTypeImplementation dateTimeLogicalTypeImplementation) {
+    this(dateTimeLogicalTypeImplementation);
     // enqueue all types
     for (Schema s : protocol.getTypes()) {
       enqueue(s);
@@ -153,16 +174,38 @@ public class SpecificCompiler {
   }
 
   public SpecificCompiler(Schema schema) {
-    this();
+    this(schema, DateTimeLogicalTypeImplementation.JODA);
+  }
+
+  public SpecificCompiler(Schema schema, DateTimeLogicalTypeImplementation dateTimeLogicalTypeImplementation) {
+    this(dateTimeLogicalTypeImplementation);
     enqueue(schema);
     this.protocol = null;
   }
 
+  /**
+   * Creates a specific compiler with the default (Joda) type for date/time related logical types.
+   *
+   * @see #SpecificCompiler(DateTimeLogicalTypeImplementation)
+   */
   SpecificCompiler() {
+    this(DateTimeLogicalTypeImplementation.JODA);
+  }
+
+  /**
+   * Creates a specific compiler with the given type to use for date/time related logical types.
+   * Use {@link DateTimeLogicalTypeImplementation#JODA} to generate Joda Time classes, use {@link DateTimeLogicalTypeImplementation#JSR310}
+   * to generate {@code java.time.*} classes for the date/time local types.
+   *
+   * @param dateTimeLogicalTypeImplementation the types used for date/time related logical types
+   */
+  SpecificCompiler(DateTimeLogicalTypeImplementation dateTimeLogicalTypeImplementation) {
+    this.dateTimeLogicalTypeImplementation = dateTimeLogicalTypeImplementation;
     this.templateDir =
       System.getProperty("org.apache.avro.specific.templates",
                          "/org/apache/avro/compiler/specific/templates/java/classic/");
     initializeVelocity();
+    initializeSpecificData();
   }
 
   /** Set the resource directory where templates reside. First, the compiler checks
@@ -225,6 +268,10 @@ public class SpecificCompiler {
     this.enableDecimalLogicalType = enableDecimalLogicalType;
   }
 
+  public DateTimeLogicalTypeImplementation getDateTimeLogicalTypeImplementation() {
+    return dateTimeLogicalTypeImplementation;
+  }
+
   private static String logChuteName = null;
 
   private void initializeVelocity() {
@@ -253,6 +300,11 @@ public class SpecificCompiler {
     velocityEngine.setProperty("runtime.log.logsystem.class", logChuteName);
   }
 
+  private void initializeSpecificData() {
+    dateTimeLogicalTypeImplementation.addLogicalTypeConversions(specificData);
+    specificData.addLogicalTypeConversion(new Conversions.DecimalConversion());
+  }
+
   /**
    * Captures output file path and contents.
    */
@@ -324,7 +376,7 @@ public class SpecificCompiler {
 
     for (File src : srcFiles) {
       Schema schema = parser.parse(src);
-      SpecificCompiler compiler = new SpecificCompiler(schema);
+      SpecificCompiler compiler = new SpecificCompiler(schema, DateTimeLogicalTypeImplementation.JODA);
       compiler.compileToDestination(src, dest);
     }
   }
@@ -630,7 +682,7 @@ public class SpecificCompiler {
   private String getConvertedLogicalType(Schema schema) {
     if (enableDecimalLogicalType
         || !(schema.getLogicalType() instanceof LogicalTypes.Decimal)) {
-      Conversion<?> conversion = SPECIFIC
+      Conversion<?> conversion = specificData
           .getConversionFor(schema.getLogicalType());
       if (conversion != null) {
         return conversion.getConvertedType().getName();
diff --git a/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm b/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm
index de7fd1c..045c7e1 100644
--- a/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm
+++ b/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm
@@ -161,10 +161,16 @@ public class ${this.mangle($schema.getName())}#if ($schema.isError()) extends or
   }
 
 #if ($this.hasLogicalTypeField($schema))
+  protected static final org.apache.avro.Conversions.DecimalConversion DECIMAL_CONVERSION = new org.apache.avro.Conversions.DecimalConversion();
+#if ($this.getDateTimeLogicalTypeImplementation().name() == "JODA")
   protected static final org.apache.avro.data.TimeConversions.DateConversion DATE_CONVERSION = new org.apache.avro.data.TimeConversions.DateConversion();
   protected static final org.apache.avro.data.TimeConversions.TimeConversion TIME_CONVERSION = new org.apache.avro.data.TimeConversions.TimeConversion();
   protected static final org.apache.avro.data.TimeConversions.TimestampConversion TIMESTAMP_CONVERSION = new org.apache.avro.data.TimeConversions.TimestampConversion();
-  protected static final org.apache.avro.Conversions.DecimalConversion DECIMAL_CONVERSION = new org.apache.avro.Conversions.DecimalConversion();
+#elseif ($this.getDateTimeLogicalTypeImplementation().name() == "JSR310")
+  protected static final org.apache.avro.data.Jsr310TimeConversions.DateConversion DATE_CONVERSION = new org.apache.avro.data.Jsr310TimeConversions.DateConversion();
+  protected static final org.apache.avro.data.Jsr310TimeConversions.TimeMillisConversion TIME_CONVERSION = new org.apache.avro.data.Jsr310TimeConversions.TimeMillisConversion();
+  protected static final org.apache.avro.data.Jsr310TimeConversions.TimestampMillisConversion TIMESTAMP_CONVERSION = new org.apache.avro.data.Jsr310TimeConversions.TimestampMillisConversion();
+#end
 
   private static final org.apache.avro.Conversion<?>[] conversions =
       new org.apache.avro.Conversion<?>[] {
diff --git a/lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java b/lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java
index 3b4e358..b5b3a0a 100644
--- a/lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java
+++ b/lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java
@@ -17,6 +17,8 @@
  */
 package org.apache.avro.compiler.specific;
 
+import static org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeImplementation.JODA;
+import static org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeImplementation.JSR310;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -142,9 +144,13 @@ public class TestSpecificCompiler {
   }
 
   private SpecificCompiler createCompiler() throws IOException {
+    return createCompiler(JODA);
+  }
+
+  private SpecificCompiler createCompiler(SpecificCompiler.DateTimeLogicalTypeImplementation dateTimeLogicalTypeImplementation) throws IOException {
     Schema.Parser parser = new Schema.Parser();
     Schema schema = parser.parse(this.src);
-    SpecificCompiler compiler = new SpecificCompiler(schema);
+    SpecificCompiler compiler = new SpecificCompiler(schema, dateTimeLogicalTypeImplementation);
     compiler.setTemplateDir(this.velocityTemplateDir);
     compiler.setStringType(StringType.CharSequence);
     return compiler;
@@ -408,6 +414,26 @@ public class TestSpecificCompiler {
   }
 
   @Test
+  public void testJavaTypeWithJsr310DateTimeTypes() throws Exception {
+    SpecificCompiler compiler = createCompiler(JSR310);
+
+    Schema dateSchema = LogicalTypes.date()
+        .addToSchema(Schema.create(Schema.Type.INT));
+    Schema timeSchema = LogicalTypes.timeMillis()
+        .addToSchema(Schema.create(Schema.Type.INT));
+    Schema timestampSchema = LogicalTypes.timestampMillis()
+        .addToSchema(Schema.create(Schema.Type.LONG));
+
+    // Date/time types should always use upper level java classes
+    Assert.assertEquals("Should use java.time.LocalDate for date type",
+        "java.time.LocalDate", compiler.javaType(dateSchema));
+    Assert.assertEquals("Should use java.time.LocalTime for time-millis type",
+        "java.time.LocalTime", compiler.javaType(timeSchema));
+    Assert.assertEquals("Should use java.time.Instant for timestamp-millis type",
+        "java.time.Instant", compiler.javaType(timestampSchema));
+  }
+
+  @Test
   public void testJavaUnbox() throws Exception {
     SpecificCompiler compiler = createCompiler();
     compiler.setEnableDecimalLogicalType(false);
@@ -442,7 +468,26 @@ public class TestSpecificCompiler {
         "org.joda.time.LocalTime", compiler.javaUnbox(timeSchema));
     Assert.assertEquals("Should use Joda DateTime for timestamp-millis type",
         "org.joda.time.DateTime", compiler.javaUnbox(timestampSchema));
+  }
 
+  @Test
+  public void testJavaUnboxJsr310DateTime() throws Exception {
+    SpecificCompiler compiler = createCompiler(JSR310);
+
+    Schema dateSchema = LogicalTypes.date()
+        .addToSchema(Schema.create(Schema.Type.INT));
+    Schema timeSchema = LogicalTypes.timeMillis()
+        .addToSchema(Schema.create(Schema.Type.INT));
+    Schema timestampSchema = LogicalTypes.timestampMillis()
+        .addToSchema(Schema.create(Schema.Type.LONG));
+    // Date/time types should always use upper level java classes, even though
+    // their underlying representations are primitive types
+    Assert.assertEquals("Should use java.time.LocalDate for date type",
+        "java.time.LocalDate", compiler.javaUnbox(dateSchema));
+    Assert.assertEquals("Should use java.time.LocalTime for time-millis type",
+        "java.time.LocalTime", compiler.javaUnbox(timeSchema));
+    Assert.assertEquals("Should use java.time.Instant for timestamp-millis type",
+        "java.time.Instant", compiler.javaUnbox(timestampSchema));
   }
 
   @Test
@@ -514,6 +559,14 @@ public class TestSpecificCompiler {
   }
 
   @Test
+  public void testLogicalTypesWithMultipleFieldsJsr310DateTime() throws Exception {
+    Schema logicalTypesWithMultipleFields = new Schema.Parser().parse(
+        new File("src/test/resources/logical_types_with_multiple_fields.avsc"));
+    assertCompilesWithJavaCompiler(
+        new SpecificCompiler(logicalTypesWithMultipleFields, JSR310).compile());
+  }
+
+  @Test
   public void testConversionInstanceWithDecimalLogicalTypeDisabled() throws Exception {
     SpecificCompiler compiler = createCompiler();
     compiler.setEnableDecimalLogicalType(false);
diff --git a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java
index eb51f76..3ddb062 100644
--- a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java
+++ b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java
@@ -23,7 +23,7 @@ import java.io.IOException;
 import java.util.Arrays;
 
 import org.apache.avro.compiler.specific.SpecificCompiler;
-
+import org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeImplementation;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.project.MavenProject;
@@ -130,6 +130,14 @@ public abstract class AbstractAvroMojo extends AbstractMojo {
   protected boolean enableDecimalLogicalType;
 
   /**
+   * Determines which type of classes to generate for date/time related logical types. Either 'joda' or 'jsr310'.
+   * Defaults to joda for backwards compatibility reasons.
+   *
+   * @parameter default-value="joda"
+   */
+  protected String dateTimeLogicalTypeImplementation = DateTimeLogicalTypeImplementation.JODA.name().toLowerCase();
+
+  /**
    * The current Maven project.
    *
    * @parameter default-value="${project}"
@@ -237,6 +245,22 @@ public abstract class AbstractAvroMojo extends AbstractMojo {
     }
   }
 
+  protected DateTimeLogicalTypeImplementation getDateTimeLogicalTypeImplementation() {
+    try {
+      if (this.dateTimeLogicalTypeImplementation == null || this.dateTimeLogicalTypeImplementation.isEmpty()) {
+        return DateTimeLogicalTypeImplementation.DEFAULT;
+      } else {
+        String upper = String.valueOf(this.dateTimeLogicalTypeImplementation).trim().toUpperCase();
+        return DateTimeLogicalTypeImplementation.valueOf(upper);
+      }
+    } catch (IllegalArgumentException e) {
+      getLog().warn("Unknown value '" + this.dateTimeLogicalTypeImplementation
+        + "' for property dateTimeLogicalTypeImplementation; using '"
+        + DateTimeLogicalTypeImplementation.DEFAULT.name().toLowerCase() + "' instead");
+      return DateTimeLogicalTypeImplementation.DEFAULT;
+    }
+  }
+
   protected abstract void doCompile(String filename, File sourceDirectory, File outputDirectory) throws IOException;
 
   protected abstract String[] getIncludes();
diff --git a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/IDLProtocolMojo.java b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/IDLProtocolMojo.java
index 3c9542e..3ee7fcb 100644
--- a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/IDLProtocolMojo.java
+++ b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/IDLProtocolMojo.java
@@ -88,7 +88,7 @@ public class IDLProtocolMojo extends AbstractAvroMojo {
       Protocol p = parser.CompilationUnit();
       String json = p.toString(true);
       Protocol protocol = Protocol.parse(json);
-      SpecificCompiler compiler = new SpecificCompiler(protocol);
+      SpecificCompiler compiler = new SpecificCompiler(protocol, getDateTimeLogicalTypeImplementation());
       compiler.setStringType(GenericData.StringType.valueOf(stringType));
       compiler.setTemplateDir(templateDirectory);
       compiler.setFieldVisibility(getFieldVisibility());
diff --git a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/ProtocolMojo.java b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/ProtocolMojo.java
index a4dbe65..25ee56b 100644
--- a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/ProtocolMojo.java
+++ b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/ProtocolMojo.java
@@ -56,7 +56,7 @@ public class ProtocolMojo extends AbstractAvroMojo {
   protected void doCompile(String filename, File sourceDirectory, File outputDirectory) throws IOException {
     File src = new File(sourceDirectory, filename);
     Protocol protocol = Protocol.parse(src);
-    SpecificCompiler compiler = new SpecificCompiler(protocol);
+    SpecificCompiler compiler = new SpecificCompiler(protocol, getDateTimeLogicalTypeImplementation());
     compiler.setTemplateDir(templateDirectory);
     compiler.setStringType(StringType.valueOf(stringType));
     compiler.setFieldVisibility(getFieldVisibility());
diff --git a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java
index 2d6671f..ab9d0eb 100644
--- a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java
+++ b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java
@@ -73,7 +73,7 @@ public class SchemaMojo extends AbstractAvroMojo {
       schema = schemaParser.parse(src);
     }
 
-    SpecificCompiler compiler = new SpecificCompiler(schema);
+    SpecificCompiler compiler = new SpecificCompiler(schema, getDateTimeLogicalTypeImplementation());
     compiler.setTemplateDir(templateDirectory);
     compiler.setStringType(StringType.valueOf(stringType));
     compiler.setFieldVisibility(getFieldVisibility());
diff --git a/lang/java/maven-plugin/src/test/avro/User.avdl b/lang/java/maven-plugin/src/test/avro/User.avdl
index 4d4fa5a..cb63ee5 100644
--- a/lang/java/maven-plugin/src/test/avro/User.avdl
+++ b/lang/java/maven-plugin/src/test/avro/User.avdl
@@ -25,6 +25,7 @@ protocol IdlTest {
     record IdlUser {
       union { null, string } id;
       union { null, long } createdOn;
+      timestamp_ms modifiedOn;
       union { null, IdlPrivacy } privacy;
     }
 
diff --git a/lang/java/maven-plugin/src/test/avro/User.avpr b/lang/java/maven-plugin/src/test/avro/User.avpr
index 9d8a109..6dd8b9b 100644
--- a/lang/java/maven-plugin/src/test/avro/User.avpr
+++ b/lang/java/maven-plugin/src/test/avro/User.avpr
@@ -27,6 +27,13 @@
           "name": "privacy", 
           "type": ["null", "ProtocolPrivacy"],
           "default": null
+        },
+        {
+          "name": "modifiedOn",
+          "type": {
+            "type": "long",
+            "logicalType": "timestamp-millis"
+          }
         }
       ]
     } 
diff --git a/lang/java/maven-plugin/src/test/avro/User.avsc b/lang/java/maven-plugin/src/test/avro/User.avsc
index 0eb5e09..a93e0d1 100644
--- a/lang/java/maven-plugin/src/test/avro/User.avsc
+++ b/lang/java/maven-plugin/src/test/avro/User.avsc
@@ -33,6 +33,13 @@
             "name": "privacyDirectImport", 
             "type": ["null", "test.PrivacyDirectImport"],
             "default": null
+        },
+        {
+            "name": "time",
+            "type": {
+                "type": "long",
+                "logicalType": "timestamp-millis"
+            }
         }
     ]
 }
diff --git a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/AbstractAvroMojoTest.java b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/AbstractAvroMojoTest.java
index 94a5870..3a75a68 100644
--- a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/AbstractAvroMojoTest.java
+++ b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/AbstractAvroMojoTest.java
@@ -20,6 +20,7 @@ package org.apache.avro.mojo;
 import java.io.File;
 import java.util.Arrays;
 import java.util.List;
+
 import org.apache.maven.plugin.testing.AbstractMojoTestCase;
 
 /**
@@ -27,7 +28,7 @@ import org.apache.maven.plugin.testing.AbstractMojoTestCase;
  *
  * @author saden
  */
-public class AbstractAvroMojoTest extends AbstractMojoTestCase {
+public abstract class AbstractAvroMojoTest extends AbstractMojoTestCase {
 
   @Override
   protected void setUp() throws Exception {
diff --git a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestIDLProtocolMojo.java b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestIDLProtocolMojo.java
index bdd4138..e686fda 100644
--- a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestIDLProtocolMojo.java
+++ b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestIDLProtocolMojo.java
@@ -17,6 +17,8 @@
  */
 package org.apache.avro.mojo;
 
+import org.codehaus.plexus.util.FileUtils;
+
 import java.io.File;
 
 /**
@@ -26,19 +28,36 @@ import java.io.File;
  */
 public class TestIDLProtocolMojo extends AbstractAvroMojoTest {
 
-  protected File testPom = new File(getBasedir(),
-          "src/test/resources/unit/idl/pom.xml");
+  protected File jodaTestPom = new File(getBasedir(),
+          "src/test/resources/unit/idl/pom-joda.xml");
+  protected File jsr310TestPom = new File(getBasedir(),
+          "src/test/resources/unit/idl/pom-jsr310.xml");
+
+  public void testIdlProtocolMojoJoda() throws Exception {
+    IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", jodaTestPom);
+
+    assertNotNull(mojo);
+    mojo.execute();
+
+    File outputDir = new File(getBasedir(), "target/test-harness/idl-joda/test");
+    String[] generatedFileNames = new String[]{"IdlPrivacy.java",
+      "IdlTest.java", "IdlUser.java", "IdlUserWrapper.java"};
+
+    String idlUserContent = FileUtils.fileRead(new File(outputDir, "IdlUser.java"));
+    assertTrue(idlUserContent.contains("org.joda.time.DateTime"));
+  }
 
-  public void testIdlProtocolMojo() throws Exception {
-    IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", testPom);
+  public void testIdlProtocolMojoJsr310() throws Exception {
+    IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", jsr310TestPom);
 
     assertNotNull(mojo);
     mojo.execute();
 
-    File outputDir = new File(getBasedir(), "target/test-harness/idl/test");
-    String[] generatedFiles = new String[]{"IdlPrivacy.java",
+    File outputDir = new File(getBasedir(), "target/test-harness/idl-jsr310/test");
+    String[] generatedFileNames = new String[]{"IdlPrivacy.java",
       "IdlTest.java", "IdlUser.java", "IdlUserWrapper.java"};
 
-    assertFilesExist(outputDir, generatedFiles);
+    String idlUserContent = FileUtils.fileRead(new File(outputDir, "IdlUser.java"));
+    assertTrue(idlUserContent.contains("java.time.Instant"));
   }
 }
diff --git a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestProtocolMojo.java b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestProtocolMojo.java
index 9618a9d..be7fcd2 100644
--- a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestProtocolMojo.java
+++ b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestProtocolMojo.java
@@ -17,6 +17,8 @@
  */
 package org.apache.avro.mojo;
 
+import org.codehaus.plexus.util.FileUtils;
+
 import java.io.File;
 
 /**
@@ -26,19 +28,40 @@ import java.io.File;
  */
 public class TestProtocolMojo extends AbstractAvroMojoTest {
 
-  protected File testPom = new File(getBasedir(),
-          "src/test/resources/unit/protocol/pom.xml");
+  protected File jodaTestPom = new File(getBasedir(),
+          "src/test/resources/unit/protocol/pom-joda.xml");
+  protected File jsr310TestPom = new File(getBasedir(),
+          "src/test/resources/unit/protocol/pom-jsr310.xml");
 
-  public void testProtocolMojo() throws Exception {
-    ProtocolMojo mojo = (ProtocolMojo) lookupMojo("protocol", testPom);
+  public void testProtocolMojoJoda() throws Exception {
+    ProtocolMojo mojo = (ProtocolMojo) lookupMojo("protocol", jodaTestPom);
 
     assertNotNull(mojo);
     mojo.execute();
 
-    File outputDir = new File(getBasedir(), "target/test-harness/protocol/test");
+    File outputDir = new File(getBasedir(), "target/test-harness/protocol-joda/test");
     String[] generatedFiles = new String[]{"ProtocolPrivacy.java",
       "ProtocolTest.java", "ProtocolUser.java"};
 
     assertFilesExist(outputDir, generatedFiles);
+
+    String protocolUserContent = FileUtils.fileRead(new File(outputDir, "ProtocolUser.java"));
+    assertTrue(protocolUserContent.contains("org.joda.time.DateTime"));
+  }
+
+  public void testProtocolMojoJsr310() throws Exception {
+    ProtocolMojo mojo = (ProtocolMojo) lookupMojo("protocol", jsr310TestPom);
+
+    assertNotNull(mojo);
+    mojo.execute();
+
+    File outputDir = new File(getBasedir(), "target/test-harness/protocol-jsr310/test");
+    String[] generatedFiles = new String[]{"ProtocolPrivacy.java",
+            "ProtocolTest.java", "ProtocolUser.java"};
+
+    assertFilesExist(outputDir, generatedFiles);
+
+    String protocolUserContent = FileUtils.fileRead(new File(outputDir, "ProtocolUser.java"));
+    assertTrue(protocolUserContent.contains("java.time.Instant"));
   }
 }
diff --git a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestSchemaMojo.java b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestSchemaMojo.java
index 08d5871..40ed5f6 100644
--- a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestSchemaMojo.java
+++ b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestSchemaMojo.java
@@ -17,6 +17,8 @@
  */
 package org.apache.avro.mojo;
 
+import org.codehaus.plexus.util.FileUtils;
+
 import java.io.File;
 
 /**
@@ -26,19 +28,40 @@ import java.io.File;
  */
 public class TestSchemaMojo extends AbstractAvroMojoTest {
 
-  protected File testPom = new File(getBasedir(),
-          "src/test/resources/unit/schema/pom.xml");
+  protected File jodaTestPom = new File(getBasedir(),
+          "src/test/resources/unit/schema/pom-joda.xml");
+  protected File jsr310TestPom = new File(getBasedir(),
+          "src/test/resources/unit/schema/pom-jsr310.xml");
 
-  public void testSchemaMojo() throws Exception {
-    SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", testPom);
+  public void testSchemaMojoJoda() throws Exception {
+    SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", jodaTestPom);
 
     assertNotNull(mojo);
     mojo.execute();
 
-    File outputDir = new File(getBasedir(), "target/test-harness/schema/test");
+    File outputDir = new File(getBasedir(), "target/test-harness/schema-joda/test");
     String[] generatedFiles = new String[]{"PrivacyDirectImport.java",
       "PrivacyImport.java", "SchemaPrivacy.java", "SchemaUser.java"};
 
     assertFilesExist(outputDir, generatedFiles);
+
+    String schemaUserContent = FileUtils.fileRead(new File(outputDir, "SchemaUser.java"));
+    assertTrue(schemaUserContent.contains("org.joda.time.DateTime"));
+  }
+
+  public void testSchemaMojoJsr310() throws Exception {
+    SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", jsr310TestPom);
+
+    assertNotNull(mojo);
+    mojo.execute();
+
+    File outputDir = new File(getBasedir(), "target/test-harness/schema-jsr310/test");
+    String[] generatedFiles = new String[]{"PrivacyDirectImport.java",
+            "PrivacyImport.java", "SchemaPrivacy.java", "SchemaUser.java"};
+
+    assertFilesExist(outputDir, generatedFiles);
+
+    String schemaUserContent = FileUtils.fileRead(new File(outputDir, "SchemaUser.java"));
+    assertTrue(schemaUserContent.contains("java.time.Instant"));
   }
 }
diff --git a/lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-joda.xml
similarity index 85%
copy from lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml
copy to lang/java/maven-plugin/src/test/resources/unit/idl/pom-joda.xml
index 69c504b..92c761d 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-joda.xml
@@ -18,12 +18,14 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
+
   <parent>
     <artifactId>avro-parent</artifactId>
     <groupId>org.apache.avro</groupId>
-    <version>1.7.3-SNAPSHOT</version>
-    <relativePath>../../../../../../../../../</relativePath>
+    <version>1.9.0-SNAPSHOT</version>
+    <relativePath>../../../../../../../../../pom.xml</relativePath>
   </parent>
+
   <artifactId>avro-maven-plugin-test</artifactId>
   <packaging>jar</packaging>
 
@@ -43,7 +45,9 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/idl</outputDirectory>
+          <outputDirectory>${basedir}/target/test-harness/idl-joda</outputDirectory>
+          <stringType>String</stringType>
+          <dateTimeLogicalTypeImplementation>joda</dateTimeLogicalTypeImplementation>
           <project implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
         </configuration>
       </plugin>
@@ -53,7 +57,7 @@
     <dependency>
       <groupId>org.apache.avro</groupId>
       <artifactId>avro</artifactId>
-      <version>1.7.3-SNAPSHOT</version>
+      <version>${parent.version}</version>
     </dependency>
     <dependency>
       <groupId>org.codehaus.jackson</groupId>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-jsr310.xml
similarity index 85%
rename from lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml
rename to lang/java/maven-plugin/src/test/resources/unit/idl/pom-jsr310.xml
index 69c504b..5709945 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-jsr310.xml
@@ -18,12 +18,14 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
+
   <parent>
     <artifactId>avro-parent</artifactId>
     <groupId>org.apache.avro</groupId>
-    <version>1.7.3-SNAPSHOT</version>
-    <relativePath>../../../../../../../../../</relativePath>
+    <version>1.9.0-SNAPSHOT</version>
+    <relativePath>../../../../../../../../../pom.xml</relativePath>
   </parent>
+
   <artifactId>avro-maven-plugin-test</artifactId>
   <packaging>jar</packaging>
 
@@ -43,7 +45,9 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/idl</outputDirectory>
+          <outputDirectory>${basedir}/target/test-harness/idl-jsr310</outputDirectory>
+          <stringType>String</stringType>
+          <dateTimeLogicalTypeImplementation>jsr310</dateTimeLogicalTypeImplementation>
           <project implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
         </configuration>
       </plugin>
@@ -53,7 +57,7 @@
     <dependency>
       <groupId>org.apache.avro</groupId>
       <artifactId>avro</artifactId>
-      <version>1.7.3-SNAPSHOT</version>
+      <version>${parent.version}</version>
     </dependency>
     <dependency>
       <groupId>org.codehaus.jackson</groupId>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-joda.xml
similarity index 92%
copy from lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml
copy to lang/java/maven-plugin/src/test/resources/unit/protocol/pom-joda.xml
index b484e3d..68ddcdc 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-joda.xml
@@ -18,12 +18,14 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
+
   <parent>
     <artifactId>avro-parent</artifactId>
     <groupId>org.apache.avro</groupId>
-    <version>1.7.3-SNAPSHOT</version>
-    <relativePath>../../../../../../../../../</relativePath>
+    <version>1.9.0-SNAPSHOT</version>
+    <relativePath>../../../../../../../../../pom.xml</relativePath>
   </parent>
+
   <artifactId>avro-maven-plugin-test</artifactId>
   <packaging>jar</packaging>
 
@@ -43,7 +45,7 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test/avro</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/protocol</outputDirectory>
+          <outputDirectory>${basedir}/target/test-harness/protocol-joda</outputDirectory>
           <project implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
         </configuration>
       </plugin>
@@ -53,7 +55,7 @@
     <dependency>
       <groupId>org.apache.avro</groupId>
       <artifactId>avro</artifactId>
-      <version>1.7.3-SNAPSHOT</version>
+      <version>${parent.version}</version>
     </dependency>
     <dependency>
       <groupId>org.codehaus.jackson</groupId>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-jsr310.xml
similarity index 89%
rename from lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml
rename to lang/java/maven-plugin/src/test/resources/unit/protocol/pom-jsr310.xml
index b484e3d..5ad5bba 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-jsr310.xml
@@ -18,12 +18,14 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
+
   <parent>
     <artifactId>avro-parent</artifactId>
     <groupId>org.apache.avro</groupId>
-    <version>1.7.3-SNAPSHOT</version>
-    <relativePath>../../../../../../../../../</relativePath>
+    <version>1.9.0-SNAPSHOT</version>
+    <relativePath>../../../../../../../../../pom.xml</relativePath>
   </parent>
+
   <artifactId>avro-maven-plugin-test</artifactId>
   <packaging>jar</packaging>
 
@@ -43,7 +45,8 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test/avro</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/protocol</outputDirectory>
+          <outputDirectory>${basedir}/target/test-harness/protocol-jsr310</outputDirectory>
+          <dateTimeLogicalTypeImplementation>jsr310</dateTimeLogicalTypeImplementation>
           <project implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
         </configuration>
       </plugin>
@@ -53,7 +56,7 @@
     <dependency>
       <groupId>org.apache.avro</groupId>
       <artifactId>avro</artifactId>
-      <version>1.7.3-SNAPSHOT</version>
+      <version>${parent.version}</version>
     </dependency>
     <dependency>
       <groupId>org.codehaus.jackson</groupId>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-joda.xml
similarity index 89%
copy from lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml
copy to lang/java/maven-plugin/src/test/resources/unit/schema/pom-joda.xml
index cc000df..f31692b 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-joda.xml
@@ -19,6 +19,13 @@
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
+  <parent>
+    <artifactId>avro-parent</artifactId>
+    <groupId>org.apache.avro</groupId>
+    <version>1.9.0-SNAPSHOT</version>
+    <relativePath>../../../../../../../../../pom.xml</relativePath>
+  </parent>
+
   <artifactId>avro-maven-plugin-test</artifactId>
   <packaging>jar</packaging>
 
@@ -38,7 +45,7 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test/avro</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/schema</outputDirectory>
+          <outputDirectory>${basedir}/target/test-harness/schema-joda</outputDirectory>
           <imports>
             <import>${basedir}/src/test/avro/imports</import>
             <import>${basedir}/src/test/avro/directImport/PrivacyDirectImport.avsc</import>
@@ -52,7 +59,7 @@
     <dependency>
       <groupId>org.apache.avro</groupId>
       <artifactId>avro</artifactId>
-      <version>1.7.3-SNAPSHOT</version>
+      <version>${parent.version}</version>
     </dependency>
     <dependency>
       <groupId>org.codehaus.jackson</groupId>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-jsr310.xml
similarity index 86%
rename from lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml
rename to lang/java/maven-plugin/src/test/resources/unit/schema/pom-jsr310.xml
index cc000df..a439363 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-jsr310.xml
@@ -19,6 +19,13 @@
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
+  <parent>
+    <artifactId>avro-parent</artifactId>
+    <groupId>org.apache.avro</groupId>
+    <version>1.9.0-SNAPSHOT</version>
+    <relativePath>../../../../../../../../../pom.xml</relativePath>
+  </parent>
+
   <artifactId>avro-maven-plugin-test</artifactId>
   <packaging>jar</packaging>
 
@@ -38,7 +45,8 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test/avro</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/schema</outputDirectory>
+          <outputDirectory>${basedir}/target/test-harness/schema-jsr310</outputDirectory>
+          <dateTimeLogicalTypeImplementation>jsr310</dateTimeLogicalTypeImplementation>
           <imports>
             <import>${basedir}/src/test/avro/imports</import>
             <import>${basedir}/src/test/avro/directImport/PrivacyDirectImport.avsc</import>
@@ -52,7 +60,7 @@
     <dependency>
       <groupId>org.apache.avro</groupId>
       <artifactId>avro</artifactId>
-      <version>1.7.3-SNAPSHOT</version>
+      <version>${parent.version}</version>
     </dependency>
     <dependency>
       <groupId>org.codehaus.jackson</groupId>
diff --git a/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java b/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java
index fc7df84..b640388 100644
--- a/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java
+++ b/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java
@@ -23,12 +23,16 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintStream;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Optional;
 import java.util.Set;
 import java.util.LinkedHashSet;
 import java.util.List;
 
 import org.apache.avro.Protocol;
 import org.apache.avro.Schema;
+import org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeImplementation;
 import org.apache.avro.generic.GenericData.StringType;
 import org.apache.avro.compiler.specific.SpecificCompiler;
 
@@ -43,7 +47,7 @@ public class SpecificCompilerTool implements Tool {
       List<String> args) throws Exception {
     if (args.size() < 3) {
       System.err
-          .println("Usage: [-encoding <outputencoding>] [-string] [-bigDecimal] (schema|protocol) input... outputdir");
+          .println("Usage: [-encoding <outputencoding>] [-string] [-bigDecimal] [-dateTimeLogicalTypeImpl <dateTimeType>] (schema|protocol) input... outputdir");
       System.err
           .println(" input - input files or directories");
       System.err
@@ -53,18 +57,21 @@ public class SpecificCompilerTool implements Tool {
       System.err.println(" -string - use java.lang.String instead of Utf8");
       System.err.println(" -bigDecimal - use java.math.BigDecimal for " +
           "decimal type instead of java.nio.ByteBuffer");
+      System.err.println(" -dateTimeLogicalTypeImpl [joda|jsr310] - use either " +
+          "Joda time classes (default) or Java 8 native date/time classes (JSR 310)");
       return 1;
     }
 
     StringType stringType = StringType.CharSequence;
     boolean useLogicalDecimal = false;
+    Optional<DateTimeLogicalTypeImplementation> dateTimeLogicalTypeImplementation = Optional.empty();
+    Optional<String> encoding = Optional.empty();
 
     int arg = 0;
 
-    String encoding = null;
     if ("-encoding".equals(args.get(arg))) {
       arg++;
-      encoding = args.get(arg);
+      encoding = Optional.of(args.get(arg));
       arg++;
     }
 
@@ -78,6 +85,17 @@ public class SpecificCompilerTool implements Tool {
       arg++;
     }
 
+    if ("-dateTimeLogicalTypeImpl".equalsIgnoreCase(args.get(arg))) {
+      arg++;
+      try {
+        dateTimeLogicalTypeImplementation = Optional.of(DateTimeLogicalTypeImplementation.valueOf(args.get(arg).toUpperCase()));
+      } catch (IllegalArgumentException | IndexOutOfBoundsException e) {
+        System.err.println("Expected one of " + Arrays.toString(DateTimeLogicalTypeImplementation.values()));
+        return 1;
+      }
+      arg++;
+    }
+
     String method = args.get(arg);
     List<File> inputs = new ArrayList<>();
     File output = new File(args.get(args.size() - 1));
@@ -90,13 +108,15 @@ public class SpecificCompilerTool implements Tool {
       Schema.Parser parser = new Schema.Parser();
       for (File src : determineInputs(inputs, SCHEMA_FILTER)) {
         Schema schema = parser.parse(src);
-        SpecificCompiler compiler = new SpecificCompiler(schema);
+        SpecificCompiler compiler = new SpecificCompiler(schema,
+          dateTimeLogicalTypeImplementation.orElse(DateTimeLogicalTypeImplementation.JODA));
         executeCompiler(compiler, encoding, stringType, useLogicalDecimal, src, output);
       }
     } else if ("protocol".equals(method)) {
       for (File src : determineInputs(inputs, PROTOCOL_FILTER)) {
         Protocol protocol = Protocol.parse(src);
-        SpecificCompiler compiler = new SpecificCompiler(protocol);
+        SpecificCompiler compiler = new SpecificCompiler(protocol,
+          dateTimeLogicalTypeImplementation.orElse(DateTimeLogicalTypeImplementation.JODA));
         executeCompiler(compiler, encoding, stringType, useLogicalDecimal, src, output);
       }
     } else {
@@ -107,16 +127,14 @@ public class SpecificCompilerTool implements Tool {
   }
 
   private void executeCompiler(SpecificCompiler compiler,
-                               String encoding,
+                               Optional<String> encoding,
                                StringType stringType,
                                boolean enableDecimalLogicalType,
                                File src,
                                File output) throws IOException {
     compiler.setStringType(stringType);
     compiler.setEnableDecimalLogicalType(enableDecimalLogicalType);
-    if (encoding != null) {
-      compiler.setOutputCharacterEncoding(encoding);
-    }
+    encoding.ifPresent(compiler::setOutputCharacterEncoding);
     compiler.compileToDestination(src, output);
   }
 
@@ -144,9 +162,8 @@ public class SpecificCompilerTool implements Tool {
     for (File file : inputs) {
       // if directory, look at contents to see what files match extension
       if (file.isDirectory()) {
-        for (File f : file.listFiles(filter)) {
-          fileSet.add(f);
-        }
+        File[] files = file.listFiles(filter);
+        Collections.addAll(fileSet, files != null ? files : new File[0]);
       }
       // otherwise, just add the file.
       else {
@@ -164,7 +181,7 @@ public class SpecificCompilerTool implements Tool {
       System.err.println("No input files found.");
     }
 
-    return fileSet.toArray((new File[fileSet.size()]));
+    return fileSet.toArray(new File[0]);
   }
 
   private static final FileExtensionFilter SCHEMA_FILTER =
diff --git a/pom.xml b/pom.xml
index f497ba4..23d06c5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -250,6 +250,7 @@
                 <exclude>lang/ruby/.gem/**</exclude>
                 <exclude>lang/ruby/pkg/**</exclude>
                 <exclude>lang/ruby/.bundle/**</exclude>
+                <exclude>lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJsr310LogicalTypes.java</exclude>
                 <exclude>lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java</exclude>
                 <exclude>lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithoutLogicalTypes.java</exclude>
                 <exclude>lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/Player.java</exclude>