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 2019/09/17 07:41:34 UTC

[avro] branch master updated: AVRO-2335: Remove Joda Time library (#631)

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 f310ac8  AVRO-2335: Remove Joda Time library (#631)
f310ac8 is described below

commit f310ac8db5ab962a49d448f41b7b953488cdb033
Author: Fokko Driesprong <fo...@apache.org>
AuthorDate: Tue Sep 17 09:41:29 2019 +0200

    AVRO-2335: Remove Joda Time library (#631)
---
 lang/java/avro/pom.xml                             |   6 -
 .../org/apache/avro/data/JodaTimeConversions.java  | 177 ----
 .../apache/avro/data/TestJodaTimeConversions.java  | 245 -----
 .../org/apache/avro/data/TestTimeConversions.java  |  44 +-
 .../specific/TestRecordWithJsr310LogicalTypes.java | 998 ---------------------
 .../avro/specific/TestRecordWithLogicalTypes.java  | 342 +++++--
 .../avro/specific/TestSpecificLogicalTypes.java    | 266 ------
 .../avro/specific/TestSpecificToFromByteArray.java |  59 +-
 ..._types.avsc => TestRecordWithLogicalTypes.avsc} |   0
 lang/java/compiler/pom.xml                         |   4 -
 .../avro/compiler/specific/ProtocolTask.java       |  15 +-
 .../apache/avro/compiler/specific/SchemaTask.java  |   6 +-
 .../avro/compiler/specific/SpecificCompiler.java   |  80 +-
 .../compiler/specific/TestSpecificCompiler.java    | 101 +--
 .../org/apache/avro/mojo/AbstractAvroMojo.java     |  33 +-
 .../java/org/apache/avro/mojo/IDLProtocolMojo.java |   2 +-
 .../java/org/apache/avro/mojo/ProtocolMojo.java    |   6 +-
 .../main/java/org/apache/avro/mojo/SchemaMojo.java |   4 +-
 .../org/apache/avro/mojo/AbstractAvroMojoTest.java |  24 +-
 .../org/apache/avro/mojo/TestIDLProtocolMojo.java  |  51 +-
 .../java/org/apache/avro/mojo/TestInduceMojo.java  |   5 +
 .../org/apache/avro/mojo/TestProtocolMojo.java     |  46 +-
 .../java/org/apache/avro/mojo/TestSchemaMojo.java  |  53 +-
 .../unit/idl/pom-injecting-velocity-tools.xml      |   3 +-
 .../src/test/resources/unit/idl/pom-jsr310.xml     |  69 --
 .../resources/unit/idl/{pom-joda.xml => pom.xml}   |   4 +-
 .../unit/protocol/pom-injecting-velocity-tools.xml |   3 +-
 .../test/resources/unit/protocol/pom-jsr310.xml    |  68 --
 .../unit/protocol/{pom-joda.xml => pom.xml}        |   4 +-
 .../unit/schema/pom-injecting-velocity-tools.xml   |   3 +-
 .../src/test/resources/unit/schema/pom-jsr310.xml  |  72 --
 .../unit/schema/{pom-joda.xml => pom.xml}          |   4 +-
 lang/java/pom.xml                                  |   6 -
 .../org/apache/avro/tool/SpecificCompilerTool.java |  25 +-
 pom.xml                                            |   1 -
 35 files changed, 475 insertions(+), 2354 deletions(-)

diff --git a/lang/java/avro/pom.xml b/lang/java/avro/pom.xml
index 0bed7aa..001597f 100644
--- a/lang/java/avro/pom.xml
+++ b/lang/java/avro/pom.xml
@@ -234,15 +234,9 @@
       <optional>true</optional>
     </dependency>
     <dependency>
-      <groupId>joda-time</groupId>
-      <artifactId>joda-time</artifactId>
-      <optional>true</optional>
-    </dependency>
-    <dependency>
       <groupId>org.hamcrest</groupId>
       <artifactId>hamcrest-library</artifactId>
       <scope>test</scope>
     </dependency>
   </dependencies>
-
 </project>
diff --git a/lang/java/avro/src/main/java/org/apache/avro/data/JodaTimeConversions.java b/lang/java/avro/src/main/java/org/apache/avro/data/JodaTimeConversions.java
deleted file mode 100644
index b009947..0000000
--- a/lang/java/avro/src/main/java/org/apache/avro/data/JodaTimeConversions.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * 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
- *
- *     https://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 org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.joda.time.Days;
-import org.joda.time.LocalDate;
-import org.joda.time.LocalTime;
-
-/**
- * @deprecated use {@link org.apache.avro.data.TimeConversions} instead of Joda
- *             date/time API
- */
-@Deprecated
-public class JodaTimeConversions {
-  public static class DateConversion extends Conversion<LocalDate> {
-    private static final LocalDate EPOCH_DATE = new LocalDate(1970, 1, 1);
-
-    @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 EPOCH_DATE.plusDays(daysFromEpoch);
-    }
-
-    @Override
-    public Integer toInt(LocalDate date, Schema schema, LogicalType type) {
-      return Days.daysBetween(EPOCH_DATE, date).getDays();
-    }
-
-    @Override
-    public Schema getRecommendedSchema() {
-      return LogicalTypes.date().addToSchema(Schema.create(Schema.Type.INT));
-    }
-  }
-
-  public static class TimeConversion 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.fromMillisOfDay(millisFromMidnight);
-    }
-
-    @Override
-    public Integer toInt(LocalTime time, Schema schema, LogicalType type) {
-      return time.millisOfDay().get();
-    }
-
-    @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.fromMillisOfDay(microsFromMidnight / 1000);
-    }
-
-    @Override
-    public Schema getRecommendedSchema() {
-      return LogicalTypes.timeMicros().addToSchema(Schema.create(Schema.Type.LONG));
-    }
-  }
-
-  public static class LossyTimeMicrosConversion extends TimeMicrosConversion {
-    @Override
-    public Long toLong(LocalTime time, Schema schema, LogicalType type) {
-      return 1000 * (long) time.millisOfDay().get();
-    }
-  }
-
-  public static class TimestampConversion extends Conversion<DateTime> {
-    @Override
-    public Class<DateTime> getConvertedType() {
-      return DateTime.class;
-    }
-
-    @Override
-    public String getLogicalTypeName() {
-      return "timestamp-millis";
-    }
-
-    @Override
-    public DateTime fromLong(Long millisFromEpoch, Schema schema, LogicalType type) {
-      return new DateTime(millisFromEpoch, DateTimeZone.UTC);
-    }
-
-    @Override
-    public Long toLong(DateTime timestamp, Schema schema, LogicalType type) {
-      return timestamp.getMillis();
-    }
-
-    @Override
-    public Schema getRecommendedSchema() {
-      return LogicalTypes.timestampMillis().addToSchema(Schema.create(Schema.Type.LONG));
-    }
-  }
-
-  public static class TimestampMicrosConversion extends Conversion<DateTime> {
-    @Override
-    public Class<DateTime> getConvertedType() {
-      return DateTime.class;
-    }
-
-    @Override
-    public String getLogicalTypeName() {
-      return "timestamp-micros";
-    }
-
-    @Override
-    public DateTime fromLong(Long microsFromEpoch, Schema schema, LogicalType type) {
-      return new DateTime(microsFromEpoch / 1000, DateTimeZone.UTC);
-    }
-
-    @Override
-    public Schema getRecommendedSchema() {
-      return LogicalTypes.timestampMicros().addToSchema(Schema.create(Schema.Type.LONG));
-    }
-  }
-
-  public static class LossyTimestampMicrosConversion extends TimestampMicrosConversion {
-    @Override
-    public Long toLong(DateTime timestamp, Schema schema, LogicalType type) {
-      return 1000 * timestamp.getMillis();
-    }
-  }
-}
diff --git a/lang/java/avro/src/test/java/org/apache/avro/data/TestJodaTimeConversions.java b/lang/java/avro/src/test/java/org/apache/avro/data/TestJodaTimeConversions.java
deleted file mode 100644
index 714dfca..0000000
--- a/lang/java/avro/src/test/java/org/apache/avro/data/TestJodaTimeConversions.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * 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
- *
- *     https://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 java.util.Date;
-
-import org.apache.avro.Conversion;
-import org.apache.avro.LogicalTypes;
-import org.apache.avro.Schema;
-import org.apache.avro.data.JodaTimeConversions.*;
-import org.apache.avro.reflect.ReflectData;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.joda.time.LocalDate;
-import org.joda.time.LocalTime;
-import org.junit.Assert;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class TestJodaTimeConversions {
-
-  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() {
-    TestJodaTimeConversions.DATE_SCHEMA = LogicalTypes.date().addToSchema(Schema.create(Schema.Type.INT));
-    TestJodaTimeConversions.TIME_MILLIS_SCHEMA = LogicalTypes.timeMillis().addToSchema(Schema.create(Schema.Type.INT));
-    TestJodaTimeConversions.TIME_MICROS_SCHEMA = LogicalTypes.timeMicros().addToSchema(Schema.create(Schema.Type.LONG));
-    TestJodaTimeConversions.TIMESTAMP_MILLIS_SCHEMA = LogicalTypes.timestampMillis()
-        .addToSchema(Schema.create(Schema.Type.LONG));
-    TestJodaTimeConversions.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 = new LocalDate(1970, 1, 6); // 5
-    LocalDate Jan_1_1970 = new LocalDate(1970, 1, 1); // 0
-    LocalDate Dec_27_1969 = new LocalDate(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 {
-    TimeConversion conversion = new TimeConversion();
-    LocalTime oneAM = new LocalTime(1, 0);
-    LocalTime afternoon = new LocalTime(15, 14, 15, 926);
-    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", 3600000,
-        (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 = new LocalTime(1, 0);
-    LocalTime afternoon = new LocalTime(15, 14, 15, 926);
-    long afternoonMicros = ((long) (15 * 60 + 14) * 60 + 15) * 1000000 + 926551;
-
-    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(3600000000L, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
-    Assert.assertEquals("15:14:15.926000 should be " + afternoonMicros, afternoon,
-        conversion.fromLong(afternoonMicros, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
-
-    try {
-      conversion.toLong(afternoon, TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMicros());
-      Assert.fail("Should not convert LocalTime to long");
-    } catch (UnsupportedOperationException e) {
-      // expected
-    }
-  }
-
-  @Test
-  public void testLossyTimeMicrosConversion() throws Exception {
-    TimeMicrosConversion conversion = new LossyTimeMicrosConversion();
-    LocalTime oneAM = new LocalTime(1, 0);
-    LocalTime afternoon = new LocalTime(15, 14, 15, 926);
-    long afternoonMicros = ((long) (15 * 60 + 14) * 60 + 15) * 1000000 + 926551;
-
-    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", 3600000000L,
-        (long) conversion.toLong(oneAM, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
-    Assert.assertEquals("15:14:15.926551 should be " + afternoonMicros, dropMicros(afternoonMicros), // loses precision!
-        (long) conversion.toLong(afternoon, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
-
-    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(3600000000L, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
-    Assert.assertEquals("15:14:15.926000 should be " + afternoonMicros, afternoon,
-        conversion.fromLong(afternoonMicros, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
-  }
-
-  @Test
-  public void testTimestampMillisConversion() throws Exception {
-    TimestampConversion conversion = new TimestampConversion();
-    long nowInstant = new Date().getTime();
-
-    DateTime 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;
-    DateTime May_28_2015_21_46_53_221 = new DateTime(2015, 5, 28, 21, 46, 53, 221, DateTimeZone.UTC);
-
-    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()));
-  }
-
-  @Test
-  public void testTimestampMicrosConversion() throws Exception {
-    TimestampMicrosConversion conversion = new TimestampMicrosConversion();
-
-    long May_28_2015_21_46_53_221_843_instant = 1432849613221L * 1000 + 843;
-    DateTime May_28_2015_21_46_53_221 = new DateTime(2015, 5, 28, 21, 46, 53, 221, DateTimeZone.UTC);
-
-    Assert.assertEquals("Known date should be correct", May_28_2015_21_46_53_221, conversion
-        .fromLong(May_28_2015_21_46_53_221_843_instant, TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMicros()));
-
-    try {
-      conversion.toLong(May_28_2015_21_46_53_221, TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMicros());
-      Assert.fail("Should not convert DateTime to long");
-    } catch (UnsupportedOperationException e) {
-      // expected
-    }
-  }
-
-  @Test
-  public void testLossyTimestampMicrosConversion() throws Exception {
-    TimestampMicrosConversion conversion = new LossyTimestampMicrosConversion();
-    long nowInstant = new Date().getTime() * 1000 + 674; // add fake micros
-
-    DateTime now = conversion.fromLong(nowInstant, TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMicros());
-    long roundTrip = conversion.toLong(now, TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMicros());
-    Assert.assertEquals("Round-trip conversion should lose microseconds", dropMicros(nowInstant), roundTrip);
-
-    long May_28_2015_21_46_53_221_843_instant = 1432849613221L * 1000 + 843;
-    DateTime May_28_2015_21_46_53_221 = new DateTime(2015, 5, 28, 21, 46, 53, 221, DateTimeZone.UTC);
-
-    Assert.assertEquals("Known date should be correct", May_28_2015_21_46_53_221, conversion
-        .fromLong(May_28_2015_21_46_53_221_843_instant, TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMicros()));
-    Assert.assertEquals("Known date should be correct", dropMicros(May_28_2015_21_46_53_221_843_instant),
-        (long) conversion.toLong(May_28_2015_21_46_53_221, TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMicros()));
-  }
-
-  /*
-   * model.addLogicalTypeConversion(new
-   * JodaTimeConversions.TimeMicrosConversion());
-   * model.addLogicalTypeConversion(new
-   * JodaTimeConversions.TimestampMicrosConversion());
-   */
-  @Test
-  public void testDynamicSchemaWithDateConversion() throws ClassNotFoundException {
-    Schema schema = getReflectedSchemaByName("org.joda.time.LocalDate", new JodaTimeConversions.DateConversion());
-    Assert.assertEquals("Reflected schema should be logicalType date", DATE_SCHEMA, schema);
-  }
-
-  @Test
-  public void testDynamicSchemaWithTimeConversion() throws ClassNotFoundException {
-    Schema schema = getReflectedSchemaByName("org.joda.time.LocalTime", new JodaTimeConversions.TimeConversion());
-    Assert.assertEquals("Reflected schema should be logicalType timeMillis", TIME_MILLIS_SCHEMA, schema);
-  }
-
-  @Test
-  public void testDynamicSchemaWithTimeMicrosConversion() throws ClassNotFoundException {
-    Schema schema = getReflectedSchemaByName("org.joda.time.LocalTime", new JodaTimeConversions.TimeMicrosConversion());
-    Assert.assertEquals("Reflected schema should be logicalType timeMicros", TIME_MICROS_SCHEMA, schema);
-  }
-
-  @Test
-  public void testDynamicSchemaWithDateTimeConversion() throws ClassNotFoundException {
-    Schema schema = getReflectedSchemaByName("org.joda.time.DateTime", new JodaTimeConversions.TimestampConversion());
-    Assert.assertEquals("Reflected schema should be logicalType timestampMillis", TIMESTAMP_MILLIS_SCHEMA, schema);
-  }
-
-  @Test
-  public void testDynamicSchemaWithDateTimeMicrosConversion() throws ClassNotFoundException {
-    Schema schema = getReflectedSchemaByName("org.joda.time.DateTime",
-        new JodaTimeConversions.TimestampMicrosConversion());
-    Assert.assertEquals("Reflected schema should be logicalType timestampMicros", TIMESTAMP_MICROS_SCHEMA, schema);
-  }
-
-  private Schema getReflectedSchemaByName(String className, Conversion<?> conversion) throws ClassNotFoundException {
-    // one argument: a fully qualified class name
-    Class<?> cls = Class.forName(className);
-
-    // get the reflected schema for the given class
-    ReflectData model = new ReflectData();
-    model.addLogicalTypeConversion(conversion);
-    return model.getSchema(cls);
-  }
-
-  private long dropMicros(long micros) {
-    return micros / 1000 * 1000;
-  }
-}
diff --git a/lang/java/avro/src/test/java/org/apache/avro/data/TestTimeConversions.java b/lang/java/avro/src/test/java/org/apache/avro/data/TestTimeConversions.java
index 69d9707..dcce14a 100644
--- a/lang/java/avro/src/test/java/org/apache/avro/data/TestTimeConversions.java
+++ b/lang/java/avro/src/test/java/org/apache/avro/data/TestTimeConversions.java
@@ -20,9 +20,11 @@ package org.apache.avro.data;
 
 import java.time.*;
 
+import org.apache.avro.Conversion;
 import org.apache.avro.LogicalTypes;
 import org.apache.avro.Schema;
 import org.apache.avro.data.TimeConversions.*;
+import org.apache.avro.reflect.ReflectData;
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -67,7 +69,7 @@ public class TestTimeConversions {
   }
 
   @Test
-  public void testTimeMillisConversion() throws Exception {
+  public void testTimeMillisConversion() {
     TimeMillisConversion conversion = new TimeMillisConversion();
     LocalTime oneAM = LocalTime.of(1, 0);
     LocalTime afternoon = LocalTime.of(15, 14, 15, 926_000_000);
@@ -180,4 +182,44 @@ public class TestTimeConversions {
     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()));
   }
+
+  @Test
+  public void testDynamicSchemaWithDateConversion() throws ClassNotFoundException {
+    Schema schema = getReflectedSchemaByName("java.time.LocalDate", new TimeConversions.DateConversion());
+    Assert.assertEquals("Reflected schema should be logicalType date", DATE_SCHEMA, schema);
+  }
+
+  @Test
+  public void testDynamicSchemaWithTimeConversion() throws ClassNotFoundException {
+    Schema schema = getReflectedSchemaByName("java.time.LocalTime", new TimeConversions.TimeMillisConversion());
+    Assert.assertEquals("Reflected schema should be logicalType timeMillis", TIME_MILLIS_SCHEMA, schema);
+  }
+
+  @Test
+  public void testDynamicSchemaWithTimeMicrosConversion() throws ClassNotFoundException {
+    Schema schema = getReflectedSchemaByName("java.time.LocalTime", new TimeConversions.TimeMicrosConversion());
+    Assert.assertEquals("Reflected schema should be logicalType timeMicros", TIME_MICROS_SCHEMA, schema);
+  }
+
+  @Test
+  public void testDynamicSchemaWithDateTimeConversion() throws ClassNotFoundException {
+    Schema schema = getReflectedSchemaByName("java.time.Instant", new TimeConversions.TimestampMillisConversion());
+    Assert.assertEquals("Reflected schema should be logicalType timestampMillis", TIMESTAMP_MILLIS_SCHEMA, schema);
+  }
+
+  @Test
+  public void testDynamicSchemaWithDateTimeMicrosConversion() throws ClassNotFoundException {
+    Schema schema = getReflectedSchemaByName("java.time.Instant", new TimeConversions.TimestampMicrosConversion());
+    Assert.assertEquals("Reflected schema should be logicalType timestampMicros", TIMESTAMP_MICROS_SCHEMA, schema);
+  }
+
+  private Schema getReflectedSchemaByName(String className, Conversion<?> conversion) throws ClassNotFoundException {
+    // one argument: a fully qualified class name
+    Class<?> cls = Class.forName(className);
+
+    // get the reflected schema for the given class
+    ReflectData model = new ReflectData();
+    model.addLogicalTypeConversion(conversion);
+    return model.getSchema(cls);
+  }
 }
diff --git a/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJsr310LogicalTypes.java b/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJsr310LogicalTypes.java
deleted file mode 100644
index 7abcb0b..0000000
--- a/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJsr310LogicalTypes.java
+++ /dev/null
@@ -1,998 +0,0 @@
-/**
- * Autogenerated by Avro
- *
- * DO NOT EDIT DIRECTLY
- */
-package org.apache.avro.specific;
-
-import org.apache.avro.data.TimeConversions;
-import org.apache.avro.message.BinaryMessageDecoder;
-import org.apache.avro.message.BinaryMessageEncoder;
-import org.apache.avro.message.SchemaStore;
-
-@SuppressWarnings("all")
-@org.apache.avro.specific.AvroGenerated
-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\",\"type\":{\"type\":\"int\",\"logicalType\":\"date\"}},{\"name\":\"t\",\"type\":{\"type\":\"int [...]
-
-  public static org.apache.avro.Schema getClassSchema() {
-    return SCHEMA$;
-  }
-
-  private static SpecificData MODEL$ = new SpecificData();
-
-  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;
-  }
-
-  /**
-   * 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 TestRecordWithJsr310LogicalTypes to a ByteBuffer. */
-  public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {
-    return ENCODER.encode(this);
-  }
-
-  /** Deserializes a TestRecordWithJsr310LogicalTypes from a ByteBuffer. */
-  public static TestRecordWithJsr310LogicalTypes fromByteBuffer(java.nio.ByteBuffer b) throws java.io.IOException {
-    return DECODER.decode(b);
-  }
-
-  @Deprecated
-  public boolean b;
-  @Deprecated
-  public int i32;
-  @Deprecated
-  public long i64;
-  @Deprecated
-  public float f32;
-  @Deprecated
-  public double f64;
-  @Deprecated
-  public java.lang.CharSequence s;
-  @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 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 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;
-    this.f32 = f32;
-    this.f64 = f64;
-    this.s = s;
-    this.d = d;
-    this.t = t;
-    this.ts = ts;
-    this.dec = dec;
-  }
-
-  @Override
-  public org.apache.avro.Schema getSchema() {
-    return SCHEMA$;
-  }
-
-  // Used by DatumWriter. Applications should not call.
-  @Override
-  public java.lang.Object get(int field$) {
-    switch (field$) {
-    case 0:
-      return b;
-    case 1:
-      return i32;
-    case 2:
-      return i64;
-    case 3:
-      return f32;
-    case 4:
-      return f64;
-    case 5:
-      return s;
-    case 6:
-      return d;
-    case 7:
-      return t;
-    case 8:
-      return ts;
-    case 9:
-      return dec;
-    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 TimeConversions.DateConversion DATE_CONVERSION = new TimeConversions.DateConversion();
-  protected static final TimeConversions.TimeMillisConversion TIME_CONVERSION = new TimeConversions.TimeMillisConversion();
-  protected static final TimeConversions.TimestampMillisConversion TIMESTAMP_CONVERSION = new TimeConversions.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")
-  @Override
-  public void put(int field$, java.lang.Object value$) {
-    switch (field$) {
-    case 0:
-      b = (java.lang.Boolean) value$;
-      break;
-    case 1:
-      i32 = (java.lang.Integer) value$;
-      break;
-    case 2:
-      i64 = (java.lang.Long) value$;
-      break;
-    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 = (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;
-  }
-
-  /**
-   * Sets the value of the 'b' field.
-   *
-   * @param value the value to set.
-   */
-  public void setB(java.lang.Boolean value) {
-    this.b = value;
-  }
-
-  /**
-   * Gets the value of the 'i32' field.
-   *
-   * @return The value of the 'i32' field.
-   */
-  public java.lang.Integer getI32() {
-    return i32;
-  }
-
-  /**
-   * Sets the value of the 'i32' field.
-   *
-   * @param value the value to set.
-   */
-  public void setI32(java.lang.Integer value) {
-    this.i32 = value;
-  }
-
-  /**
-   * Gets the value of the 'i64' field.
-   *
-   * @return The value of the 'i64' field.
-   */
-  public java.lang.Long getI64() {
-    return i64;
-  }
-
-  /**
-   * Sets the value of the 'i64' field.
-   *
-   * @param value the value to set.
-   */
-  public void setI64(java.lang.Long value) {
-    this.i64 = value;
-  }
-
-  /**
-   * Gets the value of the 'f32' field.
-   *
-   * @return The value of the 'f32' field.
-   */
-  public java.lang.Float getF32() {
-    return f32;
-  }
-
-  /**
-   * Sets the value of the 'f32' field.
-   *
-   * @param value the value to set.
-   */
-  public void setF32(java.lang.Float value) {
-    this.f32 = value;
-  }
-
-  /**
-   * Gets the value of the 'f64' field.
-   *
-   * @return The value of the 'f64' field.
-   */
-  public java.lang.Double getF64() {
-    return f64;
-  }
-
-  /**
-   * Sets the value of the 'f64' field.
-   *
-   * @param value the value to set.
-   */
-  public void setF64(java.lang.Double value) {
-    this.f64 = value;
-  }
-
-  /**
-   * Gets the value of the 's' field.
-   *
-   * @return The value of the 's' field.
-   */
-  public java.lang.CharSequence getS() {
-    return s;
-  }
-
-  /**
-   * Sets the value of the 's' field.
-   *
-   * @param value the value to set.
-   */
-  public void setS(java.lang.CharSequence value) {
-    this.s = value;
-  }
-
-  /**
-   * Gets the value of the 'd' field.
-   *
-   * @return The value of the 'd' field.
-   */
-  public java.time.LocalDate getD() {
-    return d;
-  }
-
-  /**
-   * Sets the value of the 'd' field.
-   *
-   * @param value the value to set.
-   */
-  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 java.time.LocalTime getT() {
-    return t;
-  }
-
-  /**
-   * Sets the value of the 't' field.
-   *
-   * @param value the value to set.
-   */
-  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 java.time.Instant getTs() {
-    return ts;
-  }
-
-  /**
-   * Sets the value of the 'ts' field.
-   *
-   * @param value the value to set.
-   */
-  public void setTs(java.time.Instant value) {
-    this.ts = value;
-  }
-
-  /**
-   * Gets the value of the 'dec' field.
-   *
-   * @return The value of the 'dec' field.
-   */
-  public java.math.BigDecimal getDec() {
-    return dec;
-  }
-
-  /**
-   * Sets the value of the 'dec' field.
-   *
-   * @param value the value to set.
-   */
-  public void setDec(java.math.BigDecimal value) {
-    this.dec = value;
-  }
-
-  /**
-   * Creates a new TestRecordWithJsr310LogicalTypes RecordBuilder.
-   *
-   * @return A new TestRecordWithJsr310LogicalTypes RecordBuilder
-   */
-  public static TestRecordWithJsr310LogicalTypes.Builder newBuilder() {
-    return new TestRecordWithJsr310LogicalTypes.Builder();
-  }
-
-  /**
-   * 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 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 TestRecordWithJsr310LogicalTypes instances.
-   */
-  public static class Builder
-      extends org.apache.avro.specific.SpecificRecordBuilderBase<TestRecordWithJsr310LogicalTypes>
-      implements org.apache.avro.data.RecordBuilder<TestRecordWithJsr310LogicalTypes> {
-
-    private boolean b;
-    private int i32;
-    private long i64;
-    private float f32;
-    private double f64;
-    private java.lang.CharSequence s;
-    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(SCHEMA$);
-    }
-
-    /**
-     * 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] = other.fieldSetFlags()[0];
-      }
-      if (isValidValue(fields()[1], other.i32)) {
-        this.i32 = data().deepCopy(fields()[1].schema(), other.i32);
-        fieldSetFlags()[1] = other.fieldSetFlags()[1];
-      }
-      if (isValidValue(fields()[2], other.i64)) {
-        this.i64 = data().deepCopy(fields()[2].schema(), other.i64);
-        fieldSetFlags()[2] = other.fieldSetFlags()[2];
-      }
-      if (isValidValue(fields()[3], other.f32)) {
-        this.f32 = data().deepCopy(fields()[3].schema(), other.f32);
-        fieldSetFlags()[3] = other.fieldSetFlags()[3];
-      }
-      if (isValidValue(fields()[4], other.f64)) {
-        this.f64 = data().deepCopy(fields()[4].schema(), other.f64);
-        fieldSetFlags()[4] = other.fieldSetFlags()[4];
-      }
-      if (isValidValue(fields()[5], other.s)) {
-        this.s = data().deepCopy(fields()[5].schema(), other.s);
-        fieldSetFlags()[5] = other.fieldSetFlags()[5];
-      }
-      if (isValidValue(fields()[6], other.d)) {
-        this.d = data().deepCopy(fields()[6].schema(), other.d);
-        fieldSetFlags()[6] = other.fieldSetFlags()[6];
-      }
-      if (isValidValue(fields()[7], other.t)) {
-        this.t = data().deepCopy(fields()[7].schema(), other.t);
-        fieldSetFlags()[7] = other.fieldSetFlags()[7];
-      }
-      if (isValidValue(fields()[8], other.ts)) {
-        this.ts = data().deepCopy(fields()[8].schema(), other.ts);
-        fieldSetFlags()[8] = other.fieldSetFlags()[8];
-      }
-      if (isValidValue(fields()[9], other.dec)) {
-        this.dec = data().deepCopy(fields()[9].schema(), other.dec);
-        fieldSetFlags()[9] = other.fieldSetFlags()[9];
-      }
-    }
-
-    /**
-     * 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;
-      }
-      if (isValidValue(fields()[1], other.i32)) {
-        this.i32 = data().deepCopy(fields()[1].schema(), other.i32);
-        fieldSetFlags()[1] = true;
-      }
-      if (isValidValue(fields()[2], other.i64)) {
-        this.i64 = data().deepCopy(fields()[2].schema(), other.i64);
-        fieldSetFlags()[2] = true;
-      }
-      if (isValidValue(fields()[3], other.f32)) {
-        this.f32 = data().deepCopy(fields()[3].schema(), other.f32);
-        fieldSetFlags()[3] = true;
-      }
-      if (isValidValue(fields()[4], other.f64)) {
-        this.f64 = data().deepCopy(fields()[4].schema(), other.f64);
-        fieldSetFlags()[4] = true;
-      }
-      if (isValidValue(fields()[5], other.s)) {
-        this.s = data().deepCopy(fields()[5].schema(), other.s);
-        fieldSetFlags()[5] = true;
-      }
-      if (isValidValue(fields()[6], other.d)) {
-        this.d = data().deepCopy(fields()[6].schema(), other.d);
-        fieldSetFlags()[6] = true;
-      }
-      if (isValidValue(fields()[7], other.t)) {
-        this.t = data().deepCopy(fields()[7].schema(), other.t);
-        fieldSetFlags()[7] = true;
-      }
-      if (isValidValue(fields()[8], other.ts)) {
-        this.ts = data().deepCopy(fields()[8].schema(), other.ts);
-        fieldSetFlags()[8] = true;
-      }
-      if (isValidValue(fields()[9], other.dec)) {
-        this.dec = data().deepCopy(fields()[9].schema(), other.dec);
-        fieldSetFlags()[9] = true;
-      }
-    }
-
-    /**
-     * Gets the value of the 'b' field.
-     *
-     * @return The value.
-     */
-    public java.lang.Boolean getB() {
-      return b;
-    }
-
-    /**
-     * Sets the value of the 'b' field.
-     *
-     * @param value The value of 'b'.
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder setB(boolean value) {
-      validate(fields()[0], value);
-      this.b = value;
-      fieldSetFlags()[0] = true;
-      return this;
-    }
-
-    /**
-     * 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];
-    }
-
-    /**
-     * Clears the value of the 'b' field.
-     *
-     * @return This builder.
-     */
-    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;
-    }
-
-    /**
-     * Sets the value of the 'i32' field.
-     *
-     * @param value The value of 'i32'.
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder setI32(int value) {
-      validate(fields()[1], value);
-      this.i32 = value;
-      fieldSetFlags()[1] = true;
-      return this;
-    }
-
-    /**
-     * 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];
-    }
-
-    /**
-     * Clears the value of the 'i32' field.
-     *
-     * @return This builder.
-     */
-    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;
-    }
-
-    /**
-     * Sets the value of the 'i64' field.
-     *
-     * @param value The value of 'i64'.
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder setI64(long value) {
-      validate(fields()[2], value);
-      this.i64 = value;
-      fieldSetFlags()[2] = true;
-      return this;
-    }
-
-    /**
-     * 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];
-    }
-
-    /**
-     * Clears the value of the 'i64' field.
-     *
-     * @return This builder.
-     */
-    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;
-    }
-
-    /**
-     * Sets the value of the 'f32' field.
-     *
-     * @param value The value of 'f32'.
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder setF32(float value) {
-      validate(fields()[3], value);
-      this.f32 = value;
-      fieldSetFlags()[3] = true;
-      return this;
-    }
-
-    /**
-     * 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];
-    }
-
-    /**
-     * Clears the value of the 'f32' field.
-     *
-     * @return This builder.
-     */
-    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;
-    }
-
-    /**
-     * Sets the value of the 'f64' field.
-     *
-     * @param value The value of 'f64'.
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder setF64(double value) {
-      validate(fields()[4], value);
-      this.f64 = value;
-      fieldSetFlags()[4] = true;
-      return this;
-    }
-
-    /**
-     * 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];
-    }
-
-    /**
-     * Clears the value of the 'f64' field.
-     *
-     * @return This builder.
-     */
-    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;
-    }
-
-    /**
-     * Sets the value of the 's' field.
-     *
-     * @param value The value of 's'.
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder setS(java.lang.CharSequence value) {
-      validate(fields()[5], value);
-      this.s = value;
-      fieldSetFlags()[5] = true;
-      return this;
-    }
-
-    /**
-     * 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];
-    }
-
-    /**
-     * Clears the value of the 's' field.
-     *
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder clearS() {
-      s = null;
-      fieldSetFlags()[5] = false;
-      return this;
-    }
-
-    /**
-     * Gets the value of the 'd' field.
-     *
-     * @return The value.
-     */
-    public java.time.LocalDate getD() {
-      return d;
-    }
-
-    /**
-     * Sets the value of the 'd' field.
-     *
-     * @param value The value of 'd'.
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder setD(java.time.LocalDate value) {
-      validate(fields()[6], value);
-      this.d = value;
-      fieldSetFlags()[6] = true;
-      return this;
-    }
-
-    /**
-     * 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];
-    }
-
-    /**
-     * Clears the value of the 'd' field.
-     *
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder clearD() {
-      fieldSetFlags()[6] = false;
-      return this;
-    }
-
-    /**
-     * Gets the value of the 't' field.
-     *
-     * @return The value.
-     */
-    public java.time.LocalTime getT() {
-      return t;
-    }
-
-    /**
-     * Sets the value of the 't' field.
-     *
-     * @param value The value of 't'.
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder setT(java.time.LocalTime value) {
-      validate(fields()[7], value);
-      this.t = value;
-      fieldSetFlags()[7] = true;
-      return this;
-    }
-
-    /**
-     * 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];
-    }
-
-    /**
-     * Clears the value of the 't' field.
-     *
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder clearT() {
-      fieldSetFlags()[7] = false;
-      return this;
-    }
-
-    /**
-     * Gets the value of the 'ts' field.
-     *
-     * @return The value.
-     */
-    public java.time.Instant getTs() {
-      return ts;
-    }
-
-    /**
-     * Sets the value of the 'ts' field.
-     *
-     * @param value The value of 'ts'.
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder setTs(java.time.Instant value) {
-      validate(fields()[8], value);
-      this.ts = value;
-      fieldSetFlags()[8] = true;
-      return this;
-    }
-
-    /**
-     * 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];
-    }
-
-    /**
-     * Clears the value of the 'ts' field.
-     *
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder clearTs() {
-      fieldSetFlags()[8] = false;
-      return this;
-    }
-
-    /**
-     * Gets the value of the 'dec' field.
-     *
-     * @return The value.
-     */
-    public java.math.BigDecimal getDec() {
-      return dec;
-    }
-
-    /**
-     * 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()[9] = true;
-      return this;
-    }
-
-    /**
-     * Checks whether the 'dec' field has been set.
-     *
-     * @return True if the 'dec' field has been set, false otherwise.
-     */
-    public boolean hasDec() {
-      return fieldSetFlags()[9];
-    }
-
-    /**
-     * Clears the value of the 'dec' field.
-     *
-     * @return This builder.
-     */
-    public TestRecordWithJsr310LogicalTypes.Builder clearDec() {
-      dec = null;
-      fieldSetFlags()[9] = false;
-      return this;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public TestRecordWithJsr310LogicalTypes build() {
-      try {
-        TestRecordWithJsr310LogicalTypes record = new TestRecordWithJsr310LogicalTypes();
-        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 : (java.time.LocalDate) defaultValue(fields()[6]);
-        record.t = fieldSetFlags()[7] ? this.t : (java.time.LocalTime) defaultValue(fields()[7]);
-        record.ts = fieldSetFlags()[8] ? this.ts : (java.time.Instant) defaultValue(fields()[8]);
-        record.dec = fieldSetFlags()[9] ? this.dec : (java.math.BigDecimal) defaultValue(fields()[9]);
-        return record;
-      } catch (java.lang.Exception e) {
-        throw new org.apache.avro.AvroRuntimeException(e);
-      }
-    }
-  }
-
-  @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, SpecificData.getEncoder(out));
-  }
-
-  @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, 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 17cdf0c..f684de7 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,17 +5,16 @@
  */
 package org.apache.avro.specific;
 
-import java.math.BigDecimal;
-
-import org.apache.avro.data.JodaTimeConversions;
+import org.apache.avro.data.TimeConversions;
 import org.apache.avro.message.BinaryMessageDecoder;
 import org.apache.avro.message.BinaryMessageEncoder;
+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;
+  private static final long serialVersionUID = 3313339903648295220L;
   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\":{\"type\":\"int\",\"logicalType\":\"date\"}},{\"name\":\"t\",\"type\":{\"type\":\"int\",\"l [...]
 
@@ -31,12 +30,29 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
   private static final BinaryMessageDecoder<TestRecordWithLogicalTypes> DECODER = new BinaryMessageDecoder<TestRecordWithLogicalTypes>(
       MODEL$, SCHEMA$);
 
-  /** Serializes this ${schema.getName()} to a ByteBuffer. */
+  /**
+   * Return the BinaryMessageDecoder instance used by this class.
+   */
+  public static BinaryMessageDecoder<TestRecordWithLogicalTypes> getDecoder() {
+    return DECODER;
+  }
+
+  /**
+   * 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<TestRecordWithLogicalTypes> createDecoder(SchemaStore resolver) {
+    return new BinaryMessageDecoder<TestRecordWithLogicalTypes>(MODEL$, SCHEMA$, resolver);
+  }
+
+  /** Serializes this TestRecordWithLogicalTypes to a ByteBuffer. */
   public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {
     return ENCODER.encode(this);
   }
 
-  /** Deserializes a ${schema.getName()} from a ByteBuffer. */
+  /** Deserializes a TestRecordWithLogicalTypes from a ByteBuffer. */
   public static TestRecordWithLogicalTypes fromByteBuffer(java.nio.ByteBuffer b) throws java.io.IOException {
     return DECODER.decode(b);
   }
@@ -54,13 +70,13 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
   @Deprecated
   public java.lang.CharSequence s;
   @Deprecated
-  public org.joda.time.LocalDate d;
+  public java.time.LocalDate d;
   @Deprecated
-  public org.joda.time.LocalTime t;
+  public java.time.LocalTime t;
   @Deprecated
-  public org.joda.time.DateTime ts;
+  public java.time.Instant ts;
   @Deprecated
-  public BigDecimal dec;
+  public java.math.BigDecimal dec;
 
   /**
    * Default constructor. Note that this does not initialize fields to their
@@ -72,10 +88,21 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
   /**
    * 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) {
+      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;
@@ -122,6 +149,19 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
     }
   }
 
+  protected static final org.apache.avro.Conversions.DecimalConversion DECIMAL_CONVERSION = new org.apache.avro.Conversions.DecimalConversion();
+  protected static final TimeConversions.DateConversion DATE_CONVERSION = new TimeConversions.DateConversion();
+  protected static final TimeConversions.TimeMillisConversion TIME_CONVERSION = new TimeConversions.TimeMillisConversion();
+  protected static final TimeConversions.TimestampMillisConversion TIMESTAMP_CONVERSION = new TimeConversions.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")
   @Override
@@ -146,16 +186,16 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
       s = (java.lang.CharSequence) value$;
       break;
     case 6:
-      d = (org.joda.time.LocalDate) value$;
+      d = (java.time.LocalDate) value$;
       break;
     case 7:
-      t = (org.joda.time.LocalTime) value$;
+      t = (java.time.LocalTime) value$;
       break;
     case 8:
-      ts = (org.joda.time.DateTime) value$;
+      ts = (java.time.Instant) value$;
       break;
     case 9:
-      dec = (BigDecimal) value$;
+      dec = (java.math.BigDecimal) value$;
       break;
     default:
       throw new org.apache.avro.AvroRuntimeException("Bad index");
@@ -164,6 +204,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
   /**
    * Gets the value of the 'b' field.
+   *
+   * @return The value of the 'b' field.
    */
   public java.lang.Boolean getB() {
     return b;
@@ -180,6 +222,8 @@ 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;
@@ -196,6 +240,8 @@ 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;
@@ -212,6 +258,8 @@ 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;
@@ -228,6 +276,8 @@ 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;
@@ -244,6 +294,8 @@ 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;
@@ -260,8 +312,10 @@ 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;
   }
 
@@ -270,14 +324,16 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
    *
    * @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;
   }
 
@@ -286,73 +342,83 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
    *
    * @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 JodaTimeConversions.DateConversion DATE_CONVERSION = new JodaTimeConversions.DateConversion();
-  protected static final JodaTimeConversions.TimeConversion TIME_CONVERSION = new JodaTimeConversions.TimeConversion();
-  protected static final JodaTimeConversions.TimestampConversion TIMESTAMP_CONVERSION = new JodaTimeConversions.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 */
+  /**
+   * Creates a new TestRecordWithLogicalTypes RecordBuilder.
+   *
+   * @return A new TestRecordWithLogicalTypes RecordBuilder
+   */
   public static TestRecordWithLogicalTypes.Builder newBuilder() {
     return new TestRecordWithLogicalTypes.Builder();
   }
 
   /**
    * Creates a new TestRecordWithLogicalTypes RecordBuilder by copying an existing
-   * Builder
+   * Builder.
+   *
+   * @param other The existing builder to copy.
+   * @return A new TestRecordWithLogicalTypes RecordBuilder
    */
   public static TestRecordWithLogicalTypes.Builder newBuilder(TestRecordWithLogicalTypes.Builder other) {
-    return new TestRecordWithLogicalTypes.Builder(other);
+    if (other == null) {
+      return new TestRecordWithLogicalTypes.Builder();
+    } else {
+      return new TestRecordWithLogicalTypes.Builder(other);
+    }
   }
 
   /**
    * Creates a new TestRecordWithLogicalTypes RecordBuilder by copying an existing
-   * TestRecordWithLogicalTypes instance
+   * TestRecordWithLogicalTypes instance.
+   *
+   * @param other The existing instance to copy.
+   * @return A new TestRecordWithLogicalTypes RecordBuilder
    */
   public static TestRecordWithLogicalTypes.Builder newBuilder(TestRecordWithLogicalTypes other) {
-    return new TestRecordWithLogicalTypes.Builder(other);
+    if (other == null) {
+      return new TestRecordWithLogicalTypes.Builder();
+    } else {
+      return new TestRecordWithLogicalTypes.Builder(other);
+    }
   }
 
   /**
@@ -367,66 +433,72 @@ 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 */
+    /**
+     * Creates a Builder by copying an existing Builder.
+     *
+     * @param other The existing Builder to copy.
+     */
     private Builder(TestRecordWithLogicalTypes.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
+     *
+     * @param other The existing instance to copy.
      */
     private Builder(TestRecordWithLogicalTypes other) {
-      super(TestRecordWithLogicalTypes.SCHEMA$);
+      super(SCHEMA$);
       if (isValidValue(fields()[0], other.b)) {
         this.b = data().deepCopy(fields()[0].schema(), other.b);
         fieldSetFlags()[0] = true;
@@ -471,6 +543,8 @@ 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;
@@ -479,7 +553,8 @@ 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) {
       validate(fields()[0], value);
@@ -490,6 +565,8 @@ 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];
@@ -497,6 +574,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Clears the value of the 'b' field.
+     *
+     * @return This builder.
      */
     public TestRecordWithLogicalTypes.Builder clearB() {
       fieldSetFlags()[0] = false;
@@ -505,6 +584,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Gets the value of the 'i32' field.
+     *
+     * @return The value.
      */
     public java.lang.Integer getI32() {
       return i32;
@@ -513,7 +594,8 @@ 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) {
       validate(fields()[1], value);
@@ -524,6 +606,8 @@ 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];
@@ -531,6 +615,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Clears the value of the 'i32' field.
+     *
+     * @return This builder.
      */
     public TestRecordWithLogicalTypes.Builder clearI32() {
       fieldSetFlags()[1] = false;
@@ -539,6 +625,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Gets the value of the 'i64' field.
+     *
+     * @return The value.
      */
     public java.lang.Long getI64() {
       return i64;
@@ -547,7 +635,8 @@ 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) {
       validate(fields()[2], value);
@@ -558,6 +647,8 @@ 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];
@@ -565,6 +656,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Clears the value of the 'i64' field.
+     *
+     * @return This builder.
      */
     public TestRecordWithLogicalTypes.Builder clearI64() {
       fieldSetFlags()[2] = false;
@@ -573,6 +666,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Gets the value of the 'f32' field.
+     *
+     * @return The value.
      */
     public java.lang.Float getF32() {
       return f32;
@@ -581,7 +676,8 @@ 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) {
       validate(fields()[3], value);
@@ -592,6 +688,8 @@ 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];
@@ -599,6 +697,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Clears the value of the 'f32' field.
+     *
+     * @return This builder.
      */
     public TestRecordWithLogicalTypes.Builder clearF32() {
       fieldSetFlags()[3] = false;
@@ -607,6 +707,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Gets the value of the 'f64' field.
+     *
+     * @return The value.
      */
     public java.lang.Double getF64() {
       return f64;
@@ -615,7 +717,8 @@ 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) {
       validate(fields()[4], value);
@@ -626,6 +729,8 @@ 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];
@@ -633,6 +738,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Clears the value of the 'f64' field.
+     *
+     * @return This builder.
      */
     public TestRecordWithLogicalTypes.Builder clearF64() {
       fieldSetFlags()[4] = false;
@@ -641,6 +748,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Gets the value of the 's' field.
+     *
+     * @return The value.
      */
     public java.lang.CharSequence getS() {
       return s;
@@ -649,7 +758,8 @@ 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) {
       validate(fields()[5], value);
@@ -660,6 +770,8 @@ 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];
@@ -667,6 +779,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Clears the value of the 's' field.
+     *
+     * @return This builder.
      */
     public TestRecordWithLogicalTypes.Builder clearS() {
       s = null;
@@ -676,17 +790,20 @@ 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 TestRecordWithLogicalTypes.Builder setD(java.time.LocalDate value) {
       validate(fields()[6], value);
       this.d = value;
       fieldSetFlags()[6] = true;
@@ -695,6 +812,8 @@ 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];
@@ -702,6 +821,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Clears the value of the 'd' field.
+     *
+     * @return This builder.
      */
     public TestRecordWithLogicalTypes.Builder clearD() {
       fieldSetFlags()[6] = false;
@@ -710,17 +831,20 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * 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 TestRecordWithLogicalTypes.Builder setT(java.time.LocalTime value) {
       validate(fields()[7], value);
       this.t = value;
       fieldSetFlags()[7] = true;
@@ -729,6 +853,8 @@ 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];
@@ -736,6 +862,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Clears the value of the 't' field.
+     *
+     * @return This builder.
      */
     public TestRecordWithLogicalTypes.Builder clearT() {
       fieldSetFlags()[7] = false;
@@ -744,17 +872,20 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * 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 TestRecordWithLogicalTypes.Builder setTs(java.time.Instant value) {
       validate(fields()[8], value);
       this.ts = value;
       fieldSetFlags()[8] = true;
@@ -763,6 +894,8 @@ 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];
@@ -770,6 +903,8 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
 
     /**
      * Clears the value of the 'ts' field.
+     *
+     * @return This builder.
      */
     public TestRecordWithLogicalTypes.Builder clearTs() {
       fieldSetFlags()[8] = false;
@@ -777,40 +912,49 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
     }
 
     /**
-     * Gedec the value of the 'dec' field.
+     * Gets the value of the 'dec' field.
+     *
+     * @return The value.
      */
-    public BigDecimal getDec() {
+    public java.math.BigDecimal getDec() {
       return dec;
     }
 
     /**
-     * Sedec the value of the 'dec' field.
+     * Sets the value of the 'dec' field.
      *
-     * @param value the value to set.
+     * @param value The value of 'dec'.
+     * @return This builder.
      */
-    public TestRecordWithLogicalTypes.Builder setDec(BigDecimal value) {
-      validate(fields()[8], value);
+    public TestRecordWithLogicalTypes.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.
+     *
+     * @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.
+     *
+     * @return This builder.
      */
     public TestRecordWithLogicalTypes.Builder clearDec() {
-      fieldSetFlags()[8] = false;
+      dec = null;
+      fieldSetFlags()[9] = false;
       return this;
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public TestRecordWithLogicalTypes build() {
       try {
         TestRecordWithLogicalTypes record = new TestRecordWithLogicalTypes();
@@ -820,31 +964,33 @@ public class TestRecordWithLogicalTypes extends org.apache.avro.specific.Specifi
         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]);
+        record.d = fieldSetFlags()[6] ? this.d : (java.time.LocalDate) defaultValue(fields()[6]);
+        record.t = fieldSetFlags()[7] ? this.t : (java.time.LocalTime) defaultValue(fields()[7]);
+        record.ts = fieldSetFlags()[8] ? this.ts : (java.time.Instant) defaultValue(fields()[8]);
+        record.dec = fieldSetFlags()[9] ? this.dec : (java.math.BigDecimal) defaultValue(fields()[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<TestRecordWithLogicalTypes> WRITER$ = (org.apache.avro.io.DatumWriter<TestRecordWithLogicalTypes>) 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<TestRecordWithLogicalTypes> READER$ = (org.apache.avro.io.DatumReader<TestRecordWithLogicalTypes>) 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/TestSpecificLogicalTypes.java b/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java
deleted file mode 100644
index 43d6973..0000000
--- a/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * 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
- *
- *     https://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.specific;
-
-import static java.time.format.DateTimeFormatter.ISO_INSTANT;
-import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
-import static org.hamcrest.Matchers.*;
-
-import java.io.File;
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeFormatterBuilder;
-import java.time.temporal.ChronoField;
-import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.avro.Conversions;
-import org.apache.avro.LogicalTypes;
-import org.apache.avro.Schema;
-import org.apache.avro.data.JodaTimeConversions.DateConversion;
-import org.apache.avro.data.JodaTimeConversions.TimeConversion;
-import org.apache.avro.data.JodaTimeConversions.TimestampConversion;
-import org.apache.avro.file.DataFileReader;
-import org.apache.avro.file.DataFileWriter;
-import org.apache.avro.file.FileReader;
-import org.apache.avro.io.DatumReader;
-import org.apache.avro.io.DatumWriter;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.joda.time.LocalDate;
-import org.joda.time.LocalTime;
-import org.joda.time.chrono.ISOChronology;
-import org.joda.time.format.ISODateTimeFormat;
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-/**
- * This tests compatibility between classes generated before and after
- * AVRO-1684. TestRecordWithoutLogicalTypes and TestRecordWithLogicalTypes were
- * generated from the same schema, found in
- * src/test/resources/record_with_logical_types.avsc, and
- * TestRecordWithoutLogicalTypes was renamed to avoid the conflict.
- *
- * The classes should not be re-generated because they test compatibility of
- * 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 {
-
-  // Override the default ISO_LOCAL_TIME to make sure that there are
-  // trailing zero's in the format:
-  // Expected: is "22:07:33.880"
-  // but: was "22:07:33.88"
-  private static final DateTimeFormatter ISO_LOCAL_TIME = new DateTimeFormatterBuilder()
-      .appendValue(ChronoField.HOUR_OF_DAY, 2).appendLiteral(':').appendValue(ChronoField.MINUTE_OF_HOUR, 2)
-      .optionalStart().appendLiteral(':').appendValue(ChronoField.SECOND_OF_MINUTE, 2).optionalStart()
-      .appendFraction(ChronoField.NANO_OF_SECOND, 3, 3, true).toFormatter();
-
-  @Rule
-  public final TemporaryFolder temp = new TemporaryFolder();
-
-  @Test
-  public void testRecordWithLogicalTypes() throws IOException {
-    TestRecordWithLogicalTypes record = 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, RoundingMode.HALF_DOWN));
-
-    File data = write(TestRecordWithLogicalTypes.getClassSchema(), record);
-    List<TestRecordWithLogicalTypes> actual = read(TestRecordWithLogicalTypes.getClassSchema(), data);
-
-    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().truncatedTo(ChronoUnit.MILLIS),
-        java.time.Instant.now().truncatedTo(ChronoUnit.MILLIS),
-        new BigDecimal(123.45f).setScale(2, RoundingMode.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(),
-        // There is no reliable way to get fixed width string from ISO_INSTANT below
-        // for granularity less than one second second.
-        new DateTime((System.currentTimeMillis() / 1000) * 1000, ISOChronology.getInstance())
-            .withZone(DateTimeZone.UTC),
-        new BigDecimal(123.45f).setScale(2, RoundingMode.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.dateTimeNoMillis().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().truncatedTo(ChronoUnit.MILLIS),
-        java.time.Instant.now().truncatedTo(ChronoUnit.MILLIS),
-        new BigDecimal(123.45f).setScale(2, RoundingMode.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 {
-    // the significance of the record without logical types is that it has the
-    // same schema (besides record name) as the one with logical types,
-    // including the type annotations. this verifies that the type annotations
-    // are only applied if the record was compiled to use those types. this
-    // ensures compatibility with already-compiled code.
-
-    TestRecordWithoutLogicalTypes record = new TestRecordWithoutLogicalTypes(true, 34, 35L, 3.14F, 3019.34, null,
-        new DateConversion().toInt(LocalDate.now(), null, null),
-        new TimeConversion().toInt(LocalTime.now(), null, null),
-        new TimestampConversion().toLong(DateTime.now().withZone(DateTimeZone.UTC), null, null),
-        new Conversions.DecimalConversion().toBytes(new BigDecimal(123.45f).setScale(2, RoundingMode.HALF_DOWN), null,
-            LogicalTypes.decimal(9, 2)));
-
-    File data = write(TestRecordWithoutLogicalTypes.getClassSchema(), record);
-    List<TestRecordWithoutLogicalTypes> actual = read(TestRecordWithoutLogicalTypes.getClassSchema(), data);
-
-    Assert.assertEquals("Should match written record", record, actual.get(0));
-  }
-
-  @Test
-  public void testRecordWritePrimitivesReadLogicalTypes() throws IOException {
-    LocalDate date = LocalDate.now();
-    LocalTime time = LocalTime.now();
-    DateTime timestamp = DateTime.now().withZone(DateTimeZone.UTC);
-    BigDecimal decimal = new BigDecimal(123.45f).setScale(2, RoundingMode.HALF_DOWN);
-
-    TestRecordWithoutLogicalTypes record = new TestRecordWithoutLogicalTypes(true, 34, 35L, 3.14F, 3019.34, null,
-        new DateConversion().toInt(date, null, null), new TimeConversion().toInt(time, null, null),
-        new TimestampConversion().toLong(timestamp, null, null),
-        new Conversions.DecimalConversion().toBytes(decimal, null, LogicalTypes.decimal(9, 2)));
-
-    File data = write(TestRecordWithoutLogicalTypes.getClassSchema(), record);
-    // read using the schema with logical types
-    List<TestRecordWithLogicalTypes> actual = read(TestRecordWithLogicalTypes.getClassSchema(), data);
-
-    TestRecordWithLogicalTypes expected = new TestRecordWithLogicalTypes(true, 34, 35L, 3.14F, 3019.34, null, date,
-        time, timestamp, decimal);
-
-    Assert.assertEquals("Should match written record", expected, actual.get(0));
-  }
-
-  @Test
-  public void testRecordWriteLogicalTypesReadPrimitives() throws IOException {
-    LocalDate date = LocalDate.now();
-    LocalTime time = LocalTime.now();
-    DateTime timestamp = DateTime.now().withZone(DateTimeZone.UTC);
-    BigDecimal decimal = new BigDecimal(123.45f).setScale(2, RoundingMode.HALF_DOWN);
-
-    TestRecordWithLogicalTypes record = new TestRecordWithLogicalTypes(true, 34, 35L, 3.14F, 3019.34, null, date, time,
-        timestamp, decimal);
-
-    File data = write(TestRecordWithLogicalTypes.getClassSchema(), record);
-    // read using the schema with logical types
-    List<TestRecordWithoutLogicalTypes> actual = read(TestRecordWithoutLogicalTypes.getClassSchema(), data);
-
-    TestRecordWithoutLogicalTypes expected = new TestRecordWithoutLogicalTypes(true, 34, 35L, 3.14F, 3019.34, null,
-        new DateConversion().toInt(date, null, null), new TimeConversion().toInt(time, null, null),
-        new TimestampConversion().toLong(timestamp, null, null),
-        new Conversions.DecimalConversion().toBytes(decimal, null, LogicalTypes.decimal(9, 2)));
-
-    Assert.assertEquals("Should match written record", expected, actual.get(0));
-  }
-
-  private <D> List<D> read(Schema schema, File file) throws IOException {
-    DatumReader<D> reader = newReader(schema);
-    List<D> data = new ArrayList<>();
-
-    try (FileReader<D> fileReader = new DataFileReader<>(file, reader)) {
-      for (D datum : fileReader) {
-        data.add(datum);
-      }
-    }
-
-    return data;
-  }
-
-  @SuppressWarnings("unchecked")
-  private <D> DatumReader<D> newReader(Schema schema) {
-    return SpecificData.get().createDatumReader(schema);
-  }
-
-  @SuppressWarnings("unchecked")
-  private <D extends SpecificRecord> File write(Schema schema, D... data) throws IOException {
-    File file = temp.newFile();
-    DatumWriter<D> writer = SpecificData.get().createDatumWriter(schema);
-
-    try (DataFileWriter<D> fileWriter = new DataFileWriter<>(writer)) {
-      fileWriter.create(schema, file);
-      for (D datum : data) {
-        fileWriter.append(datum);
-      }
-    }
-
-    return file;
-  }
-}
diff --git a/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificToFromByteArray.java b/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificToFromByteArray.java
index 5ea4a4a..9bf4005 100644
--- a/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificToFromByteArray.java
+++ b/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificToFromByteArray.java
@@ -17,66 +17,69 @@
  */
 package org.apache.avro.specific;
 
-import static org.junit.Assert.assertEquals;
+import org.apache.avro.Conversions;
+import org.apache.avro.LogicalTypes;
+import org.apache.avro.message.MissingSchemaException;
+import org.junit.Test;
 
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.nio.ByteBuffer;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.temporal.ChronoUnit;
 
-import org.apache.avro.Conversions;
-import org.apache.avro.LogicalTypes;
-import org.apache.avro.data.JodaTimeConversions;
-import org.apache.avro.message.MissingSchemaException;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.joda.time.LocalDate;
-import org.joda.time.LocalTime;
-import org.junit.Test;
+import static org.junit.Assert.assertEquals;
 
 public class TestSpecificToFromByteArray {
+
   @Test
   public void testSpecificToFromByteBufferWithLogicalTypes() throws IOException {
-    TestRecordWithLogicalTypes record = new TestRecordWithLogicalTypes(true, 34, 35L, 3.14F, 3019.34, null,
-        LocalDate.now(), LocalTime.now(), DateTime.now().withZone(DateTimeZone.UTC), new BigDecimal("123.45"));
+    // Java 9+ comes with NANO precision and since we encode it using millis
+    // precision
+    // Therefore we won't want to have NANOs in the input
+    LocalTime t = LocalTime.now().truncatedTo(ChronoUnit.MILLIS);
+    Instant instant = Instant.now().truncatedTo(ChronoUnit.MILLIS);
+
+    final TestRecordWithLogicalTypes record = new TestRecordWithLogicalTypes(true, 34, 35L, 3.14F, 3019.34, null,
+        LocalDate.now(), t, instant, new BigDecimal("123.45"));
 
-    ByteBuffer b = record.toByteBuffer();
-    TestRecordWithLogicalTypes copy = TestRecordWithLogicalTypes.fromByteBuffer(b);
+    final ByteBuffer b = record.toByteBuffer();
+    final TestRecordWithLogicalTypes copy = TestRecordWithLogicalTypes.fromByteBuffer(b);
 
     assertEquals(record, copy);
   }
 
   @Test
   public void testSpecificToFromByteBufferWithoutLogicalTypes() throws IOException {
-    TestRecordWithoutLogicalTypes record = new TestRecordWithoutLogicalTypes(true, 34, 35L, 3.14F, 3019.34, null,
-        new JodaTimeConversions.DateConversion().toInt(LocalDate.now(), null, null),
-        new JodaTimeConversions.TimeConversion().toInt(LocalTime.now(), null, null),
-        new JodaTimeConversions.TimestampConversion().toLong(DateTime.now().withZone(DateTimeZone.UTC), null, null),
+    final TestRecordWithoutLogicalTypes record = new TestRecordWithoutLogicalTypes(true, 34, 35L, 3.14F, 3019.34, null,
+        (int) System.currentTimeMillis() / 1000, (int) System.currentTimeMillis() / 1000, System.currentTimeMillis(),
         new Conversions.DecimalConversion().toBytes(new BigDecimal("123.45"), null, LogicalTypes.decimal(9, 2)));
 
-    ByteBuffer b = record.toByteBuffer();
-    TestRecordWithoutLogicalTypes copy = TestRecordWithoutLogicalTypes.fromByteBuffer(b);
+    final ByteBuffer b = record.toByteBuffer();
+    final TestRecordWithoutLogicalTypes copy = TestRecordWithoutLogicalTypes.fromByteBuffer(b);
 
     assertEquals(record, copy);
   }
 
   @Test(expected = MissingSchemaException.class)
   public void testSpecificByteArrayIncompatibleWithLogicalTypes() throws IOException {
-    TestRecordWithoutLogicalTypes withoutLogicalTypes = new TestRecordWithoutLogicalTypes(true, 34, 35L, 3.14F, 3019.34,
-        null, new JodaTimeConversions.DateConversion().toInt(LocalDate.now(), null, null),
-        new JodaTimeConversions.TimeConversion().toInt(LocalTime.now(), null, null),
-        new JodaTimeConversions.TimestampConversion().toLong(DateTime.now().withZone(DateTimeZone.UTC), null, null),
+    final TestRecordWithoutLogicalTypes withoutLogicalTypes = new TestRecordWithoutLogicalTypes(true, 34, 35L, 3.14F,
+        3019.34, null, (int) System.currentTimeMillis() / 1000, (int) System.currentTimeMillis() / 1000,
+        System.currentTimeMillis(),
         new Conversions.DecimalConversion().toBytes(new BigDecimal("123.45"), null, LogicalTypes.decimal(9, 2)));
 
-    ByteBuffer b = withoutLogicalTypes.toByteBuffer();
+    final ByteBuffer b = withoutLogicalTypes.toByteBuffer();
     TestRecordWithLogicalTypes.fromByteBuffer(b);
   }
 
   @Test(expected = MissingSchemaException.class)
   public void testSpecificByteArrayIncompatibleWithoutLogicalTypes() throws IOException {
-    TestRecordWithLogicalTypes withLogicalTypes = new TestRecordWithLogicalTypes(true, 34, 35L, 3.14F, 3019.34, null,
-        LocalDate.now(), LocalTime.now(), DateTime.now().withZone(DateTimeZone.UTC), new BigDecimal("123.45"));
+    final TestRecordWithLogicalTypes withLogicalTypes = new TestRecordWithLogicalTypes(true, 34, 35L, 3.14F, 3019.34,
+        null, LocalDate.now(), LocalTime.now(), Instant.now(), new BigDecimal("123.45"));
 
-    ByteBuffer b = withLogicalTypes.toByteBuffer();
+    final ByteBuffer b = withLogicalTypes.toByteBuffer();
     TestRecordWithoutLogicalTypes.fromByteBuffer(b);
   }
 }
diff --git a/lang/java/avro/src/test/resources/record_with_logical_types.avsc b/lang/java/avro/src/test/resources/TestRecordWithLogicalTypes.avsc
similarity index 100%
rename from lang/java/avro/src/test/resources/record_with_logical_types.avsc
rename to lang/java/avro/src/test/resources/TestRecordWithLogicalTypes.avsc
diff --git a/lang/java/compiler/pom.xml b/lang/java/compiler/pom.xml
index 533ff25..cffcf81 100644
--- a/lang/java/compiler/pom.xml
+++ b/lang/java/compiler/pom.xml
@@ -211,10 +211,6 @@
       <!-- can only be used from within ant -->
       <scope>provided</scope>
     </dependency>
-    <dependency>
-      <groupId>joda-time</groupId>
-      <artifactId>joda-time</artifactId>
-    </dependency>
   </dependencies>
 
 </project>
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 e0d86bd..eb35e1e 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,7 +23,6 @@ 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;
@@ -37,7 +36,6 @@ public class ProtocolTask extends Task {
   private File src;
   private File dest = new File(".");
   private StringType stringType = StringType.CharSequence;
-  private DateTimeLogicalTypeImplementation dateTimeLogicalTypeImplementation = DateTimeLogicalTypeImplementation.DEFAULT;
 
   private final ArrayList<FileSet> filesets = new ArrayList<>();
 
@@ -61,17 +59,6 @@ public class ProtocolTask extends Task {
     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);
@@ -99,7 +86,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, getDateTimeLogicalTypeImplementation());
+    SpecificCompiler compiler = new SpecificCompiler(protocol);
     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 7cf5bc6..49280e5 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
@@ -26,9 +26,9 @@ import org.apache.avro.Schema;
 public class SchemaTask extends ProtocolTask {
   @Override
   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, getDateTimeLogicalTypeImplementation());
+    final Schema.Parser parser = new Schema.Parser();
+    final Schema schema = parser.parse(src);
+    final SpecificCompiler compiler = new SpecificCompiler(schema);
     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 9320480..14d5a3e 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
@@ -39,7 +39,6 @@ import org.apache.avro.Conversion;
 import org.apache.avro.Conversions;
 import org.apache.avro.LogicalTypes;
 import org.apache.avro.data.TimeConversions;
-import org.apache.avro.data.JodaTimeConversions;
 import org.apache.avro.specific.SpecificData;
 
 import org.apache.avro.Protocol;
@@ -96,29 +95,12 @@ public class SpecificCompiler {
     PUBLIC, PUBLIC_DEPRECATED, PRIVATE
   }
 
-  public enum DateTimeLogicalTypeImplementation {
-    JODA {
-      @Override
-      void addLogicalTypeConversions(SpecificData specificData) {
-        specificData.addLogicalTypeConversion(new JodaTimeConversions.DateConversion());
-        specificData.addLogicalTypeConversion(new JodaTimeConversions.TimeConversion());
-        specificData.addLogicalTypeConversion(new JodaTimeConversions.TimestampConversion());
-      }
-    },
-    JSR310 {
-      @Override
-      void addLogicalTypeConversions(SpecificData specificData) {
-        specificData.addLogicalTypeConversion(new TimeConversions.DateConversion());
-        specificData.addLogicalTypeConversion(new TimeConversions.TimeMillisConversion());
-        specificData.addLogicalTypeConversion(new TimeConversions.TimeMicrosConversion());
-        specificData.addLogicalTypeConversion(new TimeConversions.TimestampMillisConversion());
-        specificData.addLogicalTypeConversion(new TimeConversions.TimestampMicrosConversion());
-      }
-    };
-
-    public static final DateTimeLogicalTypeImplementation DEFAULT = JSR310;
-
-    abstract void addLogicalTypeConversions(SpecificData specificData);
+  void addLogicalTypeConversions(SpecificData specificData) {
+    specificData.addLogicalTypeConversion(new TimeConversions.DateConversion());
+    specificData.addLogicalTypeConversion(new TimeConversions.TimeMillisConversion());
+    specificData.addLogicalTypeConversion(new TimeConversions.TimeMicrosConversion());
+    specificData.addLogicalTypeConversion(new TimeConversions.TimestampMillisConversion());
+    specificData.addLogicalTypeConversion(new TimeConversions.TimestampMicrosConversion());
   }
 
   private final SpecificData specificData = new SpecificData();
@@ -134,7 +116,6 @@ public class SpecificCompiler {
   private boolean createAllArgsConstructor = true;
   private String outputCharacterEncoding;
   private boolean enableDecimalLogicalType = false;
-  private final DateTimeLogicalTypeImplementation dateTimeLogicalTypeImplementation;
   private String suffix = ".java";
   private List<Object> additionalVelocityTools = new ArrayList<>();
 
@@ -164,11 +145,7 @@ public class SpecificCompiler {
       + " * DO NOT EDIT DIRECTLY\n" + " */\n";
 
   public SpecificCompiler(Protocol protocol) {
-    this(protocol, DateTimeLogicalTypeImplementation.DEFAULT);
-  }
-
-  public SpecificCompiler(Protocol protocol, DateTimeLogicalTypeImplementation dateTimeLogicalTypeImplementation) {
-    this(dateTimeLogicalTypeImplementation);
+    this();
     // enqueue all types
     for (Schema s : protocol.getTypes()) {
       enqueue(s);
@@ -177,36 +154,16 @@ public class SpecificCompiler {
   }
 
   public SpecificCompiler(Schema schema) {
-    this(schema, DateTimeLogicalTypeImplementation.DEFAULT);
-  }
-
-  public SpecificCompiler(Schema schema, DateTimeLogicalTypeImplementation dateTimeLogicalTypeImplementation) {
-    this(dateTimeLogicalTypeImplementation);
+    this();
     enqueue(schema);
     this.protocol = null;
   }
 
   /**
-   * Creates a specific compiler with the default (JSR310) type for date/time
-   * related logical types.
-   *
-   * @see #SpecificCompiler(DateTimeLogicalTypeImplementation)
-   */
-  SpecificCompiler() {
-    this(DateTimeLogicalTypeImplementation.DEFAULT);
-  }
-
-  /**
    * 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
+   * logical types.
    */
-  SpecificCompiler(DateTimeLogicalTypeImplementation dateTimeLogicalTypeImplementation) {
-    this.dateTimeLogicalTypeImplementation = dateTimeLogicalTypeImplementation;
+  SpecificCompiler() {
     this.templateDir = System.getProperty("org.apache.avro.specific.templates",
         "/org/apache/avro/compiler/specific/templates/java/classic/");
     initializeVelocity();
@@ -305,10 +262,6 @@ public class SpecificCompiler {
     this.enableDecimalLogicalType = enableDecimalLogicalType;
   }
 
-  public DateTimeLogicalTypeImplementation getDateTimeLogicalTypeImplementation() {
-    return dateTimeLogicalTypeImplementation;
-  }
-
   public void addCustomConversion(Class<?> conversionClass) {
     try {
       final Conversion<?> conversion = (Conversion<?>) conversionClass.getDeclaredConstructor().newInstance();
@@ -398,7 +351,7 @@ public class SpecificCompiler {
   }
 
   private void initializeSpecificData() {
-    dateTimeLogicalTypeImplementation.addLogicalTypeConversions(specificData);
+    addLogicalTypeConversions(specificData);
     specificData.addLogicalTypeConversion(new Conversions.DecimalConversion());
   }
 
@@ -475,7 +428,7 @@ public class SpecificCompiler {
 
     for (File src : srcFiles) {
       Schema schema = parser.parse(src);
-      SpecificCompiler compiler = new SpecificCompiler(schema, DateTimeLogicalTypeImplementation.DEFAULT);
+      SpecificCompiler compiler = new SpecificCompiler(schema);
       compiler.compileToDestination(src, dest);
     }
   }
@@ -976,16 +929,17 @@ public class SpecificCompiler {
 
   /** Utility for template use. Returns the java annotations for a schema. */
   public String[] javaAnnotations(JsonProperties props) {
-    Object value = props.getObjectProp("javaAnnotation");
+    final Object value = props.getObjectProp("javaAnnotation");
     if (value == null)
       return new String[0];
     if (value instanceof String)
       return new String[] { value.toString() };
     if (value instanceof List) {
-      List<?> list = (List<?>) value;
-      List<String> annots = new ArrayList<>();
-      for (Object o : list)
+      final List<?> list = (List<?>) value;
+      final List<String> annots = new ArrayList<>();
+      for (Object o : list) {
         annots.add(o.toString());
+      }
       return annots.toArray(new String[0]);
     }
     return new String[0];
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 5e5c985..2fed7fe 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,8 +17,6 @@
  */
 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;
@@ -134,14 +132,9 @@ public class TestSpecificCompiler {
   }
 
   private SpecificCompiler createCompiler() throws IOException {
-    return createCompiler(JSR310);
-  }
-
-  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, dateTimeLogicalTypeImplementation);
+    SpecificCompiler compiler = new SpecificCompiler(schema);
     String velocityTemplateDir = "src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/";
     compiler.setTemplateDir(velocityTemplateDir);
     compiler.setStringType(StringType.CharSequence);
@@ -346,7 +339,7 @@ public class TestSpecificCompiler {
 
   @Test
   public void testJavaTypeWithDecimalLogicalTypeEnabled() throws Exception {
-    SpecificCompiler compiler = createCompiler(JODA);
+    SpecificCompiler compiler = createCompiler();
     compiler.setEnableDecimalLogicalType(true);
 
     Schema dateSchema = LogicalTypes.date().addToSchema(Schema.create(Schema.Type.INT));
@@ -359,11 +352,10 @@ public class TestSpecificCompiler {
     // Decimal type target class depends on configuration
     // UUID should always be CharSequence since we haven't added its
     // support in SpecificRecord
-    Assert.assertEquals("Should use Joda LocalDate for date type", "org.joda.time.LocalDate",
-        compiler.javaType(dateSchema));
-    Assert.assertEquals("Should use Joda LocalTime for time-millis type", "org.joda.time.LocalTime",
+    Assert.assertEquals("Should use LocalDate for date type", "java.time.LocalDate", compiler.javaType(dateSchema));
+    Assert.assertEquals("Should use LocalTime for time-millis type", "java.time.LocalTime",
         compiler.javaType(timeSchema));
-    Assert.assertEquals("Should use Joda DateTime for timestamp-millis type", "org.joda.time.DateTime",
+    Assert.assertEquals("Should use DateTime for timestamp-millis type", "java.time.Instant",
         compiler.javaType(timestampSchema));
     Assert.assertEquals("Should use Java BigDecimal type", "java.math.BigDecimal", compiler.javaType(decimalSchema));
     Assert.assertEquals("Should use Java CharSequence type", "java.lang.CharSequence", compiler.javaType(uuidSchema));
@@ -371,7 +363,7 @@ public class TestSpecificCompiler {
 
   @Test
   public void testJavaTypeWithDecimalLogicalTypeDisabled() throws Exception {
-    SpecificCompiler compiler = createCompiler(JODA);
+    SpecificCompiler compiler = createCompiler();
     compiler.setEnableDecimalLogicalType(false);
 
     Schema dateSchema = LogicalTypes.date().addToSchema(Schema.create(Schema.Type.INT));
@@ -384,19 +376,18 @@ public class TestSpecificCompiler {
     // Decimal type target class depends on configuration
     // UUID should always be CharSequence since we haven't added its
     // support in SpecificRecord
-    Assert.assertEquals("Should use Joda LocalDate for date type", "org.joda.time.LocalDate",
-        compiler.javaType(dateSchema));
-    Assert.assertEquals("Should use Joda LocalTime for time-millis type", "org.joda.time.LocalTime",
+    Assert.assertEquals("Should use LocalDate for date type", "java.time.LocalDate", compiler.javaType(dateSchema));
+    Assert.assertEquals("Should use LocalTime for time-millis type", "java.time.LocalTime",
         compiler.javaType(timeSchema));
-    Assert.assertEquals("Should use Joda DateTime for timestamp-millis type", "org.joda.time.DateTime",
+    Assert.assertEquals("Should use DateTime for timestamp-millis type", "java.time.Instant",
         compiler.javaType(timestampSchema));
     Assert.assertEquals("Should use ByteBuffer type", "java.nio.ByteBuffer", compiler.javaType(decimalSchema));
     Assert.assertEquals("Should use Java CharSequence type", "java.lang.CharSequence", compiler.javaType(uuidSchema));
   }
 
   @Test
-  public void testJavaTypeWithJsr310DateTimeTypes() throws Exception {
-    SpecificCompiler compiler = createCompiler(JSR310);
+  public void testJavaTypeWithDateTimeTypes() throws Exception {
+    SpecificCompiler compiler = createCompiler();
 
     Schema dateSchema = LogicalTypes.date().addToSchema(Schema.create(Schema.Type.INT));
     Schema timeSchema = LogicalTypes.timeMillis().addToSchema(Schema.create(Schema.Type.INT));
@@ -419,7 +410,7 @@ public class TestSpecificCompiler {
 
   @Test
   public void testJavaUnbox() throws Exception {
-    SpecificCompiler compiler = createCompiler(JODA);
+    SpecificCompiler compiler = createCompiler();
     compiler.setEnableDecimalLogicalType(false);
 
     Schema intSchema = Schema.create(Schema.Type.INT);
@@ -438,17 +429,16 @@ public class TestSpecificCompiler {
     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 Joda LocalDate for date type", "org.joda.time.LocalDate",
-        compiler.javaUnbox(dateSchema));
-    Assert.assertEquals("Should use Joda LocalTime for time-millis type", "org.joda.time.LocalTime",
+    Assert.assertEquals("Should use LocalDate for date type", "java.time.LocalDate", compiler.javaUnbox(dateSchema));
+    Assert.assertEquals("Should use LocalTime for time-millis type", "java.time.LocalTime",
         compiler.javaUnbox(timeSchema));
-    Assert.assertEquals("Should use Joda DateTime for timestamp-millis type", "org.joda.time.DateTime",
+    Assert.assertEquals("Should use DateTime for timestamp-millis type", "java.time.Instant",
         compiler.javaUnbox(timestampSchema));
   }
 
   @Test
-  public void testJavaUnboxJsr310DateTime() throws Exception {
-    SpecificCompiler compiler = createCompiler(JSR310);
+  public void testJavaUnboxDateTime() throws Exception {
+    SpecificCompiler compiler = createCompiler();
 
     Schema dateSchema = LogicalTypes.date().addToSchema(Schema.create(Schema.Type.INT));
     Schema timeSchema = LogicalTypes.timeMillis().addToSchema(Schema.create(Schema.Type.INT));
@@ -544,67 +534,62 @@ public class TestSpecificCompiler {
 
   @Test
   public void testGetUsedConversionClassesForNullableLogicalTypesInNestedRecord() throws Exception {
-    SpecificCompiler compiler = createCompiler(JODA);
+    SpecificCompiler compiler = createCompiler();
 
     final Schema schema = new Schema.Parser().parse(
         "{\"type\":\"record\",\"name\":\"NestedLogicalTypesRecord\",\"namespace\":\"org.apache.avro.codegentest.testdata\",\"doc\":\"Test nested types with logical types in generated Java classes\",\"fields\":[{\"name\":\"nestedRecord\",\"type\":{\"type\":\"record\",\"name\":\"NestedRecord\",\"fields\":[{\"name\":\"nullableDateField\",\"type\":[\"null\",{\"type\":\"int\",\"logicalType\":\"date\"}]}]}}]}");
 
     final Collection<String> usedConversionClasses = compiler.getUsedConversionClasses(schema);
     Assert.assertEquals(1, usedConversionClasses.size());
-    Assert.assertEquals("org.apache.avro.data.JodaTimeConversions.DateConversion",
-        usedConversionClasses.iterator().next());
+    Assert.assertEquals("org.apache.avro.data.TimeConversions.DateConversion", usedConversionClasses.iterator().next());
   }
 
   @Test
   public void testGetUsedConversionClassesForNullableLogicalTypesInArray() throws Exception {
-    SpecificCompiler compiler = createCompiler(JODA);
+    SpecificCompiler compiler = createCompiler();
 
     final Schema schema = new Schema.Parser().parse(
         "{\"type\":\"record\",\"name\":\"NullableLogicalTypesArray\",\"namespace\":\"org.apache.avro.codegentest.testdata\",\"doc\":\"Test nested types with logical types in generated Java classes\",\"fields\":[{\"name\":\"arrayOfLogicalType\",\"type\":{\"type\":\"array\",\"items\":[\"null\",{\"type\":\"int\",\"logicalType\":\"date\"}]}}]}");
 
     final Collection<String> usedConversionClasses = compiler.getUsedConversionClasses(schema);
     Assert.assertEquals(1, usedConversionClasses.size());
-    Assert.assertEquals("org.apache.avro.data.JodaTimeConversions.DateConversion",
-        usedConversionClasses.iterator().next());
+    Assert.assertEquals("org.apache.avro.data.TimeConversions.DateConversion", usedConversionClasses.iterator().next());
   }
 
   @Test
   public void testGetUsedConversionClassesForNullableLogicalTypesInArrayOfRecords() throws Exception {
-    SpecificCompiler compiler = createCompiler(JODA);
+    SpecificCompiler compiler = createCompiler();
 
     final Schema schema = new Schema.Parser().parse(
         "{\"type\":\"record\",\"name\":\"NestedLogicalTypesArray\",\"namespace\":\"org.apache.avro.codegentest.testdata\",\"doc\":\"Test nested types with logical types in generated Java classes\",\"fields\":[{\"name\":\"arrayOfRecords\",\"type\":{\"type\":\"array\",\"items\":{\"type\":\"record\",\"name\":\"RecordInArray\",\"fields\":[{\"name\":\"nullableDateField\",\"type\":[\"null\",{\"type\":\"int\",\"logicalType\":\"date\"}]}]}}}]}");
 
     final Collection<String> usedConversionClasses = compiler.getUsedConversionClasses(schema);
     Assert.assertEquals(1, usedConversionClasses.size());
-    Assert.assertEquals("org.apache.avro.data.JodaTimeConversions.DateConversion",
-        usedConversionClasses.iterator().next());
+    Assert.assertEquals("org.apache.avro.data.TimeConversions.DateConversion", usedConversionClasses.iterator().next());
   }
 
   @Test
   public void testGetUsedConversionClassesForNullableLogicalTypesInUnionOfRecords() throws Exception {
-    SpecificCompiler compiler = createCompiler(JODA);
+    SpecificCompiler compiler = createCompiler();
 
     final Schema schema = new Schema.Parser().parse(
         "{\"type\":\"record\",\"name\":\"NestedLogicalTypesUnion\",\"namespace\":\"org.apache.avro.codegentest.testdata\",\"doc\":\"Test nested types with logical types in generated Java classes\",\"fields\":[{\"name\":\"unionOfRecords\",\"type\":[\"null\",{\"type\":\"record\",\"name\":\"RecordInUnion\",\"fields\":[{\"name\":\"nullableDateField\",\"type\":[\"null\",{\"type\":\"int\",\"logicalType\":\"date\"}]}]}]}]}");
 
     final Collection<String> usedConversionClasses = compiler.getUsedConversionClasses(schema);
     Assert.assertEquals(1, usedConversionClasses.size());
-    Assert.assertEquals("org.apache.avro.data.JodaTimeConversions.DateConversion",
-        usedConversionClasses.iterator().next());
+    Assert.assertEquals("org.apache.avro.data.TimeConversions.DateConversion", usedConversionClasses.iterator().next());
   }
 
   @Test
   public void testGetUsedConversionClassesForNullableLogicalTypesInMapOfRecords() throws Exception {
-    SpecificCompiler compiler = createCompiler(JODA);
+    SpecificCompiler compiler = createCompiler();
 
     final Schema schema = new Schema.Parser().parse(
         "{\"type\":\"record\",\"name\":\"NestedLogicalTypesMap\",\"namespace\":\"org.apache.avro.codegentest.testdata\",\"doc\":\"Test nested types with logical types in generated Java classes\",\"fields\":[{\"name\":\"mapOfRecords\",\"type\":{\"type\":\"map\",\"values\":{\"type\":\"record\",\"name\":\"RecordInMap\",\"fields\":[{\"name\":\"nullableDateField\",\"type\":[\"null\",{\"type\":\"int\",\"logicalType\":\"date\"}]}]},\"avro.java.string\":\"String\"}}]}");
 
     final Collection<String> usedConversionClasses = compiler.getUsedConversionClasses(schema);
     Assert.assertEquals(1, usedConversionClasses.size());
-    Assert.assertEquals("org.apache.avro.data.JodaTimeConversions.DateConversion",
-        usedConversionClasses.iterator().next());
+    Assert.assertEquals("org.apache.avro.data.TimeConversions.DateConversion", usedConversionClasses.iterator().next());
   }
 
   @Test
@@ -612,7 +597,7 @@ public class TestSpecificCompiler {
     Schema logicalTypesWithMultipleFields = new Schema.Parser()
         .parse(new File("src/test/resources/logical_types_with_multiple_fields.avsc"));
     assertCompilesWithJavaCompiler(new File(OUTPUT_DIR.getRoot(), name.getMethodName()),
-        new SpecificCompiler(logicalTypesWithMultipleFields, JODA).compile(), true);
+        new SpecificCompiler(logicalTypesWithMultipleFields).compile(), true);
   }
 
   @Test
@@ -624,30 +609,30 @@ public class TestSpecificCompiler {
   }
 
   @Test
-  public void testLogicalTypesWithMultipleFieldsJsr310DateTime() throws Exception {
+  public void testLogicalTypesWithMultipleFieldsDateTime() throws Exception {
     Schema logicalTypesWithMultipleFields = new Schema.Parser()
         .parse(new File("src/test/resources/logical_types_with_multiple_fields.avsc"));
     assertCompilesWithJavaCompiler(new File(this.outputFile, name.getMethodName()),
-        new SpecificCompiler(logicalTypesWithMultipleFields, JSR310).compile());
+        new SpecificCompiler(logicalTypesWithMultipleFields).compile());
   }
 
   @Test
   public void testConversionInstanceWithDecimalLogicalTypeDisabled() throws Exception {
-    SpecificCompiler compiler = createCompiler(JODA);
+    final SpecificCompiler compiler = createCompiler();
     compiler.setEnableDecimalLogicalType(false);
 
-    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));
-    Schema decimalSchema = LogicalTypes.decimal(9, 2).addToSchema(Schema.create(Schema.Type.BYTES));
-    Schema uuidSchema = LogicalTypes.uuid().addToSchema(Schema.create(Schema.Type.STRING));
+    final Schema dateSchema = LogicalTypes.date().addToSchema(Schema.create(Schema.Type.INT));
+    final Schema timeSchema = LogicalTypes.timeMillis().addToSchema(Schema.create(Schema.Type.INT));
+    final Schema timestampSchema = LogicalTypes.timestampMillis().addToSchema(Schema.create(Schema.Type.LONG));
+    final Schema decimalSchema = LogicalTypes.decimal(9, 2).addToSchema(Schema.create(Schema.Type.BYTES));
+    final Schema uuidSchema = LogicalTypes.uuid().addToSchema(Schema.create(Schema.Type.STRING));
 
     Assert.assertEquals("Should use date conversion for date type",
-        "new org.apache.avro.data.JodaTimeConversions.DateConversion()", compiler.conversionInstance(dateSchema));
+        "new org.apache.avro.data.TimeConversions.DateConversion()", compiler.conversionInstance(dateSchema));
     Assert.assertEquals("Should use time conversion for time type",
-        "new org.apache.avro.data.JodaTimeConversions.TimeConversion()", compiler.conversionInstance(timeSchema));
+        "new org.apache.avro.data.TimeConversions.TimeMillisConversion()", compiler.conversionInstance(timeSchema));
     Assert.assertEquals("Should use timestamp conversion for date type",
-        "new org.apache.avro.data.JodaTimeConversions.TimestampConversion()",
+        "new org.apache.avro.data.TimeConversions.TimestampMillisConversion()",
         compiler.conversionInstance(timestampSchema));
     Assert.assertEquals("Should use null for decimal if the flag is off", "null",
         compiler.conversionInstance(decimalSchema));
@@ -657,7 +642,7 @@ public class TestSpecificCompiler {
 
   @Test
   public void testConversionInstanceWithDecimalLogicalTypeEnabled() throws Exception {
-    SpecificCompiler compiler = createCompiler(JODA);
+    SpecificCompiler compiler = createCompiler();
     compiler.setEnableDecimalLogicalType(true);
 
     Schema dateSchema = LogicalTypes.date().addToSchema(Schema.create(Schema.Type.INT));
@@ -667,11 +652,11 @@ public class TestSpecificCompiler {
     Schema uuidSchema = LogicalTypes.uuid().addToSchema(Schema.create(Schema.Type.STRING));
 
     Assert.assertEquals("Should use date conversion for date type",
-        "new org.apache.avro.data.JodaTimeConversions.DateConversion()", compiler.conversionInstance(dateSchema));
+        "new org.apache.avro.data.TimeConversions.DateConversion()", compiler.conversionInstance(dateSchema));
     Assert.assertEquals("Should use time conversion for time type",
-        "new org.apache.avro.data.JodaTimeConversions.TimeConversion()", compiler.conversionInstance(timeSchema));
+        "new org.apache.avro.data.TimeConversions.TimeMillisConversion()", compiler.conversionInstance(timeSchema));
     Assert.assertEquals("Should use timestamp conversion for date type",
-        "new org.apache.avro.data.JodaTimeConversions.TimestampConversion()",
+        "new org.apache.avro.data.TimeConversions.TimestampMillisConversion()",
         compiler.conversionInstance(timestampSchema));
     Assert.assertEquals("Should use null for decimal if the flag is off",
         "new org.apache.avro.Conversions.DecimalConversion()", compiler.conversionInstance(decimalSchema));
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 31d5f00..4d749ab 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
@@ -28,7 +28,6 @@ import java.util.Arrays;
 import java.util.List;
 
 import org.apache.avro.compiler.specific.SpecificCompiler;
-import org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeImplementation;
 import org.apache.maven.artifact.DependencyResolutionRequiredException;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
@@ -172,14 +171,6 @@ 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 jsr310.
-   *
-   * @parameter default-value="jsr310"
-   */
-  protected String dateTimeLogicalTypeImplementation = DateTimeLogicalTypeImplementation.JSR310.name().toLowerCase();
-
-  /**
    * The current Maven project.
    *
    * @parameter default-value="${project}"
@@ -230,8 +221,8 @@ public abstract class AbstractAvroMojo extends AbstractMojo {
   }
 
   private String[] getIncludedFiles(String absPath, String[] excludes, String[] includes) {
-    FileSetManager fileSetManager = new FileSetManager();
-    FileSet fs = new FileSet();
+    final FileSetManager fileSetManager = new FileSetManager();
+    final FileSet fs = new FileSet();
     fs.setDirectory(absPath);
     fs.setFollowSymlinks(false);
 
@@ -279,24 +270,8 @@ 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 List<Object> instantiateAdditionalVelocityTools() {
-    List<Object> velocityTools = new ArrayList<>(velocityToolsClassesNames.length);
+    final List<Object> velocityTools = new ArrayList<>(velocityToolsClassesNames.length);
     for (String velocityToolClassName : velocityToolsClassesNames) {
       try {
         Class klass = Class.forName(velocityToolClassName);
@@ -311,7 +286,7 @@ public abstract class AbstractAvroMojo extends AbstractMojo {
   protected abstract void doCompile(String filename, File sourceDirectory, File outputDirectory) throws IOException;
 
   protected URLClassLoader createClassLoader() throws DependencyResolutionRequiredException, MalformedURLException {
-    List<URL> urls = appendElements(project.getRuntimeClasspathElements());
+    final List<URL> urls = appendElements(project.getRuntimeClasspathElements());
     urls.addAll(appendElements(project.getTestClasspathElements()));
     return new URLClassLoader(urls.toArray(new URL[0]), Thread.currentThread().getContextClassLoader());
   }
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 fce1037..afcde3e 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
@@ -87,7 +87,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, getDateTimeLogicalTypeImplementation());
+        final SpecificCompiler compiler = new SpecificCompiler(protocol);
         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 bafaf28..5c70963 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
@@ -57,9 +57,9 @@ public class ProtocolMojo extends AbstractAvroMojo {
 
   @Override
   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, getDateTimeLogicalTypeImplementation());
+    final File src = new File(sourceDirectory, filename);
+    final Protocol protocol = Protocol.parse(src);
+    final SpecificCompiler compiler = new SpecificCompiler(protocol);
     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 31addc5..e33bef9 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
@@ -64,7 +64,7 @@ public class SchemaMojo extends AbstractAvroMojo {
   @Override
   protected void doCompile(String filename, File sourceDirectory, File outputDirectory) throws IOException {
     File src = new File(sourceDirectory, filename);
-    Schema schema;
+    final Schema schema;
 
     // This is necessary to maintain backward-compatibility. If there are
     // no imported files then isolate the schemas from each other, otherwise
@@ -76,7 +76,7 @@ public class SchemaMojo extends AbstractAvroMojo {
       schema = schemaParser.parse(src);
     }
 
-    SpecificCompiler compiler = new SpecificCompiler(schema, getDateTimeLogicalTypeImplementation());
+    final SpecificCompiler compiler = new SpecificCompiler(schema);
     compiler.setTemplateDir(templateDirectory);
     compiler.setStringType(StringType.valueOf(stringType));
     compiler.setFieldVisibility(getFieldVisibility());
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 65f2d62..5a83669 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
@@ -19,14 +19,13 @@ package org.apache.avro.mojo;
 
 import java.io.File;
 import java.util.Arrays;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.apache.maven.plugin.testing.AbstractMojoTestCase;
 
 /**
  * Base class for all Avro mojo test classes.
- *
- * @author saden
  */
 public abstract class AbstractAvroMojoTest extends AbstractMojoTestCase {
 
@@ -43,19 +42,18 @@ public abstract class AbstractAvroMojoTest extends AbstractMojoTestCase {
   /**
    * Assert the existence files in the given given directory.
    *
-   * @param directory the directory being checked
-   * @param files     the files whose existence is being checked.
+   * @param directory     the directory being checked
+   * @param expectedFiles the files whose existence is being checked.
    */
-  protected void assertFilesExist(File directory, String... files) {
+  void assertFilesExist(File directory, Set<String> expectedFiles) {
     assertNotNull(directory);
-    assertTrue(directory.exists());
-    assertNotNull(files);
-    assertTrue(files.length > 0);
+    assertTrue("Directory " + directory.toString() + " does not exists", directory.exists());
+    assertNotNull(expectedFiles);
+    assertTrue(expectedFiles.size() > 0);
+
+    final Set<String> filesInDirectory = new HashSet<>(Arrays.asList(directory.list()));
 
-    List<String> dirList = Arrays.asList(directory.list());
+    assertEquals(expectedFiles, filesInDirectory);
 
-    for (String file : files) {
-      assertTrue("File " + file + " does not exist.", dirList.contains(file));
-    }
   }
 }
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 0c83b32..7dabdd0 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
@@ -18,63 +18,52 @@
 package org.apache.avro.mojo;
 
 import org.codehaus.plexus.util.FileUtils;
+import org.junit.Test;
 
 import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * Test the IDL Protocol Mojo.
- *
- * @author saden
  */
 public class TestIDLProtocolMojo extends AbstractAvroMojoTest {
 
-  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");
-  protected File injectingVelocityToolsTestPom = new File(getBasedir(),
+  private File testPom = new File(getBasedir(), "src/test/resources/unit/idl/pom.xml");
+  private File injectingVelocityToolsTestPom = new File(getBasedir(),
       "src/test/resources/unit/idl/pom-injecting-velocity-tools.xml");
 
-  public void testIdlProtocolMojoJoda() throws Exception {
-    IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", jodaTestPom);
+  @Test
+  public void testIdlProtocolMojo() throws Exception {
+    final IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", testPom);
 
     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 testIdlProtocolMojoJsr310() throws Exception {
-    IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", jsr310TestPom);
-
-    assertNotNull(mojo);
-    mojo.execute();
-
-    File outputDir = new File(getBasedir(), "target/test-harness/idl-jsr310/test");
-    String[] generatedFileNames = new String[] { "IdlPrivacy.java", "IdlTest.java", "IdlUser.java",
-        "IdlUserWrapper.java" };
+    final File outputDir = new File(getBasedir(), "target/test-harness/idl/test/");
+    final Set<String> generatedFiles = new HashSet<>(Arrays.asList("IdlPrivacy.java", "IdlTest.java", "IdlUser.java",
+        "IdlUserWrapper.java", "IdlClasspathImportTest.java"));
+    assertFilesExist(outputDir, generatedFiles);
 
-    String idlUserContent = FileUtils.fileRead(new File(outputDir, "IdlUser.java"));
+    final String idlUserContent = FileUtils.fileRead(new File(outputDir, "IdlUser.java"));
     assertTrue(idlUserContent.contains("java.time.Instant"));
   }
 
+  @Test
   public void testSetCompilerVelocityAdditionalTools() throws Exception {
-    injectingVelocityToolsTestPom = new File(getBasedir(),
-        "src/test/resources/unit/idl/pom-injecting-velocity-tools.xml");
-    IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", injectingVelocityToolsTestPom);
+    final IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", injectingVelocityToolsTestPom);
 
     assertNotNull(mojo);
     mojo.execute();
 
-    File outputDir = new File(getBasedir(), "target/test-harness/idl/test");
-    String[] generatedFiles = new String[] { "IdlPrivacy.java", "IdlTest.java", "IdlUser.java", "IdlUserWrapper.java" };
+    final File outputDir = new File(getBasedir(), "target/test-harness/idl-inject/test");
+    final Set<String> generatedFiles = new HashSet<>(Arrays.asList("IdlPrivacy.java", "IdlTest.java", "IdlUser.java",
+        "IdlUserWrapper.java", "IdlClasspathImportTest.java"));
 
     assertFilesExist(outputDir, generatedFiles);
 
-    String schemaUserContent = FileUtils.fileRead(new File(outputDir, "IdlUser.java"));
+    final String schemaUserContent = FileUtils.fileRead(new File(outputDir, "IdlUser.java"));
     assertTrue(schemaUserContent.contains("It works!"));
   }
 }
diff --git a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestInduceMojo.java b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestInduceMojo.java
index b9f5be5..f0dcfe0 100644
--- a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestInduceMojo.java
+++ b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestInduceMojo.java
@@ -27,6 +27,7 @@ import org.apache.avro.entities.Person;
 import org.apache.avro.protocols.Remote;
 import org.apache.avro.reflect.ReflectData;
 import org.apache.maven.plugin.testing.AbstractMojoTestCase;
+import org.junit.Test;
 
 public class TestInduceMojo extends AbstractMojoTestCase {
 
@@ -52,6 +53,7 @@ public class TestInduceMojo extends AbstractMojoTestCase {
     assertNotNull(mojo);
   }
 
+  @Test
   public void testInduceSchema() throws Exception {
     executeMojo(schemaPom);
 
@@ -62,6 +64,7 @@ public class TestInduceMojo extends AbstractMojoTestCase {
     assertEquals(ReflectData.get().getSchema(Person.class), new Schema.Parser().parse(personSchemaFile));
   }
 
+  @Test
   public void testInducedSchemasFileExtension() throws Exception {
     executeMojo(schemaPom);
 
@@ -71,6 +74,7 @@ public class TestInduceMojo extends AbstractMojoTestCase {
     }
   }
 
+  @Test
   public void testInduceProtocol() throws Exception {
     executeMojo(protocolPom);
 
@@ -81,6 +85,7 @@ public class TestInduceMojo extends AbstractMojoTestCase {
     assertEquals(ReflectData.get().getProtocol(Remote.class), Protocol.parse(remoteProtocolFile));
   }
 
+  @Test
   public void testInducedProtocolsFileExtension() throws Exception {
     executeMojo(protocolPom);
 
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 8d182e6..f5d7311 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
@@ -18,59 +18,49 @@
 package org.apache.avro.mojo;
 
 import org.codehaus.plexus.util.FileUtils;
+import org.junit.Test;
 
 import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * Test the Protocol Mojo.
- *
- * @author saden
  */
 public class TestProtocolMojo extends AbstractAvroMojoTest {
 
-  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");
-  protected File injectingVelocityToolsTestPom = new File(getBasedir(),
+  private File testPom = new File(getBasedir(), "src/test/resources/unit/protocol/pom.xml");
+  private File injectingVelocityToolsTestPom = new File(getBasedir(),
       "src/test/resources/unit/protocol/pom-injecting-velocity-tools.xml");
 
-  public void testProtocolMojoJoda() throws Exception {
-    ProtocolMojo mojo = (ProtocolMojo) lookupMojo("protocol", jodaTestPom);
-
-    assertNotNull(mojo);
-    mojo.execute();
-
-    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);
+  @Test
+  public void testProtocolMojo() throws Exception {
+    final ProtocolMojo mojo = (ProtocolMojo) lookupMojo("protocol", testPom);
 
     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" };
+    final File outputDir = new File(getBasedir(), "target/test-harness/protocol/test");
+    final Set<String> generatedFiles = new HashSet<>(
+        Arrays.asList("ProtocolPrivacy.java", "ProtocolTest.java", "ProtocolUser.java"));
 
     assertFilesExist(outputDir, generatedFiles);
 
-    String protocolUserContent = FileUtils.fileRead(new File(outputDir, "ProtocolUser.java"));
-    assertTrue(protocolUserContent.contains("java.time.Instant"));
+    final String protocolUserContent = FileUtils.fileRead(new File(outputDir, "ProtocolUser.java"));
+    assertTrue("Got " + protocolUserContent + " instead", protocolUserContent.contains("java.time.Instant"));
   }
 
+  @Test
   public void testSetCompilerVelocityAdditionalTools() throws Exception {
     ProtocolMojo mojo = (ProtocolMojo) lookupMojo("protocol", injectingVelocityToolsTestPom);
 
     assertNotNull(mojo);
     mojo.execute();
 
-    File outputDir = new File(getBasedir(), "target/test-harness/protocol/test");
-    String[] generatedFiles = new String[] { "ProtocolPrivacy.java", "ProtocolTest.java", "ProtocolUser.java" };
+    File outputDir = new File(getBasedir(), "target/test-harness/protocol-inject/test");
+    final Set<String> generatedFiles = new HashSet<>(
+        Arrays.asList("ProtocolPrivacy.java", "ProtocolTest.java", "ProtocolUser.java"));
 
     assertFilesExist(outputDir, generatedFiles);
 
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 c90a765..f6bdc7f 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
@@ -18,66 +18,53 @@
 package org.apache.avro.mojo;
 
 import org.codehaus.plexus.util.FileUtils;
+import org.junit.Test;
 
 import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * Test the Schema Mojo.
- *
- * @author saden
  */
 public class TestSchemaMojo extends AbstractAvroMojoTest {
 
-  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");
-  protected File injectingVelocityToolsTestPom = new File(getBasedir(),
+  private File testPom = new File(getBasedir(), "src/test/resources/unit/schema/pom.xml");
+  private File injectingVelocityToolsTestPom = new File(getBasedir(),
       "src/test/resources/unit/schema/pom-injecting-velocity-tools.xml");
 
-  public void testSchemaMojoJoda() throws Exception {
-    SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", jodaTestPom);
-
-    assertNotNull(mojo);
-    mojo.execute();
-
-    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);
+  @Test
+  public void testSchemaMojo() throws Exception {
+    final SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", testPom);
 
     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" };
+    final File outputDir = new File(getBasedir(), "target/test-harness/schema/test");
+    final Set<String> generatedFiles = new HashSet<>(
+        Arrays.asList("PrivacyDirectImport.java", "PrivacyImport.java", "SchemaPrivacy.java", "SchemaUser.java"));
 
     assertFilesExist(outputDir, generatedFiles);
 
-    String schemaUserContent = FileUtils.fileRead(new File(outputDir, "SchemaUser.java"));
+    final String schemaUserContent = FileUtils.fileRead(new File(outputDir, "SchemaUser.java"));
     assertTrue(schemaUserContent.contains("java.time.Instant"));
   }
 
+  @Test
   public void testSetCompilerVelocityAdditionalTools() throws Exception {
-    SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", injectingVelocityToolsTestPom);
+    final SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", injectingVelocityToolsTestPom);
 
     assertNotNull(mojo);
     mojo.execute();
 
-    File outputDir = new File(getBasedir(), "target/test-harness/schema/test");
-    String[] generatedFiles = new String[] { "PrivacyDirectImport.java", "PrivacyImport.java", "SchemaPrivacy.java",
-        "SchemaUser.java" };
+    final File outputDir = new File(getBasedir(), "target/test-harness/schema-inject/test");
+    final Set<String> generatedFiles = new HashSet<>(
+        Arrays.asList("PrivacyDirectImport.java", "PrivacyImport.java", "SchemaPrivacy.java", "SchemaUser.java"));
 
     assertFilesExist(outputDir, generatedFiles);
 
-    String schemaUserContent = FileUtils.fileRead(new File(outputDir, "SchemaUser.java"));
-    assertTrue(schemaUserContent.contains("It works!"));
+    final String schemaUserContent = FileUtils.fileRead(new File(outputDir, "SchemaUser.java"));
+    assertTrue("Got " + schemaUserContent + " instead", schemaUserContent.contains("It works!"));
   }
 }
diff --git a/lang/java/maven-plugin/src/test/resources/unit/idl/pom-injecting-velocity-tools.xml b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-injecting-velocity-tools.xml
index c33ea58..0d0f9df 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/idl/pom-injecting-velocity-tools.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-injecting-velocity-tools.xml
@@ -45,7 +45,7 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/idl</outputDirectory>
+          <outputDirectory>${basedir}/target/test-harness/idl-inject</outputDirectory>
           <stringType>String</stringType>
           <templateDirectory>
             ${basedir}/src/test/resources/templates/
@@ -70,5 +70,4 @@
       <version>${jackson.databind.version}</version>
     </dependency>
   </dependencies>
-
 </project>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/idl/pom-jsr310.xml b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-jsr310.xml
deleted file mode 100644
index 2987fe7..0000000
--- a/lang/java/maven-plugin/src/test/resources/unit/idl/pom-jsr310.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-   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
-
-       https://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.
--->
-<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.10.0-SNAPSHOT</version>
-    <relativePath>../../../../../../../../../pom.xml</relativePath>
-  </parent>
-
-  <artifactId>avro-maven-plugin-test</artifactId>
-  <packaging>jar</packaging>
-
-  <name>testproject</name>
-
-  <build>
-    <plugins>
-      <plugin>
-        <artifactId>avro-maven-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>idl</id>
-            <goals>
-              <goal>idl-protocol</goal>
-            </goals>
-          </execution>
-        </executions>
-        <configuration>
-          <sourceDirectory>${basedir}/src/test</sourceDirectory>
-          <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>
-    </plugins>
-  </build>
-  <dependencies>
-    <dependency>
-      <groupId>org.apache.avro</groupId>
-      <artifactId>avro</artifactId>
-      <version>${parent.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>com.fasterxml.jackson.core</groupId>
-      <artifactId>jackson-databind</artifactId>
-      <version>${jackson.databind.version}</version>
-    </dependency>
-  </dependencies>
-
-</project>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/idl/pom-joda.xml b/lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml
similarity index 93%
rename from lang/java/maven-plugin/src/test/resources/unit/idl/pom-joda.xml
rename to lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml
index 9e09763..1ef0bb5 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/idl/pom-joda.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml
@@ -45,9 +45,8 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/idl-joda</outputDirectory>
+          <outputDirectory>${basedir}/target/test-harness/idl</outputDirectory>
           <stringType>String</stringType>
-          <dateTimeLogicalTypeImplementation>joda</dateTimeLogicalTypeImplementation>
           <project implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
         </configuration>
       </plugin>
@@ -65,5 +64,4 @@
       <version>${jackson.databind.version}</version>
     </dependency>
   </dependencies>
-
 </project>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-injecting-velocity-tools.xml b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-injecting-velocity-tools.xml
index 61a5ce3..0246fe7 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-injecting-velocity-tools.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-injecting-velocity-tools.xml
@@ -45,7 +45,7 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test/avro</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/protocol</outputDirectory>
+          <outputDirectory>${basedir}/target/test-harness/protocol-inject</outputDirectory>
           <project implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
           <templateDirectory>
             ${basedir}/src/test/resources/templates/
@@ -70,5 +70,4 @@
       <version>${jackson.databind.version}</version>
     </dependency>
   </dependencies>
-
 </project>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-jsr310.xml b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-jsr310.xml
deleted file mode 100644
index 2fc566c..0000000
--- a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-jsr310.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-   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
-
-       https://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.
--->
-<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.10.0-SNAPSHOT</version>
-    <relativePath>../../../../../../../../../pom.xml</relativePath>
-  </parent>
-
-  <artifactId>avro-maven-plugin-test</artifactId>
-  <packaging>jar</packaging>
-
-  <name>testproject</name>
-
-  <build>
-    <plugins>
-      <plugin>
-        <artifactId>avro-maven-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>protocol</id>
-            <goals>
-              <goal>protocol</goal>
-            </goals>
-          </execution>
-        </executions>
-        <configuration>
-          <sourceDirectory>${basedir}/src/test/avro</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/protocol-jsr310</outputDirectory>
-          <dateTimeLogicalTypeImplementation>jsr310</dateTimeLogicalTypeImplementation>
-          <project implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-  <dependencies>
-    <dependency>
-      <groupId>org.apache.avro</groupId>
-      <artifactId>avro</artifactId>
-      <version>${parent.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>com.fasterxml.jackson.core</groupId>
-      <artifactId>jackson-databind</artifactId>
-      <version>${jackson.databind.version}</version>
-    </dependency>
-  </dependencies>
-
-</project>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-joda.xml b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml
similarity index 95%
rename from lang/java/maven-plugin/src/test/resources/unit/protocol/pom-joda.xml
rename to lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml
index cde8044..48d5883 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-joda.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml
@@ -45,8 +45,7 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test/avro</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/protocol-joda</outputDirectory>
-          <dateTimeLogicalTypeImplementation>joda</dateTimeLogicalTypeImplementation>
+          <outputDirectory>${basedir}/target/test-harness/protocol</outputDirectory>
           <project implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
         </configuration>
       </plugin>
@@ -64,5 +63,4 @@
       <version>${jackson.databind.version}</version>
     </dependency>
   </dependencies>
-
 </project>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/schema/pom-injecting-velocity-tools.xml b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-injecting-velocity-tools.xml
index fbfa132..6a2a2b3 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/schema/pom-injecting-velocity-tools.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-injecting-velocity-tools.xml
@@ -38,7 +38,7 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test/avro</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/schema</outputDirectory>
+          <outputDirectory>${basedir}/target/test-harness/schema-inject</outputDirectory>
           <imports>
             <import>${basedir}/src/test/avro/imports</import>
             <import>${basedir}/src/test/avro/directImport/PrivacyDirectImport.avsc</import>
@@ -67,5 +67,4 @@
       <version>${jackson.databind.version}</version>
     </dependency>
   </dependencies>
-
 </project>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/schema/pom-jsr310.xml b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-jsr310.xml
deleted file mode 100644
index f24a26e..0000000
--- a/lang/java/maven-plugin/src/test/resources/unit/schema/pom-jsr310.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-   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
-
-       https://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.
--->
-<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.10.0-SNAPSHOT</version>
-    <relativePath>../../../../../../../../../pom.xml</relativePath>
-  </parent>
-
-  <artifactId>avro-maven-plugin-test</artifactId>
-  <packaging>jar</packaging>
-
-  <name>testproject</name>
-
-  <build>
-    <plugins>
-      <plugin>
-        <artifactId>avro-maven-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>schema</id>
-            <goals>
-              <goal>schema</goal>
-            </goals>
-          </execution>
-        </executions>
-        <configuration>
-          <sourceDirectory>${basedir}/src/test/avro</sourceDirectory>
-          <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>
-          </imports>
-          <project implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-  <dependencies>
-    <dependency>
-      <groupId>org.apache.avro</groupId>
-      <artifactId>avro</artifactId>
-      <version>${parent.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>com.fasterxml.jackson.core</groupId>
-      <artifactId>jackson-databind</artifactId>
-      <version>${jackson.databind.version}</version>
-    </dependency>
-  </dependencies>
-
-</project>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/schema/pom-joda.xml b/lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml
similarity index 95%
rename from lang/java/maven-plugin/src/test/resources/unit/schema/pom-joda.xml
rename to lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml
index 3b0ba9d..3860453 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/schema/pom-joda.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml
@@ -45,12 +45,11 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test/avro</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/schema-joda</outputDirectory>
+          <outputDirectory>${basedir}/target/test-harness/schema</outputDirectory>
           <imports>
             <import>${basedir}/src/test/avro/imports</import>
             <import>${basedir}/src/test/avro/directImport/PrivacyDirectImport.avsc</import>
           </imports>
-          <dateTimeLogicalTypeImplementation>joda</dateTimeLogicalTypeImplementation>
           <project implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
         </configuration>
       </plugin>
@@ -68,5 +67,4 @@
       <version>${jackson.databind.version}</version>
     </dependency>
   </dependencies>
-
 </project>
diff --git a/lang/java/pom.xml b/lang/java/pom.xml
index fb50fc2..6fbe508 100644
--- a/lang/java/pom.xml
+++ b/lang/java/pom.xml
@@ -60,7 +60,6 @@
     <tukaani.version>1.8</tukaani.version>
     <easymock.version>4.0.2</easymock.version>
     <hamcrest.version>2.1</hamcrest.version>
-    <joda.version>2.10.1</joda.version>
     <grpc.version>1.23.0</grpc.version>
     <netty-codec-http2.version>4.1.39.Final</netty-codec-http2.version>
     <zstd-jni.version>1.4.3-1</zstd-jni.version>
@@ -570,11 +569,6 @@
         <version>${commons-compress.version}</version>
       </dependency>
       <dependency>
-        <groupId>joda-time</groupId>
-        <artifactId>joda-time</artifactId>
-        <version>${joda.version}</version>
-      </dependency>
-      <dependency>
         <groupId>io.grpc</groupId>
         <artifactId>grpc-core</artifactId>
         <version>${grpc.version}</version>
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 9f04ec9..a2dd60f 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,7 +23,6 @@ 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;
@@ -32,7 +31,6 @@ 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;
 
@@ -46,22 +44,19 @@ public class SpecificCompilerTool implements Tool {
   public int run(InputStream in, PrintStream out, PrintStream err, List<String> args) throws Exception {
     if (args.size() < 3) {
       System.err.println(
-          "Usage: [-encoding <outputencoding>] [-string] [-bigDecimal] [-dateTimeLogicalTypeImpl <dateTimeType>] [-templateDir <templateDir>] (schema|protocol) input... outputdir");
+          "Usage: [-encoding <outputencoding>] [-string] [-bigDecimal] [-templateDir <templateDir>] (schema|protocol) input... outputdir");
       System.err.println(" input - input files or directories");
       System.err.println(" outputdir - directory to write generated java");
       System.err.println(" -encoding <outputencoding> - set the encoding of " + "output file(s)");
       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 [jsr310|joda] - use either "
-          + "Java 8 native date/time classes (JSR 310)(default) or Joda time classes");
       System.err.println(" -templateDir - directory with custom Velocity templates");
       return 1;
     }
 
     StringType stringType = StringType.CharSequence;
     boolean useLogicalDecimal = false;
-    Optional<DateTimeLogicalTypeImplementation> dateTimeLogicalTypeImplementation = Optional.empty();
     Optional<String> encoding = Optional.empty();
     Optional<String> templateDir = Optional.empty();
 
@@ -83,18 +78,6 @@ 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++;
-    }
-
     if ("-templateDir".equals(args.get(arg))) {
       arg++;
       templateDir = Optional.of(args.get(arg));
@@ -113,15 +96,13 @@ 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,
-            dateTimeLogicalTypeImplementation.orElse(DateTimeLogicalTypeImplementation.DEFAULT));
+        final SpecificCompiler compiler = new SpecificCompiler(schema);
         executeCompiler(compiler, encoding, stringType, useLogicalDecimal, templateDir, src, output);
       }
     } else if ("protocol".equals(method)) {
       for (File src : determineInputs(inputs, PROTOCOL_FILTER)) {
         Protocol protocol = Protocol.parse(src);
-        SpecificCompiler compiler = new SpecificCompiler(protocol,
-            dateTimeLogicalTypeImplementation.orElse(DateTimeLogicalTypeImplementation.DEFAULT));
+        final SpecificCompiler compiler = new SpecificCompiler(protocol);
         executeCompiler(compiler, encoding, stringType, useLogicalDecimal, templateDir, src, output);
       }
     } else {
diff --git a/pom.xml b/pom.xml
index ebf436e..c805f21 100644
--- a/pom.xml
+++ b/pom.xml
@@ -307,7 +307,6 @@
                 <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>