[ 
https://issues.apache.org/jira/browse/AVRO-2079?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16635515#comment-16635515
 ] 

ASF GitHub Bot commented on AVRO-2079:
--------------------------------------

nandorKollar closed pull request #248: AVRO-2079: Add ability to generate Java8 
native date/time classes
URL: https://github.com/apache/avro/pull/248
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git 
a/lang/java/avro/src/main/java/org/apache/avro/data/Java8TimeConversions.java 
b/lang/java/avro/src/main/java/org/apache/avro/data/Java8TimeConversions.java
new file mode 100644
index 000000000..b172bd887
--- /dev/null
+++ 
b/lang/java/avro/src/main/java/org/apache/avro/data/Java8TimeConversions.java
@@ -0,0 +1,158 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.avro.data;
+
+import org.apache.avro.Conversion;
+import org.apache.avro.LogicalType;
+import org.apache.avro.Schema;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.concurrent.TimeUnit;
+
+public class Java8TimeConversions {
+  public static class DateConversion extends Conversion<LocalDate> {
+
+    @Override
+    public Class<LocalDate> getConvertedType() {
+      return LocalDate.class;
+    }
+
+    @Override
+    public String getLogicalTypeName() {
+      return "date";
+    }
+
+    @Override
+    public LocalDate fromInt(Integer daysFromEpoch, Schema schema, LogicalType 
type) {
+      return LocalDate.ofEpochDay(daysFromEpoch);
+    }
+
+    @Override
+    public Integer toInt(LocalDate date, Schema schema, LogicalType type) {
+      long epochDays = date.toEpochDay();
+
+      return Math.toIntExact(epochDays);
+    }
+  }
+
+  public static class TimeMillisConversion extends Conversion<LocalTime> {
+    @Override
+    public Class<LocalTime> getConvertedType() {
+      return LocalTime.class;
+    }
+
+    @Override
+    public String getLogicalTypeName() {
+      return "time-millis";
+    }
+
+    @Override
+    public LocalTime fromInt(Integer millisFromMidnight, Schema schema, 
LogicalType type) {
+      return 
LocalTime.ofNanoOfDay(TimeUnit.MILLISECONDS.toNanos(millisFromMidnight));
+    }
+
+    @Override
+    public Integer toInt(LocalTime time, Schema schema, LogicalType type) {
+      return 
Math.toIntExact(TimeUnit.NANOSECONDS.toMillis(time.toNanoOfDay()));
+    }
+  }
+
+  public static class TimeMicrosConversion extends Conversion<LocalTime> {
+    @Override
+    public Class<LocalTime> getConvertedType() {
+      return LocalTime.class;
+    }
+
+    @Override
+    public String getLogicalTypeName() {
+      return "time-micros";
+    }
+
+    @Override
+    public LocalTime fromLong(Long microsFromMidnight, Schema schema, 
LogicalType type) {
+      return 
LocalTime.ofNanoOfDay(TimeUnit.MICROSECONDS.toNanos(microsFromMidnight));
+    }
+
+    @Override
+    public Long toLong(LocalTime time, Schema schema, LogicalType type) {
+      return TimeUnit.NANOSECONDS.toMicros(time.toNanoOfDay());
+    }
+  }
+
+  public static class TimestampMillisConversion extends Conversion<Instant> {
+    @Override
+    public Class<Instant> getConvertedType() {
+      return Instant.class;
+    }
+
+    @Override
+    public String getLogicalTypeName() {
+      return "timestamp-millis";
+    }
+
+    @Override
+    public Instant fromLong(Long millisFromEpoch, Schema schema, LogicalType 
type) {
+      return Instant.ofEpochMilli(millisFromEpoch);
+    }
+
+    @Override
+    public Long toLong(Instant timestamp, Schema schema, LogicalType type) {
+      return timestamp.toEpochMilli();
+    }
+  }
+
+  public static class TimestampMicrosConversion extends Conversion<Instant> {
+    @Override
+    public Class<Instant> getConvertedType() {
+      return Instant.class;
+    }
+
+    @Override
+    public String getLogicalTypeName() {
+      return "timestamp-micros";
+    }
+
+    @Override
+    public Instant fromLong(Long microsFromEpoch, Schema schema, LogicalType 
type) {
+      long epochSeconds = microsFromEpoch / (1_000_000);
+      long nanoAdjustment = (microsFromEpoch % (1_000_000)) * 1_000;
+
+      return Instant.ofEpochSecond(epochSeconds, nanoAdjustment);
+    }
+
+    @Override
+    public Long toLong(Instant instant, Schema schema, LogicalType type) {
+      long seconds = instant.getEpochSecond();
+      int nanos = instant.getNano();
+
+      if (seconds < 0 && nanos > 0) {
+        long micros = Math.multiplyExact(seconds + 1, 1_000_000);
+        long adjustment = (nanos / 1_000L) - 1_000_000;
+
+        return Math.addExact(micros, adjustment);
+      } else {
+        long micros = Math.multiplyExact(seconds, 1_000_000);
+
+        return Math.addExact(micros, nanos / 1_000);
+      }
+    }
+  }
+}
diff --git 
a/lang/java/avro/src/main/java/org/apache/avro/data/TimeConversions.java 
b/lang/java/avro/src/main/java/org/apache/avro/data/JodaTimeConversions.java
similarity index 99%
rename from 
lang/java/avro/src/main/java/org/apache/avro/data/TimeConversions.java
rename to 
lang/java/avro/src/main/java/org/apache/avro/data/JodaTimeConversions.java
index b53bb1f69..769ebdd66 100644
--- a/lang/java/avro/src/main/java/org/apache/avro/data/TimeConversions.java
+++ b/lang/java/avro/src/main/java/org/apache/avro/data/JodaTimeConversions.java
@@ -27,7 +27,7 @@
 import org.joda.time.LocalDate;
 import org.joda.time.LocalTime;
 
-public class TimeConversions {
+public class JodaTimeConversions {
   public static class DateConversion extends Conversion<LocalDate> {
     private static final LocalDate EPOCH_DATE = new LocalDate(1970, 1, 1);
 
diff --git 
a/lang/java/avro/src/test/java/org/apache/avro/data/TestJava8TimeConversions.java
 
b/lang/java/avro/src/test/java/org/apache/avro/data/TestJava8TimeConversions.java
new file mode 100644
index 000000000..5583308ae
--- /dev/null
+++ 
b/lang/java/avro/src/test/java/org/apache/avro/data/TestJava8TimeConversions.java
@@ -0,0 +1,243 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.avro.data;
+
+import org.apache.avro.LogicalTypes;
+import org.apache.avro.Schema;
+import org.apache.avro.data.Java8TimeConversions.DateConversion;
+import org.apache.avro.data.Java8TimeConversions.TimeMillisConversion;
+import org.apache.avro.data.Java8TimeConversions.TimeMicrosConversion;
+import org.apache.avro.data.Java8TimeConversions.TimestampMicrosConversion;
+import org.apache.avro.data.Java8TimeConversions.TimestampMillisConversion;
+
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.time.ZonedDateTime;
+import java.util.Date;
+
+public class TestJava8TimeConversions {
+
+  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() {
+    TestJava8TimeConversions.DATE_SCHEMA = LogicalTypes.date()
+        .addToSchema(Schema.create(Schema.Type.INT));
+    TestJava8TimeConversions.TIME_MILLIS_SCHEMA = LogicalTypes.timeMillis()
+        .addToSchema(Schema.create(Schema.Type.INT));
+    TestJava8TimeConversions.TIME_MICROS_SCHEMA = LogicalTypes.timeMicros()
+        .addToSchema(Schema.create(Schema.Type.LONG));
+    TestJava8TimeConversions.TIMESTAMP_MILLIS_SCHEMA = 
LogicalTypes.timestampMillis()
+        .addToSchema(Schema.create(Schema.Type.LONG));
+    TestJava8TimeConversions.TIMESTAMP_MICROS_SCHEMA = 
LogicalTypes.timestampMicros()
+        .addToSchema(Schema.create(Schema.Type.LONG));
+  }
+
+  @Test
+  public void testDateConversion() throws Exception {
+    DateConversion conversion = new DateConversion();
+    LocalDate Jan_6_1970 = LocalDate.of(1970, 1, 6);    //  5
+    LocalDate Jan_1_1970 = LocalDate.of(1970, 1, 1);    //  0
+    LocalDate Dec_27_1969 = LocalDate.of(1969, 12, 27); // -5
+
+    Assert.assertEquals("6 Jan 1970 should be 5", 5,
+        (int) conversion.toInt(Jan_6_1970, DATE_SCHEMA, LogicalTypes.date()));
+    Assert.assertEquals("1 Jan 1970 should be 0", 0,
+        (int) conversion.toInt(Jan_1_1970, DATE_SCHEMA, LogicalTypes.date()));
+    Assert.assertEquals("27 Dec 1969 should be -5", -5,
+        (int) conversion.toInt(Dec_27_1969, DATE_SCHEMA, LogicalTypes.date()));
+
+    Assert.assertEquals("6 Jan 1970 should be 5",
+        conversion.fromInt(5, DATE_SCHEMA, LogicalTypes.date()), Jan_6_1970);
+    Assert.assertEquals("1 Jan 1970 should be 0",
+        conversion.fromInt(0, DATE_SCHEMA, LogicalTypes.date()), Jan_1_1970);
+    Assert.assertEquals("27 Dec 1969 should be -5",
+        conversion.fromInt(-5, DATE_SCHEMA, LogicalTypes.date()), Dec_27_1969);
+  }
+
+  @Test
+  public void testTimeMillisConversion() throws Exception {
+    TimeMillisConversion conversion = new TimeMillisConversion();
+    LocalTime oneAM = LocalTime.of(1, 0);
+    LocalTime afternoon = LocalTime.of(15, 14, 15, 926_000_000);
+    int afternoonMillis = ((15 * 60 + 14) * 60 + 15) * 1000 + 926;
+
+    Assert.assertEquals("Midnight should be 0", 0,
+        (int) conversion.toInt(
+            LocalTime.MIDNIGHT, TIME_MILLIS_SCHEMA, 
LogicalTypes.timeMillis()));
+    Assert.assertEquals("01:00 should be 3,600,000", 3_600_000,
+        (int) conversion.toInt(
+            oneAM, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis()));
+    Assert.assertEquals("15:14:15.926 should be " + afternoonMillis,
+        afternoonMillis,
+        (int) conversion.toInt(
+            afternoon, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis()));
+
+    Assert.assertEquals("Midnight should be 0",
+        LocalTime.MIDNIGHT,
+        conversion.fromInt(0, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis()));
+    Assert.assertEquals("01:00 should be 3,600,000",
+        oneAM,
+        conversion.fromInt(
+            3600000, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis()));
+    Assert.assertEquals("15:14:15.926 should be " + afternoonMillis,
+        afternoon,
+        conversion.fromInt(
+            afternoonMillis, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis()));
+  }
+
+  @Test
+  public void testTimeMicrosConversion() throws Exception {
+    TimeMicrosConversion conversion = new TimeMicrosConversion();
+    LocalTime oneAM = LocalTime.of(1, 0);
+    LocalTime afternoon = LocalTime.of(15, 14, 15, 926_551_000);
+    long afternoonMicros = ((long) (15 * 60 + 14) * 60 + 15) * 1_000_000 + 
926_551;
+
+    Assert.assertEquals("Midnight should be 0",
+        LocalTime.MIDNIGHT,
+        conversion.fromLong(0L, TIME_MICROS_SCHEMA, 
LogicalTypes.timeMicros()));
+    Assert.assertEquals("01:00 should be 3,600,000,000",
+        oneAM,
+        conversion.fromLong(
+            3_600_000_000L, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
+    Assert.assertEquals("15:14:15.926551 should be " + afternoonMicros,
+        afternoon,
+        conversion.fromLong(
+            afternoonMicros, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
+
+    Assert.assertEquals("Midnight should be 0", 0,
+        (long) conversion.toLong(
+            LocalTime.MIDNIGHT, TIME_MICROS_SCHEMA, 
LogicalTypes.timeMicros()));
+    Assert.assertEquals("01:00 should be 3,600,000,000", 3_600_000_000L,
+        (long) conversion.toLong(
+            oneAM, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
+    Assert.assertEquals("15:14:15.926551 should be " + afternoonMicros,
+        afternoonMicros,
+        (long) conversion.toLong(
+            afternoon, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros()));
+  }
+
+  @Test
+  public void testTimestampMillisConversion() throws Exception {
+    TimestampMillisConversion conversion = new TimestampMillisConversion();
+    long nowInstant = new Date().getTime(); // ms precision
+
+    // round trip
+    Instant now = conversion.fromLong(
+        nowInstant, TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis());
+    long roundTrip = conversion.toLong(
+        now, TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis());
+    Assert.assertEquals("Round-trip conversion should work",
+        nowInstant, roundTrip);
+
+    long May_28_2015_21_46_53_221_instant = 1432849613221L;
+    Instant May_28_2015_21_46_53_221 =
+        ZonedDateTime.of(2015, 5, 28, 21, 46, 53, 221_000_000, 
ZoneOffset.UTC).toInstant();
+
+    // known dates from https://www.epochconverter.com/
+    // > Epoch
+    Assert.assertEquals("Known date should be correct",
+        May_28_2015_21_46_53_221,
+        conversion.fromLong(May_28_2015_21_46_53_221_instant,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+    Assert.assertEquals("Known date should be correct",
+        May_28_2015_21_46_53_221_instant,
+        (long) conversion.toLong(May_28_2015_21_46_53_221,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+
+    // Epoch
+    Assert.assertEquals("1970-01-01 should be 0",
+        Instant.EPOCH,
+        conversion.fromLong(0L,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+    Assert.assertEquals("1970-01-01 should be 0",
+        0L,
+        (long) conversion.toLong(ZonedDateTime.ofInstant(Instant.EPOCH, 
ZoneOffset.UTC).toInstant(),
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+
+    // < Epoch
+    long Jul_01_1969_12_00_00_123_instant = -15854400000L + 123;
+    Instant Jul_01_1969_12_00_00_123 =
+        ZonedDateTime.of(1969, 7, 1, 12, 0, 0, 123_000_000, 
ZoneOffset.UTC).toInstant();
+
+    Assert.assertEquals("Pre 1970 date should be correct",
+        Jul_01_1969_12_00_00_123,
+        conversion.fromLong(Jul_01_1969_12_00_00_123_instant,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+    Assert.assertEquals("Pre 1970 date should be correct",
+        Jul_01_1969_12_00_00_123_instant,
+        (long) conversion.toLong(Jul_01_1969_12_00_00_123,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+  }
+
+  @Test
+  public void testTimestampMicrosConversion() throws Exception {
+    TimestampMicrosConversion conversion = new TimestampMicrosConversion();
+
+    // known dates from https://www.epochconverter.com/
+    // > Epoch
+    long May_28_2015_21_46_53_221_843_instant = 1432849613221L * 1000 + 843;
+    Instant May_28_2015_21_46_53_221_843 =
+        ZonedDateTime.of(2015, 5, 28, 21, 46, 53, 221_843_000, 
ZoneOffset.UTC).toInstant();
+
+    Assert.assertEquals("Known date should be correct",
+        May_28_2015_21_46_53_221_843,
+        conversion.fromLong(May_28_2015_21_46_53_221_843_instant,
+            TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMicros()));
+
+    Assert.assertEquals("Known date should be correct",
+        May_28_2015_21_46_53_221_843_instant,
+        (long) conversion.toLong(May_28_2015_21_46_53_221_843,
+            TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMillis()));
+
+    // Epoch
+    Assert.assertEquals("1970-01-01 should be 0",
+        Instant.EPOCH,
+        conversion.fromLong(0L,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+    Assert.assertEquals("1970-01-01 should be 0",
+        0L,
+        (long) conversion.toLong(ZonedDateTime.ofInstant(Instant.EPOCH, 
ZoneOffset.UTC).toInstant(),
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+
+    // < Epoch
+    long Jul_01_1969_12_00_00_000_123_instant = -15854400000L * 1000 + 123;
+    Instant Jul_01_1969_12_00_00_000_123 =
+        ZonedDateTime.of(1969, 7, 1, 12, 0, 0, 123_000, 
ZoneOffset.UTC).toInstant();
+
+    Assert.assertEquals("Pre 1970 date should be correct",
+        Jul_01_1969_12_00_00_000_123,
+        conversion.fromLong(Jul_01_1969_12_00_00_000_123_instant,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+    Assert.assertEquals("Pre 1970 date should be correct",
+        Jul_01_1969_12_00_00_000_123_instant,
+        (long) conversion.toLong(Jul_01_1969_12_00_00_000_123,
+            TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()));
+  }
+}
diff --git 
a/lang/java/avro/src/test/java/org/apache/avro/data/TestTimeConversions.java 
b/lang/java/avro/src/test/java/org/apache/avro/data/TestJodaTimeConversions.java
similarity index 91%
rename from 
lang/java/avro/src/test/java/org/apache/avro/data/TestTimeConversions.java
rename to 
lang/java/avro/src/test/java/org/apache/avro/data/TestJodaTimeConversions.java
index 5e315cd1e..ca2bb5042 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/TestJodaTimeConversions.java
@@ -20,13 +20,13 @@
 
 import org.apache.avro.LogicalTypes;
 import org.apache.avro.Schema;
-import org.apache.avro.data.TimeConversions.DateConversion;
-import org.apache.avro.data.TimeConversions.LossyTimeMicrosConversion;
-import org.apache.avro.data.TimeConversions.LossyTimestampMicrosConversion;
-import org.apache.avro.data.TimeConversions.TimeMicrosConversion;
-import org.apache.avro.data.TimeConversions.TimestampMicrosConversion;
-import org.apache.avro.data.TimeConversions.TimeConversion;
-import org.apache.avro.data.TimeConversions.TimestampConversion;
+import org.apache.avro.data.JodaTimeConversions.DateConversion;
+import org.apache.avro.data.JodaTimeConversions.LossyTimeMicrosConversion;
+import org.apache.avro.data.JodaTimeConversions.LossyTimestampMicrosConversion;
+import org.apache.avro.data.JodaTimeConversions.TimeMicrosConversion;
+import org.apache.avro.data.JodaTimeConversions.TimestampMicrosConversion;
+import org.apache.avro.data.JodaTimeConversions.TimeConversion;
+import org.apache.avro.data.JodaTimeConversions.TimestampConversion;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
@@ -36,7 +36,7 @@
 import org.junit.Test;
 import java.util.Date;
 
-public class TestTimeConversions {
+public class TestJodaTimeConversions {
 
   public static Schema DATE_SCHEMA;
   public static Schema TIME_MILLIS_SCHEMA;
@@ -46,15 +46,15 @@
 
   @BeforeClass
   public static void createSchemas() {
-    TestTimeConversions.DATE_SCHEMA = LogicalTypes.date()
+    TestJodaTimeConversions.DATE_SCHEMA = LogicalTypes.date()
         .addToSchema(Schema.create(Schema.Type.INT));
-    TestTimeConversions.TIME_MILLIS_SCHEMA = LogicalTypes.timeMillis()
+    TestJodaTimeConversions.TIME_MILLIS_SCHEMA = LogicalTypes.timeMillis()
         .addToSchema(Schema.create(Schema.Type.INT));
-    TestTimeConversions.TIME_MICROS_SCHEMA = LogicalTypes.timeMicros()
+    TestJodaTimeConversions.TIME_MICROS_SCHEMA = LogicalTypes.timeMicros()
         .addToSchema(Schema.create(Schema.Type.LONG));
-    TestTimeConversions.TIMESTAMP_MILLIS_SCHEMA = 
LogicalTypes.timestampMillis()
+    TestJodaTimeConversions.TIMESTAMP_MILLIS_SCHEMA = 
LogicalTypes.timestampMillis()
         .addToSchema(Schema.create(Schema.Type.LONG));
-    TestTimeConversions.TIMESTAMP_MICROS_SCHEMA = 
LogicalTypes.timestampMicros()
+    TestJodaTimeConversions.TIMESTAMP_MICROS_SCHEMA = 
LogicalTypes.timestampMicros()
         .addToSchema(Schema.create(Schema.Type.LONG));
   }
 
diff --git 
a/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJava8LogicalTypes.java
 
b/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJava8LogicalTypes.java
new file mode 100644
index 000000000..522a2c025
--- /dev/null
+++ 
b/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJava8LogicalTypes.java
@@ -0,0 +1,893 @@
+/**
+ * Autogenerated by Avro
+ *
+ * DO NOT EDIT DIRECTLY
+ */
+package org.apache.avro.specific;
+
+import org.apache.avro.specific.SpecificData;
+import org.apache.avro.message.BinaryMessageEncoder;
+import org.apache.avro.message.BinaryMessageDecoder;
+import org.apache.avro.message.SchemaStore;
+
+@SuppressWarnings("all")
+@org.apache.avro.specific.AvroGenerated
+public class TestRecordWithJava8LogicalTypes 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\":\"TestRecordWithJava8LogicalTypes\",\"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\",\"logicalType\":\"time-millis\"}},{\"name\":\"ts\",\"type\":{\"type\":\"long\",\"logicalType\":\"timestamp-millis\"}},{\"name\":\"dec\",\"type\":{\"type\":\"bytes\",\"logicalType\":\"decimal\",\"precision\":9,\"scale\":2}}]}");
+  public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }
+
+  private static SpecificData MODEL$ = new SpecificData();
+
+  private static final BinaryMessageEncoder<TestRecordWithJava8LogicalTypes> 
ENCODER =
+      new BinaryMessageEncoder<TestRecordWithJava8LogicalTypes>(MODEL$, 
SCHEMA$);
+
+  private static final BinaryMessageDecoder<TestRecordWithJava8LogicalTypes> 
DECODER =
+      new BinaryMessageDecoder<TestRecordWithJava8LogicalTypes>(MODEL$, 
SCHEMA$);
+
+  /**
+   * Return the BinaryMessageDecoder instance used by this class.
+   */
+  public static BinaryMessageDecoder<TestRecordWithJava8LogicalTypes> 
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<TestRecordWithJava8LogicalTypes> 
createDecoder(SchemaStore resolver) {
+    return new BinaryMessageDecoder<TestRecordWithJava8LogicalTypes>(MODEL$, 
SCHEMA$, resolver);
+  }
+
+  /** Serializes this TestRecordWithJava8LogicalTypes to a ByteBuffer. */
+  public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {
+    return ENCODER.encode(this);
+  }
+
+  /** Deserializes a TestRecordWithJava8LogicalTypes from a ByteBuffer. */
+  public static TestRecordWithJava8LogicalTypes 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 TestRecordWithJava8LogicalTypes() {}
+
+  /**
+   * 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 TestRecordWithJava8LogicalTypes(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;
+  }
+
+  public org.apache.avro.Schema getSchema() { return SCHEMA$; }
+  // Used by DatumWriter.  Applications should not call.
+  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 
org.apache.avro.data.Java8TimeConversions.DateConversion DATE_CONVERSION = new 
org.apache.avro.data.Java8TimeConversions.DateConversion();
+  protected static final 
org.apache.avro.data.Java8TimeConversions.TimeMillisConversion TIME_CONVERSION 
= new org.apache.avro.data.Java8TimeConversions.TimeMillisConversion();
+  protected static final 
org.apache.avro.data.Java8TimeConversions.TimestampMillisConversion 
TIMESTAMP_CONVERSION = new 
org.apache.avro.data.Java8TimeConversions.TimestampMillisConversion();
+
+  private static final org.apache.avro.Conversion<?>[] conversions =
+      new org.apache.avro.Conversion<?>[] {
+      null,
+      null,
+      null,
+      null,
+      null,
+      null,
+      DATE_CONVERSION,
+      TIME_CONVERSION,
+      TIMESTAMP_CONVERSION,
+      DECIMAL_CONVERSION,
+      null
+  };
+
+  @Override
+  public org.apache.avro.Conversion<?> getConversion(int field) {
+    return conversions[field];
+  }
+
+  // Used by DatumReader.  Applications should not call.
+  @SuppressWarnings(value="unchecked")
+  public void put(int field$, java.lang.Object value$) {
+    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 TestRecordWithJava8LogicalTypes RecordBuilder.
+   * @return A new TestRecordWithJava8LogicalTypes RecordBuilder
+   */
+  public static 
org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder newBuilder() {
+    return new 
org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder();
+  }
+
+  /**
+   * Creates a new TestRecordWithJava8LogicalTypes RecordBuilder by copying an 
existing Builder.
+   * @param other The existing builder to copy.
+   * @return A new TestRecordWithJava8LogicalTypes RecordBuilder
+   */
+  public static 
org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder 
newBuilder(org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder 
other) {
+    if (other == null) {
+      return new 
org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder();
+    } else {
+      return new 
org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder(other);
+    }
+  }
+
+  /**
+   * Creates a new TestRecordWithJava8LogicalTypes RecordBuilder by copying an 
existing TestRecordWithJava8LogicalTypes instance.
+   * @param other The existing instance to copy.
+   * @return A new TestRecordWithJava8LogicalTypes RecordBuilder
+   */
+  public static 
org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder 
newBuilder(org.apache.avro.specific.TestRecordWithJava8LogicalTypes other) {
+    if (other == null) {
+      return new 
org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder();
+    } else {
+      return new 
org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder(other);
+    }
+  }
+
+  /**
+   * RecordBuilder for TestRecordWithJava8LogicalTypes instances.
+   */
+  public static class Builder extends 
org.apache.avro.specific.SpecificRecordBuilderBase<TestRecordWithJava8LogicalTypes>
+    implements 
org.apache.avro.data.RecordBuilder<TestRecordWithJava8LogicalTypes> {
+
+    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(org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 
TestRecordWithJava8LogicalTypes instance
+     * @param other The existing instance to copy.
+     */
+    private Builder(org.apache.avro.specific.TestRecordWithJava8LogicalTypes 
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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.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 org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder 
clearDec() {
+      dec = null;
+      fieldSetFlags()[9] = false;
+      return this;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public TestRecordWithJava8LogicalTypes build() {
+      try {
+        TestRecordWithJava8LogicalTypes record = new 
TestRecordWithJava8LogicalTypes();
+        record.b = fieldSetFlags()[0] ? this.b : (java.lang.Boolean) 
defaultValue(fields()[0], record.getConversion(0));
+        record.i32 = fieldSetFlags()[1] ? this.i32 : (java.lang.Integer) 
defaultValue(fields()[1], record.getConversion(1));
+        record.i64 = fieldSetFlags()[2] ? this.i64 : (java.lang.Long) 
defaultValue(fields()[2], record.getConversion(2));
+        record.f32 = fieldSetFlags()[3] ? this.f32 : (java.lang.Float) 
defaultValue(fields()[3], record.getConversion(3));
+        record.f64 = fieldSetFlags()[4] ? this.f64 : (java.lang.Double) 
defaultValue(fields()[4], record.getConversion(4));
+        record.s = fieldSetFlags()[5] ? this.s : (java.lang.CharSequence) 
defaultValue(fields()[5], record.getConversion(5));
+        record.d = fieldSetFlags()[6] ? this.d : (java.time.LocalDate) 
defaultValue(fields()[6], record.getConversion(6));
+        record.t = fieldSetFlags()[7] ? this.t : (java.time.LocalTime) 
defaultValue(fields()[7], record.getConversion(7));
+        record.ts = fieldSetFlags()[8] ? this.ts : (java.time.Instant) 
defaultValue(fields()[8], record.getConversion(8));
+        record.dec = fieldSetFlags()[9] ? this.dec : (java.math.BigDecimal) 
defaultValue(fields()[9], record.getConversion(9));
+        return record;
+      } catch (java.lang.Exception e) {
+        throw new org.apache.avro.AvroRuntimeException(e);
+      }
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private static final 
org.apache.avro.io.DatumWriter<TestRecordWithJava8LogicalTypes>
+    WRITER$ = 
(org.apache.avro.io.DatumWriter<TestRecordWithJava8LogicalTypes>)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<TestRecordWithJava8LogicalTypes>
+    READER$ = 
(org.apache.avro.io.DatumReader<TestRecordWithJava8LogicalTypes>)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 da3f878bd..e4853d312 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
@@ -264,9 +264,9 @@ public void setTs(org.joda.time.DateTime value) {
     this.ts = value;
   }
 
-  protected static final org.apache.avro.data.TimeConversions.DateConversion 
DATE_CONVERSION = new org.apache.avro.data.TimeConversions.DateConversion();
-  protected static final org.apache.avro.data.TimeConversions.TimeConversion 
TIME_CONVERSION = new org.apache.avro.data.TimeConversions.TimeConversion();
-  protected static final 
org.apache.avro.data.TimeConversions.TimestampConversion TIMESTAMP_CONVERSION = 
new org.apache.avro.data.TimeConversions.TimestampConversion();
+  protected static final 
org.apache.avro.data.JodaTimeConversions.DateConversion DATE_CONVERSION = new 
org.apache.avro.data.JodaTimeConversions.DateConversion();
+  protected static final 
org.apache.avro.data.JodaTimeConversions.TimeConversion TIME_CONVERSION = new 
org.apache.avro.data.JodaTimeConversions.TimeConversion();
+  protected static final 
org.apache.avro.data.JodaTimeConversions.TimestampConversion 
TIMESTAMP_CONVERSION = new 
org.apache.avro.data.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<?>[] {
diff --git 
a/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java
 
b/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java
index 64d45cb5e..ccaec55e1 100644
--- 
a/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java
+++ 
b/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java
@@ -20,9 +20,9 @@
 import org.apache.avro.Conversions;
 import org.apache.avro.LogicalTypes;
 import org.apache.avro.Schema;
-import org.apache.avro.data.TimeConversions.DateConversion;
-import org.apache.avro.data.TimeConversions.TimeConversion;
-import org.apache.avro.data.TimeConversions.TimestampConversion;
+import org.apache.avro.data.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;
@@ -39,9 +39,15 @@
 import java.io.File;
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.time.ZoneOffset;
 import java.util.ArrayList;
 import java.util.List;
 
+import static org.hamcrest.Matchers.comparesEqualTo;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+
 /**
  * This tests compatibility between classes generated before and after
  * AVRO-1684. TestRecordWithoutLogicalTypes and TestRecordWithLogicalTypes were
@@ -53,6 +59,10 @@
  * 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 TestRecordWithJava8LogicalTypes} was generated from
+ * the same schema and tests were added to test compatibility between the
+ * two versions.
  */
 public class TestSpecificLogicalTypes {
 
@@ -80,6 +90,97 @@ public void testRecordWithLogicalTypes() throws IOException {
 
     Assert.assertEquals("Should match written record", record, actual.get(0));
   }
+  @Test
+  public void testRecordWithJava8LogicalTypes() throws IOException {
+    TestRecordWithJava8LogicalTypes record = new 
TestRecordWithJava8LogicalTypes(
+        true,
+        34,
+        35L,
+        3.14F,
+        3019.34,
+        null,
+        java.time.LocalDate.now(),
+        java.time.LocalTime.now(),
+        java.time.Instant.now(),
+        new BigDecimal(123.45f).setScale(2, BigDecimal.ROUND_HALF_DOWN)
+    );
+
+    File data = write(TestRecordWithJava8LogicalTypes.getClassSchema(), 
record);
+    List<TestRecordWithJava8LogicalTypes> actual = read(
+        TestRecordWithJava8LogicalTypes.getClassSchema(), data);
+
+    Assert.assertEquals("Should match written record", record, actual.get(0));
+  }
+
+  @Test
+  public void testAbilityToReadJava8RecordWrittenAsJodaRecord() throws 
IOException {
+    TestRecordWithLogicalTypes withJoda = new TestRecordWithLogicalTypes(
+            true,
+            34,
+            35L,
+            3.14F,
+            3019.34,
+            null,
+            LocalDate.now(),
+            LocalTime.now(),
+            DateTime.now().withZone(DateTimeZone.UTC),
+            new BigDecimal(123.45f).setScale(2, BigDecimal.ROUND_HALF_DOWN)
+    );
+
+    File data = write(TestRecordWithLogicalTypes.getClassSchema(), withJoda);
+    List<TestRecordWithJava8LogicalTypes> actual = read(
+        TestRecordWithJava8LogicalTypes.getClassSchema(), data);
+
+    Assert.assertThat(actual, is(not(empty())));
+    TestRecordWithJava8LogicalTypes withJava8 = actual.get(0);
+
+    Assert.assertThat(withJava8.getB(), is(withJoda.getB()));
+    Assert.assertThat(withJava8.getI32(), is(withJoda.getI32()));
+    Assert.assertThat(withJava8.getI64(), is(withJoda.getI64()));
+    Assert.assertThat(withJava8.getF32(), is(withJoda.getF32()));
+    Assert.assertThat(withJava8.getF64(), is(withJoda.getF64()));
+    Assert.assertThat(withJava8.getS(), is(withJoda.getS()));
+    // all of these print in the ISO-8601 format
+    Assert.assertThat(withJava8.getD().toString(), 
is(withJoda.getD().toString()));
+    Assert.assertThat(withJava8.getT().toString(), 
is(withJoda.getT().toString()));
+    Assert.assertThat(withJava8.getTs().toString(), 
is(withJoda.getTs().toString()));
+    Assert.assertThat(withJava8.getDec(), comparesEqualTo(withJoda.getDec()));
+  }
+
+  @Test
+  public void testAbilityToReadJodaRecordWrittenAsJava8Record() throws 
IOException {
+    TestRecordWithJava8LogicalTypes withJava8 = new 
TestRecordWithJava8LogicalTypes(
+            true,
+            34,
+            35L,
+            3.14F,
+            3019.34,
+            null,
+            java.time.LocalDate.now(),
+            java.time.LocalTime.now(),
+            java.time.Instant.now(),
+            new BigDecimal(123.45f).setScale(2, BigDecimal.ROUND_HALF_DOWN)
+    );
+
+    File data = write(TestRecordWithJava8LogicalTypes.getClassSchema(), 
withJava8);
+    List<TestRecordWithLogicalTypes> actual = read(
+        TestRecordWithLogicalTypes.getClassSchema(), data);
+
+    Assert.assertThat(actual, is(not(empty())));
+    TestRecordWithLogicalTypes withJoda = actual.get(0);
+
+    Assert.assertThat(withJoda.getB(), is(withJava8.getB()));
+    Assert.assertThat(withJoda.getI32(), is(withJava8.getI32()));
+    Assert.assertThat(withJoda.getI64(), is(withJava8.getI64()));
+    Assert.assertThat(withJoda.getF32(), is(withJava8.getF32()));
+    Assert.assertThat(withJoda.getF64(), is(withJava8.getF64()));
+    Assert.assertThat(withJoda.getS(), is(withJava8.getS()));
+    // all of these print in the ISO-8601 format
+    Assert.assertThat(withJoda.getD().toString(), 
is(withJava8.getD().toString()));
+    Assert.assertThat(withJoda.getT().toString(), 
is(withJava8.getT().toString()));
+    Assert.assertThat(withJoda.getTs().toString(), 
is(withJava8.getTs().toString()));
+    Assert.assertThat(withJoda.getDec(), comparesEqualTo(withJava8.getDec()));
+  }
 
   @Test
   public void testRecordWithoutLogicalTypes() throws IOException {
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 d3a81c315..8773cdbdc 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
@@ -19,7 +19,7 @@
 
 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.message.MissingSchemaException;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
@@ -63,9 +63,9 @@ public void testSpecificToFromByteBufferWithoutLogicalTypes() 
throws IOException
         3.14F,
         3019.34,
         null,
-        new TimeConversions.DateConversion().toInt(LocalDate.now(), null, 
null),
-        new TimeConversions.TimeConversion().toInt(LocalTime.now(), null, 
null),
-        new TimeConversions.TimestampConversion().toLong(
+        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),
         new Conversions.DecimalConversion().toBytes(
             new BigDecimal("123.45"), null, LogicalTypes.decimal(9, 2))
@@ -86,9 +86,9 @@ public void 
testSpecificByteArrayIncompatibleWithLogicalTypes() throws IOExcepti
         3.14F,
         3019.34,
         null,
-        new TimeConversions.DateConversion().toInt(LocalDate.now(), null, 
null),
-        new TimeConversions.TimeConversion().toInt(LocalTime.now(), null, 
null),
-        new TimeConversions.TimestampConversion().toLong(
+        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),
         new Conversions.DecimalConversion().toBytes(
             new BigDecimal("123.45"), null, LogicalTypes.decimal(9, 2))
diff --git a/lang/java/avro/src/test/resources/record_with_logical_types.avsc 
b/lang/java/avro/src/test/resources/record_with_logical_types.avsc
index 9932f9505..f5d212917 100644
--- a/lang/java/avro/src/test/resources/record_with_logical_types.avsc
+++ b/lang/java/avro/src/test/resources/record_with_logical_types.avsc
@@ -1,7 +1,7 @@
 {
   "type" : "record",
   "name" : "TestRecordWithLogicalTypes",
-  "doc" : "Schema for TestRecordWithLogicalTypes and 
TestRecordWithoutLogicalTypes, see TestSpecificLogicalTypes"
+  "doc" : "Schema for TestRecordWithLogicalTypes and 
TestRecordWithoutLogicalTypes, see TestSpecificLogicalTypes",
   "namespace" : "org.apache.avro.specific",
   "fields" : [ {
     "name" : "b",
diff --git 
a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/ProtocolTask.java
 
b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/ProtocolTask.java
index 36bf67b51..71762434b 100644
--- 
a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/ProtocolTask.java
+++ 
b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/ProtocolTask.java
@@ -23,6 +23,7 @@
 
 import org.apache.avro.AvroRuntimeException;
 import org.apache.avro.Protocol;
+import 
org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeType;
 import org.apache.avro.generic.GenericData.StringType;
 
 import org.apache.tools.ant.BuildException;
@@ -36,6 +37,7 @@
   private File src;
   private File dest = new File(".");
   private StringType stringType = StringType.CharSequence;
+  private DateTimeLogicalTypeType dateTimeLogicalTypeType = 
DateTimeLogicalTypeType.JODA;
 
   private final ArrayList<FileSet> filesets = new ArrayList<FileSet>();
 
@@ -51,6 +53,16 @@
   /** Get the string type. */
   public StringType getStringType() { return this.stringType; }
 
+  /** Sets the date/time logical type type (either JODA or JAVA8) */
+  public void setDateTimeLogicalTypeType(DateTimeLogicalTypeType 
dateTimeLogicalTypeType) {
+    this.dateTimeLogicalTypeType = dateTimeLogicalTypeType;
+  }
+
+  /** Get the date/time logical type type (either JODA or JAVA8) */
+  public DateTimeLogicalTypeType getDateTimeLogicalTypeType() {
+    return dateTimeLogicalTypeType;
+  }
+
   /** Add a fileset. */
   public void addFileset(FileSet set) { filesets.add(set); }
 
@@ -77,7 +89,7 @@ public void execute() {
 
   protected void doCompile(File src, File dir) throws IOException {
     Protocol protocol = Protocol.parse(src);
-    SpecificCompiler compiler = new SpecificCompiler(protocol);
+    SpecificCompiler compiler = new SpecificCompiler(protocol, 
getDateTimeLogicalTypeType());
     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 9c5c12aa4..a48a8aa45 100644
--- 
a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SchemaTask.java
+++ 
b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SchemaTask.java
@@ -28,7 +28,7 @@
   protected void doCompile(File src, File dest) throws IOException {
     Schema.Parser parser = new Schema.Parser();
     Schema schema = parser.parse(src);
-    SpecificCompiler compiler = new SpecificCompiler(schema);
+    SpecificCompiler compiler = new SpecificCompiler(schema, 
getDateTimeLogicalTypeType());
     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 7a6f5f1cc..e7c41f202 100644
--- 
a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java
+++ 
b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java
@@ -37,9 +37,8 @@
 import org.apache.avro.Conversion;
 import org.apache.avro.Conversions;
 import org.apache.avro.LogicalTypes;
-import org.apache.avro.data.TimeConversions.DateConversion;
-import org.apache.avro.data.TimeConversions.TimeConversion;
-import org.apache.avro.data.TimeConversions.TimestampConversion;
+import org.apache.avro.data.Java8TimeConversions;
+import org.apache.avro.data.JodaTimeConversions;
 import org.apache.avro.specific.SpecificData;
 import org.codehaus.jackson.JsonNode;
 
@@ -88,18 +87,16 @@
    */
   protected static final int MAX_FIELD_PARAMETER_UNIT_COUNT = 
JVM_METHOD_ARG_LIMIT - 1;
 
-  public static enum FieldVisibility {
+  public enum FieldVisibility {
     PUBLIC, PUBLIC_DEPRECATED, PRIVATE
   }
 
-  private static final SpecificData SPECIFIC = new SpecificData();
-  static {
-    SPECIFIC.addLogicalTypeConversion(new DateConversion());
-    SPECIFIC.addLogicalTypeConversion(new TimeConversion());
-    SPECIFIC.addLogicalTypeConversion(new TimestampConversion());
-    SPECIFIC.addLogicalTypeConversion(new Conversions.DecimalConversion());
+  public enum DateTimeLogicalTypeType {
+    JODA, JAVA8
   }
 
+  private final SpecificData specificData = new SpecificData();
+
   private final Set<Schema> queue = new HashSet<Schema>();
   private Protocol protocol;
   private VelocityEngine velocityEngine;
@@ -109,6 +106,7 @@
   private boolean createAllArgsConstructor = true;
   private String outputCharacterEncoding;
   private boolean enableDecimalLogicalType = false;
+  private DateTimeLogicalTypeType dateTimeLogicalTypeType = 
DateTimeLogicalTypeType.JODA;
   private String suffix = ".java";
 
   /*
@@ -144,7 +142,11 @@ public boolean isCreateAllArgsConstructor() {
       " */\n";
 
   public SpecificCompiler(Protocol protocol) {
-    this();
+    this(protocol, DateTimeLogicalTypeType.JODA);
+  }
+
+  public SpecificCompiler(Protocol protocol, DateTimeLogicalTypeType 
dateTimeLogicalTypeType) {
+    this(dateTimeLogicalTypeType);
     // enqueue all types
     for (Schema s : protocol.getTypes()) {
       enqueue(s);
@@ -153,16 +155,38 @@ public SpecificCompiler(Protocol protocol) {
   }
 
   public SpecificCompiler(Schema schema) {
-    this();
+    this(schema, DateTimeLogicalTypeType.JODA);
+  }
+
+  public SpecificCompiler(Schema schema, DateTimeLogicalTypeType 
dateTimeLogicalTypeType) {
+    this(dateTimeLogicalTypeType);
     enqueue(schema);
     this.protocol = null;
   }
 
+  /**
+   * Creates a specific compler with the default (Joda) type for date/time 
related logical types.
+   *
+   * @see #SpecificCompiler(DateTimeLogicalTypeType)
+   */
   SpecificCompiler() {
+    this(DateTimeLogicalTypeType.JODA);
+  }
+
+  /**
+   * Creates a specific compiler with the given type to use for date/time 
related logical types.
+   * Use {@link DateTimeLogicalTypeType#JODA} to generate Joda Time classes, 
use {@link DateTimeLogicalTypeType#JAVA8}
+   * to generate {@code java.time.*} classes for the date/time local types.
+   *
+   * @param dateTimeLogicalTypeType the types used for date/time related 
logical types
+   */
+  SpecificCompiler(DateTimeLogicalTypeType dateTimeLogicalTypeType) {
+    this.dateTimeLogicalTypeType = dateTimeLogicalTypeType;
     this.templateDir =
       System.getProperty("org.apache.avro.specific.templates",
                          
"/org/apache/avro/compiler/specific/templates/java/classic/");
     initializeVelocity();
+    initializeSpecificData();
   }
 
   /** Set the resource directory where templates reside. First, the compiler 
checks
@@ -225,6 +249,14 @@ public void setEnableDecimalLogicalType(boolean 
enableDecimalLogicalType) {
     this.enableDecimalLogicalType = enableDecimalLogicalType;
   }
 
+  public boolean useJodaForDateTimeLogicalTypes() {
+    return dateTimeLogicalTypeType == DateTimeLogicalTypeType.JODA;
+  }
+
+  public boolean useJava8ForDateTimeLogicalTypes() {
+    return dateTimeLogicalTypeType == DateTimeLogicalTypeType.JAVA8;
+  }
+
   private static String logChuteName = null;
 
   private void initializeVelocity() {
@@ -253,6 +285,20 @@ private void initializeVelocity() {
     velocityEngine.setProperty("runtime.log.logsystem.class", logChuteName);
   }
 
+  private void initializeSpecificData() {
+    if(dateTimeLogicalTypeType == DateTimeLogicalTypeType.JODA) {
+      specificData.addLogicalTypeConversion(new 
JodaTimeConversions.DateConversion());
+      specificData.addLogicalTypeConversion(new 
JodaTimeConversions.TimeConversion());
+      specificData.addLogicalTypeConversion(new 
JodaTimeConversions.TimestampConversion());
+    } else if (dateTimeLogicalTypeType == DateTimeLogicalTypeType.JAVA8) {
+      specificData.addLogicalTypeConversion(new 
Java8TimeConversions.DateConversion());
+      specificData.addLogicalTypeConversion(new 
Java8TimeConversions.TimeMillisConversion());
+      specificData.addLogicalTypeConversion(new 
Java8TimeConversions.TimestampMillisConversion());
+    }
+
+    specificData.addLogicalTypeConversion(new Conversions.DecimalConversion());
+  }
+
   /**
    * Captures output file path and contents.
    */
@@ -324,7 +370,7 @@ public static void compileSchema(File[] srcFiles, File 
dest) throws IOException
 
     for (File src : srcFiles) {
       Schema schema = parser.parse(src);
-      SpecificCompiler compiler = new SpecificCompiler(schema);
+      SpecificCompiler compiler = new SpecificCompiler(schema, 
DateTimeLogicalTypeType.JODA);
       compiler.compileToDestination(src, dest);
     }
   }
@@ -630,7 +676,7 @@ private String javaType(Schema schema, boolean 
checkConvertedLogicalType) {
   private String getConvertedLogicalType(Schema schema) {
     if (enableDecimalLogicalType
         || !(schema.getLogicalType() instanceof LogicalTypes.Decimal)) {
-      Conversion<?> conversion = SPECIFIC
+      Conversion<?> conversion = specificData
           .getConversionFor(schema.getLogicalType());
       if (conversion != null) {
         return conversion.getConvertedType().getName();
diff --git 
a/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm
 
b/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm
index ccec4b60c..aa566486e 100644
--- 
a/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm
+++ 
b/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm
@@ -151,10 +151,16 @@ public class ${this.mangle($schema.getName())}#if 
($schema.isError()) extends or
   }
 
 #if ($this.hasLogicalTypeField($schema))
-  protected static final org.apache.avro.data.TimeConversions.DateConversion 
DATE_CONVERSION = new org.apache.avro.data.TimeConversions.DateConversion();
-  protected static final org.apache.avro.data.TimeConversions.TimeConversion 
TIME_CONVERSION = new org.apache.avro.data.TimeConversions.TimeConversion();
-  protected static final 
org.apache.avro.data.TimeConversions.TimestampConversion TIMESTAMP_CONVERSION = 
new org.apache.avro.data.TimeConversions.TimestampConversion();
   protected static final org.apache.avro.Conversions.DecimalConversion 
DECIMAL_CONVERSION = new org.apache.avro.Conversions.DecimalConversion();
+#if ($this.useJodaForDateTimeLogicalTypes())
+  protected static final 
org.apache.avro.data.JodaTimeConversions.DateConversion DATE_CONVERSION = new 
org.apache.avro.data.JodaTimeConversions.DateConversion();
+  protected static final 
org.apache.avro.data.JodaTimeConversions.TimeConversion TIME_CONVERSION = new 
org.apache.avro.data.JodaTimeConversions.TimeConversion();
+  protected static final 
org.apache.avro.data.JodaTimeConversions.TimestampConversion 
TIMESTAMP_CONVERSION = new 
org.apache.avro.data.JodaTimeConversions.TimestampConversion();
+#elseif ($this.useJava8ForDateTimeLogicalTypes())
+  protected static final 
org.apache.avro.data.Java8TimeConversions.DateConversion DATE_CONVERSION = new 
org.apache.avro.data.Java8TimeConversions.DateConversion();
+  protected static final 
org.apache.avro.data.Java8TimeConversions.TimeMillisConversion TIME_CONVERSION 
= new org.apache.avro.data.Java8TimeConversions.TimeMillisConversion();
+  protected static final 
org.apache.avro.data.Java8TimeConversions.TimestampMillisConversion 
TIMESTAMP_CONVERSION = new 
org.apache.avro.data.Java8TimeConversions.TimestampMillisConversion();
+#end
 
   private static final org.apache.avro.Conversion<?>[] conversions =
       new org.apache.avro.Conversion<?>[] {
diff --git 
a/lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java
 
b/lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java
index 35d53b524..08fd55c5b 100644
--- 
a/lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java
+++ 
b/lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java
@@ -17,6 +17,8 @@
  */
 package org.apache.avro.compiler.specific;
 
+import static 
org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeType.JODA;
+import static 
org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeType.JAVA8;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -39,6 +41,7 @@
 import org.apache.avro.LogicalTypes;
 import org.apache.avro.Schema;
 import org.apache.avro.SchemaBuilder;
+import 
org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeType;
 import org.apache.avro.generic.GenericData.StringType;
 import org.junit.After;
 import org.junit.Assert;
@@ -112,9 +115,13 @@ private static Schema createSampleRecordSchema(int 
numStringFields, int numDoubl
   }
 
   private SpecificCompiler createCompiler() throws IOException {
+    return createCompiler(JODA);
+  }
+
+  private SpecificCompiler createCompiler(DateTimeLogicalTypeType 
dateTimeLogicalTypeType) throws IOException {
     Schema.Parser parser = new Schema.Parser();
     Schema schema = parser.parse(this.src);
-    SpecificCompiler compiler = new SpecificCompiler(schema);
+    SpecificCompiler compiler = new SpecificCompiler(schema, 
dateTimeLogicalTypeType);
     compiler.setTemplateDir(this.velocityTemplateDir);
     compiler.setStringType(StringType.CharSequence);
     return compiler;
@@ -377,6 +384,26 @@ public void testJavaTypeWithDecimalLogicalTypeDisabled() 
throws Exception {
         "java.lang.CharSequence", compiler.javaType(uuidSchema));
   }
 
+  @Test
+  public void testJavaTypeWithJava8DateTimeTypes() throws Exception {
+    SpecificCompiler compiler = createCompiler(JAVA8);
+
+    Schema dateSchema = LogicalTypes.date()
+        .addToSchema(Schema.create(Schema.Type.INT));
+    Schema timeSchema = LogicalTypes.timeMillis()
+        .addToSchema(Schema.create(Schema.Type.INT));
+    Schema timestampSchema = LogicalTypes.timestampMillis()
+        .addToSchema(Schema.create(Schema.Type.LONG));
+
+    // Date/time types should always use upper level java classes
+    Assert.assertEquals("Should use java.time.LocalDate for date type",
+        "java.time.LocalDate", compiler.javaType(dateSchema));
+    Assert.assertEquals("Should use java.time.LocalTime for time-millis type",
+        "java.time.LocalTime", compiler.javaType(timeSchema));
+    Assert.assertEquals("Should use java.time.Instant for timestamp-millis 
type",
+        "java.time.Instant", compiler.javaType(timestampSchema));
+  }
+
   @Test
   public void testJavaUnbox() throws Exception {
     SpecificCompiler compiler = createCompiler();
@@ -412,7 +439,26 @@ public void testJavaUnbox() throws Exception {
         "org.joda.time.LocalTime", compiler.javaUnbox(timeSchema));
     Assert.assertEquals("Should use Joda DateTime for timestamp-millis type",
         "org.joda.time.DateTime", compiler.javaUnbox(timestampSchema));
+  }
 
+  @Test
+  public void testJavaUnboxJava8DateTime() throws Exception {
+    SpecificCompiler compiler = createCompiler(JAVA8);
+
+    Schema dateSchema = LogicalTypes.date()
+        .addToSchema(Schema.create(Schema.Type.INT));
+    Schema timeSchema = LogicalTypes.timeMillis()
+        .addToSchema(Schema.create(Schema.Type.INT));
+    Schema timestampSchema = LogicalTypes.timestampMillis()
+        .addToSchema(Schema.create(Schema.Type.LONG));
+    // Date/time types should always use upper level java classes, even though
+    // their underlying representations are primitive types
+    Assert.assertEquals("Should use java.time.LocalDate for date type",
+        "java.time.LocalDate", compiler.javaUnbox(dateSchema));
+    Assert.assertEquals("Should use java.time.LocalTime for time-millis type",
+        "java.time.LocalTime", compiler.javaUnbox(timeSchema));
+    Assert.assertEquals("Should use java.time.Instant for timestamp-millis 
type",
+        "java.time.Instant", compiler.javaUnbox(timestampSchema));
   }
 
   @Test
@@ -475,6 +521,14 @@ public void testLogicalTypesWithMultipleFields() throws 
Exception {
         new SpecificCompiler(logicalTypesWithMultipleFields).compile());
   }
 
+  @Test
+  public void testLogicalTypesWithMultipleFieldsJava8DateTime() throws 
Exception {
+    Schema logicalTypesWithMultipleFields = new Schema.Parser().parse(
+        new 
File("src/test/resources/logical_types_with_multiple_fields.avsc"));
+    assertCompilesWithJavaCompiler(
+        new SpecificCompiler(logicalTypesWithMultipleFields, JAVA8).compile());
+  }
+
   @Test
   public void testConversionInstanceWithDecimalLogicalTypeDisabled() throws 
Exception {
     SpecificCompiler compiler = createCompiler();
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 9d78ede91..69edf3c09 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
@@ -129,6 +129,14 @@
    */
   protected boolean enableDecimalLogicalType;
 
+  /**
+   * Determines which type of classes to generate for date/time related 
logical types. Either 'joda' or 'java8'.
+   * Defaults to joda for backwards compatibility reasons.
+   *
+   * @parameter default-value="joda"
+   */
+  protected String dateTimeLogicalTypeType = 
SpecificCompiler.DateTimeLogicalTypeType.JODA.name().toLowerCase();
+
   /**
    * The current Maven project.
    *
@@ -237,6 +245,15 @@ private void compileFiles(String[] files, File sourceDir, 
File outDir) throws Mo
     }
   }
 
+  protected SpecificCompiler.DateTimeLogicalTypeType 
getDateTimeLocalTypeType() {
+    try {
+      String upper = 
String.valueOf(this.dateTimeLogicalTypeType).trim().toUpperCase();
+      return SpecificCompiler.DateTimeLogicalTypeType.valueOf(upper);
+    } catch (IllegalArgumentException e) {
+      return SpecificCompiler.DateTimeLogicalTypeType.JODA;
+    }
+  }
+
   protected abstract void doCompile(String filename, File sourceDirectory, 
File outputDirectory) throws IOException;
 
   protected abstract String[] getIncludes();
diff --git 
a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/IDLProtocolMojo.java
 
b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/IDLProtocolMojo.java
index 3f61ac2a2..f82b30ad3 100644
--- 
a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/IDLProtocolMojo.java
+++ 
b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/IDLProtocolMojo.java
@@ -88,7 +88,7 @@ protected void doCompile(String filename, File 
sourceDirectory, File outputDirec
       Protocol p = parser.CompilationUnit();
       String json = p.toString(true);
       Protocol protocol = Protocol.parse(json);
-      SpecificCompiler compiler = new SpecificCompiler(protocol);
+      SpecificCompiler compiler = new SpecificCompiler(protocol, 
getDateTimeLocalTypeType());
       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 a30a08245..a2f898c98 100644
--- 
a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/ProtocolMojo.java
+++ 
b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/ProtocolMojo.java
@@ -56,7 +56,7 @@
   protected void doCompile(String filename, File sourceDirectory, File 
outputDirectory) throws IOException {
     File src = new File(sourceDirectory, filename);
     Protocol protocol = Protocol.parse(src);
-    SpecificCompiler compiler = new SpecificCompiler(protocol);
+    SpecificCompiler compiler = new SpecificCompiler(protocol, 
getDateTimeLocalTypeType());
     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 9a849207f..48b73f397 100644
--- a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java
+++ b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java
@@ -73,7 +73,7 @@ protected void doCompile(String filename, File 
sourceDirectory, File outputDirec
       schema = schemaParser.parse(src);
     }
 
-    SpecificCompiler compiler = new SpecificCompiler(schema);
+    SpecificCompiler compiler = new SpecificCompiler(schema, 
getDateTimeLocalTypeType());
     compiler.setTemplateDir(templateDirectory);
     compiler.setStringType(StringType.valueOf(stringType));
     compiler.setFieldVisibility(getFieldVisibility());
diff --git a/lang/java/maven-plugin/src/test/avro/User.avdl 
b/lang/java/maven-plugin/src/test/avro/User.avdl
index 4d4fa5a9a..cb63ee528 100644
--- a/lang/java/maven-plugin/src/test/avro/User.avdl
+++ b/lang/java/maven-plugin/src/test/avro/User.avdl
@@ -25,6 +25,7 @@ protocol IdlTest {
     record IdlUser {
       union { null, string } id;
       union { null, long } createdOn;
+      timestamp_ms modifiedOn;
       union { null, IdlPrivacy } privacy;
     }
 
diff --git a/lang/java/maven-plugin/src/test/avro/User.avpr 
b/lang/java/maven-plugin/src/test/avro/User.avpr
index 9d8a10958..6dd8b9b89 100644
--- a/lang/java/maven-plugin/src/test/avro/User.avpr
+++ b/lang/java/maven-plugin/src/test/avro/User.avpr
@@ -27,6 +27,13 @@
           "name": "privacy", 
           "type": ["null", "ProtocolPrivacy"],
           "default": null
+        },
+        {
+          "name": "modifiedOn",
+          "type": {
+            "type": "long",
+            "logicalType": "timestamp-millis"
+          }
         }
       ]
     } 
diff --git a/lang/java/maven-plugin/src/test/avro/User.avsc 
b/lang/java/maven-plugin/src/test/avro/User.avsc
index 0eb5e096e..a93e0d13f 100644
--- a/lang/java/maven-plugin/src/test/avro/User.avsc
+++ b/lang/java/maven-plugin/src/test/avro/User.avsc
@@ -33,6 +33,13 @@
             "name": "privacyDirectImport", 
             "type": ["null", "test.PrivacyDirectImport"],
             "default": null
+        },
+        {
+            "name": "time",
+            "type": {
+                "type": "long",
+                "logicalType": "timestamp-millis"
+            }
         }
     ]
 }
diff --git 
a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/AbstractAvroMojoTest.java
 
b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/AbstractAvroMojoTest.java
index 23487814e..24e79c064 100644
--- 
a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/AbstractAvroMojoTest.java
+++ 
b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/AbstractAvroMojoTest.java
@@ -20,14 +20,18 @@
 import java.io.File;
 import java.util.Arrays;
 import java.util.List;
+
 import org.apache.maven.plugin.testing.AbstractMojoTestCase;
 
+import static java.util.Arrays.stream;
+import static java.util.stream.Collectors.toList;
+
 /**
  * Base class for all Arvo mojo test classes.
  *
  * @author saden
  */
-public class AbstractAvroMojoTest extends AbstractMojoTestCase {
+public abstract class AbstractAvroMojoTest extends AbstractMojoTestCase {
 
   @Override
   protected void setUp() throws Exception {
diff --git 
a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestIDLProtocolMojo.java
 
b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestIDLProtocolMojo.java
index 931687284..20cb36011 100644
--- 
a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestIDLProtocolMojo.java
+++ 
b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestIDLProtocolMojo.java
@@ -17,7 +17,13 @@
  */
 package org.apache.avro.mojo;
 
+import org.codehaus.plexus.util.FileUtils;
+
 import java.io.File;
+import java.io.FileReader;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
 
 /**
  * Test the IDL Protocol Mojo.
@@ -26,19 +32,36 @@
  */
 public class TestIDLProtocolMojo extends AbstractAvroMojoTest {
 
-  protected File testPom = new File(getBasedir(),
-          "src/test/resources/unit/idl/pom.xml");
+  protected File jodaTestPom = new File(getBasedir(),
+          "src/test/resources/unit/idl/pom-joda.xml");
+  protected File java8TestPom = new File(getBasedir(),
+          "src/test/resources/unit/idl/pom-java8.xml");
+
+  public void testIdlProtocolMojoJoda() throws Exception {
+    IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", 
jodaTestPom);
+
+    assertNotNull(mojo);
+    mojo.execute();
+
+    File outputDir = new File(getBasedir(), 
"target/test-harness/idl-joda/test");
+    String[] generatedFileNames = new String[]{"IdlPrivacy.java",
+      "IdlTest.java", "IdlUser.java", "IdlUserWrapper.java"};
+
+    String idlUserContent = FileUtils.fileRead(new File(outputDir, 
"IdlUser.java"));
+    assertTrue(idlUserContent.contains("org.joda.time.DateTime"));
+  }
 
-  public void testIdlProtocolMojo() throws Exception {
-    IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", 
testPom);
+  public void testIdlProtocolMojoJava8() throws Exception {
+    IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", 
java8TestPom);
 
     assertNotNull(mojo);
     mojo.execute();
 
-    File outputDir = new File(getBasedir(), "target/test-harness/idl/test");
-    String[] generatedFiles = new String[]{"IdlPrivacy.java",
+    File outputDir = new File(getBasedir(), 
"target/test-harness/idl-java8/test");
+    String[] generatedFileNames = new String[]{"IdlPrivacy.java",
       "IdlTest.java", "IdlUser.java", "IdlUserWrapper.java"};
 
-    assertFilesExist(outputDir, generatedFiles);
+    String idlUserContent = FileUtils.fileRead(new File(outputDir, 
"IdlUser.java"));
+    assertTrue(idlUserContent.contains("java.time.Instant"));
   }
 }
diff --git 
a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestProtocolMojo.java
 
b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestProtocolMojo.java
index 342c155f4..7f5890d02 100644
--- 
a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestProtocolMojo.java
+++ 
b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestProtocolMojo.java
@@ -17,6 +17,8 @@
  */
 package org.apache.avro.mojo;
 
+import org.codehaus.plexus.util.FileUtils;
+
 import java.io.File;
 
 /**
@@ -26,19 +28,40 @@
  */
 public class TestProtocolMojo extends AbstractAvroMojoTest {
 
-  protected File testPom = new File(getBasedir(),
-          "src/test/resources/unit/protocol/pom.xml");
+  protected File jodaTestPom = new File(getBasedir(),
+          "src/test/resources/unit/protocol/pom-joda.xml");
+  protected File java8TestPom = new File(getBasedir(),
+          "src/test/resources/unit/protocol/pom-java8.xml");
 
-  public void testProtocolMojo() throws Exception {
-    ProtocolMojo mojo = (ProtocolMojo) lookupMojo("protocol", testPom);
+  public void testProtocolMojoJoda() throws Exception {
+    ProtocolMojo mojo = (ProtocolMojo) lookupMojo("protocol", jodaTestPom);
 
     assertNotNull(mojo);
     mojo.execute();
 
-    File outputDir = new File(getBasedir(), 
"target/test-harness/protocol/test");
+    File outputDir = new File(getBasedir(), 
"target/test-harness/protocol-joda/test");
     String[] generatedFiles = new String[]{"ProtocolPrivacy.java",
       "ProtocolTest.java", "ProtocolUser.java"};
 
     assertFilesExist(outputDir, generatedFiles);
+
+    String protocolUserContent = FileUtils.fileRead(new File(outputDir, 
"ProtocolUser.java"));
+    assertTrue(protocolUserContent.contains("org.joda.time.DateTime"));
+  }
+
+  public void testProtocolMojoJava8() throws Exception {
+    ProtocolMojo mojo = (ProtocolMojo) lookupMojo("protocol", java8TestPom);
+
+    assertNotNull(mojo);
+    mojo.execute();
+
+    File outputDir = new File(getBasedir(), 
"target/test-harness/protocol-java8/test");
+    String[] generatedFiles = new String[]{"ProtocolPrivacy.java",
+            "ProtocolTest.java", "ProtocolUser.java"};
+
+    assertFilesExist(outputDir, generatedFiles);
+
+    String protocolUserContent = FileUtils.fileRead(new File(outputDir, 
"ProtocolUser.java"));
+    assertTrue(protocolUserContent.contains("java.time.Instant"));
   }
 }
diff --git 
a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestSchemaMojo.java 
b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestSchemaMojo.java
index 3e7fe7454..fb2ab9103 100644
--- 
a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestSchemaMojo.java
+++ 
b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestSchemaMojo.java
@@ -17,6 +17,8 @@
  */
 package org.apache.avro.mojo;
 
+import org.codehaus.plexus.util.FileUtils;
+
 import java.io.File;
 
 /**
@@ -26,19 +28,40 @@
  */
 public class TestSchemaMojo extends AbstractAvroMojoTest {
 
-  protected File testPom = new File(getBasedir(),
-          "src/test/resources/unit/schema/pom.xml");
+  protected File jodaTestPom = new File(getBasedir(),
+          "src/test/resources/unit/schema/pom-joda.xml");
+  protected File java8TestPom = new File(getBasedir(),
+          "src/test/resources/unit/schema/pom-java8.xml");
 
-  public void testSchemaMojo() throws Exception {
-    SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", testPom);
+  public void testSchemaMojoJoda() throws Exception {
+    SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", jodaTestPom);
 
     assertNotNull(mojo);
     mojo.execute();
 
-    File outputDir = new File(getBasedir(), "target/test-harness/schema/test");
+    File outputDir = new File(getBasedir(), 
"target/test-harness/schema-joda/test");
     String[] generatedFiles = new String[]{"PrivacyDirectImport.java",
       "PrivacyImport.java", "SchemaPrivacy.java", "SchemaUser.java"};
 
     assertFilesExist(outputDir, generatedFiles);
+
+    String schemaUserContent = FileUtils.fileRead(new File(outputDir, 
"SchemaUser.java"));
+    assertTrue(schemaUserContent.contains("org.joda.time.DateTime"));
+  }
+
+  public void testSchemaMojoJava8() throws Exception {
+    SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", java8TestPom);
+
+    assertNotNull(mojo);
+    mojo.execute();
+
+    File outputDir = new File(getBasedir(), 
"target/test-harness/schema-java8/test");
+    String[] generatedFiles = new String[]{"PrivacyDirectImport.java",
+            "PrivacyImport.java", "SchemaPrivacy.java", "SchemaUser.java"};
+
+    assertFilesExist(outputDir, generatedFiles);
+
+    String schemaUserContent = FileUtils.fileRead(new File(outputDir, 
"SchemaUser.java"));
+    assertTrue(schemaUserContent.contains("java.time.Instant"));
   }
 }
diff --git a/lang/java/maven-plugin/src/test/resources/unit/idl/pom-java8.xml 
b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-java8.xml
new file mode 100644
index 000000000..099a7e7a7
--- /dev/null
+++ b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-java8.xml
@@ -0,0 +1,67 @@
+<?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
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>avro-parent</artifactId>
+    <groupId>org.apache.avro</groupId>
+    <version>1.7.3-SNAPSHOT</version>
+    <relativePath>../../../../../../../../../</relativePath>
+  </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-java8</outputDirectory>
+          <stringType>String</stringType>
+          <dateTimeLogicalTypeType>java8</dateTimeLogicalTypeType>
+          <project 
implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.avro</groupId>
+      <artifactId>avro</artifactId>
+      <version>1.7.3-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-mapper-asl</artifactId>
+      <version>1.9.10</version>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml 
b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-joda.xml
similarity index 92%
rename from lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml
rename to lang/java/maven-plugin/src/test/resources/unit/idl/pom-joda.xml
index 69c504bb3..f5347a649 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-joda.xml
@@ -43,7 +43,9 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test</sourceDirectory>
-          <outputDirectory>${basedir}/target/test-harness/idl</outputDirectory>
+          
<outputDirectory>${basedir}/target/test-harness/idl-joda</outputDirectory>
+          <stringType>String</stringType>
+          <dateTimeLogicalTypeType>joda</dateTimeLogicalTypeType>
           <project 
implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
         </configuration>
       </plugin>
diff --git 
a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-java8.xml 
b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-java8.xml
new file mode 100644
index 000000000..6f1db51d6
--- /dev/null
+++ b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-java8.xml
@@ -0,0 +1,66 @@
+<?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
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>avro-parent</artifactId>
+    <groupId>org.apache.avro</groupId>
+    <version>1.7.3-SNAPSHOT</version>
+    <relativePath>../../../../../../../../../</relativePath>
+  </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-java8</outputDirectory>
+          <dateTimeLogicalTypeType>java8</dateTimeLogicalTypeType>
+          <project 
implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.avro</groupId>
+      <artifactId>avro</artifactId>
+      <version>1.7.3-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-mapper-asl</artifactId>
+      <version>1.9.10</version>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml 
b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-joda.xml
similarity index 98%
rename from lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml
rename to lang/java/maven-plugin/src/test/resources/unit/protocol/pom-joda.xml
index b484e3dd4..d01cafa9b 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-joda.xml
@@ -43,7 +43,7 @@
         </executions>
         <configuration>
           <sourceDirectory>${basedir}/src/test/avro</sourceDirectory>
-          
<outputDirectory>${basedir}/target/test-harness/protocol</outputDirectory>
+          
<outputDirectory>${basedir}/target/test-harness/protocol-joda</outputDirectory>
           <project 
implementation="org.apache.maven.plugin.testing.stubs.MavenProjectStub"/>
         </configuration>
       </plugin>
diff --git 
a/lang/java/maven-plugin/src/test/resources/unit/schema/pom-java8.xml 
b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-java8.xml
new file mode 100644
index 000000000..a43188382
--- /dev/null
+++ b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-java8.xml
@@ -0,0 +1,65 @@
+<?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
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<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>
+
+  <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-java8</outputDirectory>
+          <dateTimeLogicalTypeType>java8</dateTimeLogicalTypeType>
+          <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>1.7.3-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-mapper-asl</artifactId>
+      <version>1.9.10</version>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml 
b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-joda.xml
similarity index 98%
rename from lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml
rename to lang/java/maven-plugin/src/test/resources/unit/schema/pom-joda.xml
index cc000df05..396da470e 100644
--- a/lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml
+++ b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-joda.xml
@@ -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-joda</outputDirectory>
           <imports>
             <import>${basedir}/src/test/avro/imports</import>
             
<import>${basedir}/src/test/avro/directImport/PrivacyDirectImport.avsc</import>
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 2ba6616e1..b0f335670 100644
--- 
a/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java
+++ 
b/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java
@@ -23,12 +23,15 @@
 import java.io.InputStream;
 import java.io.PrintStream;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Optional;
 import java.util.Set;
 import java.util.LinkedHashSet;
 import java.util.List;
 
 import org.apache.avro.Protocol;
 import org.apache.avro.Schema;
+import 
org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeType;
 import org.apache.avro.generic.GenericData.StringType;
 import org.apache.avro.compiler.specific.SpecificCompiler;
 
@@ -43,7 +46,7 @@ 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] (schema|protocol) input... outputdir");
+          .println("Usage: [-encoding <outputencoding>] [-string] 
[-bigDecimal] [-dateTimeLogicalType <dateTimeType>] (schema|protocol) input... 
outputdir");
       System.err
           .println(" input - input files or directories");
       System.err
@@ -53,18 +56,21 @@ public int run(InputStream in, PrintStream out, PrintStream 
err,
       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(" -dateTimeLogicalType [joda|java8] use either " +
+          "Joda time classes (default) or java 8 native date/time classes");
       return 1;
     }
 
     StringType stringType = StringType.CharSequence;
     boolean useLogicalDecimal = false;
+    Optional<DateTimeLogicalTypeType> dateTimeLogicalTypeType = 
Optional.empty();
+    Optional<String> encoding = Optional.empty();
 
     int arg = 0;
 
-    String encoding = null;
     if ("-encoding".equals(args.get(arg))) {
       arg++;
-      encoding = args.get(arg);
+      encoding = Optional.of(args.get(arg));
       arg++;
     }
 
@@ -78,6 +84,17 @@ public int run(InputStream in, PrintStream out, PrintStream 
err,
       arg++;
     }
 
+    if ("-dateTimeLogicalType".equalsIgnoreCase(args.get(arg))) {
+      arg++;
+      try {
+        dateTimeLogicalTypeType = 
Optional.of(DateTimeLogicalTypeType.valueOf(args.get(arg).toUpperCase()));
+      } catch (IllegalArgumentException | IndexOutOfBoundsException e) {
+        System.err.println("Expected one of " + 
Arrays.toString(DateTimeLogicalTypeType.values()));
+        return 1;
+      }
+      arg++;
+    }
+
     String method = args.get(arg);
     List<File> inputs = new ArrayList<File>();
     File output = new File(args.get(args.size() - 1));
@@ -90,13 +107,13 @@ public int run(InputStream in, PrintStream out, 
PrintStream err,
       Schema.Parser parser = new Schema.Parser();
       for (File src : determineInputs(inputs, SCHEMA_FILTER)) {
         Schema schema = parser.parse(src);
-        SpecificCompiler compiler = new SpecificCompiler(schema);
+        SpecificCompiler compiler = new SpecificCompiler(schema, 
dateTimeLogicalTypeType.orElse(DateTimeLogicalTypeType.JODA));
         executeCompiler(compiler, encoding, stringType, useLogicalDecimal, 
src, output);
       }
     } else if ("protocol".equals(method)) {
       for (File src : determineInputs(inputs, PROTOCOL_FILTER)) {
         Protocol protocol = Protocol.parse(src);
-        SpecificCompiler compiler = new SpecificCompiler(protocol);
+        SpecificCompiler compiler = new SpecificCompiler(protocol, 
dateTimeLogicalTypeType.orElse(DateTimeLogicalTypeType.JODA));
         executeCompiler(compiler, encoding, stringType, useLogicalDecimal, 
src, output);
       }
     } else {
@@ -107,16 +124,14 @@ public int run(InputStream in, PrintStream out, 
PrintStream err,
   }
 
   private void executeCompiler(SpecificCompiler compiler,
-                               String encoding,
+                               Optional<String> encoding,
                                StringType stringType,
                                boolean enableDecimalLogicalType,
                                File src,
                                File output) throws IOException {
     compiler.setStringType(stringType);
     compiler.setEnableDecimalLogicalType(enableDecimalLogicalType);
-    if (encoding != null) {
-      compiler.setOutputCharacterEncoding(encoding);
-    }
+    encoding.ifPresent(compiler::setOutputCharacterEncoding);
     compiler.compileToDestination(src, output);
   }
 


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


> Add ability to use Java 8 date/time types instead of Joda time.
> ---------------------------------------------------------------
>
>                 Key: AVRO-2079
>                 URL: https://issues.apache.org/jira/browse/AVRO-2079
>             Project: Avro
>          Issue Type: Improvement
>          Components: java, logical types
>    Affects Versions: 1.8.2
>            Reporter: Auke van Leeuwen
>            Priority: Major
>              Labels: patch-available
>             Fix For: 1.9.0
>
>
> Currently, for the date/time related logical types, we are generating Joda 
> date/time objects. Since we've moved to Java-8 (AVRO-2043) it seems logical 
> to also provide the possibility to generate {{java.time.*}} date/time objects 
> instead of the Joda time variants.
> I propose to make this is a switch in {{SpecificCompiler.java}} which will 
> default to Joda (I think), but can be set to generate the Java 8 versions.
> (I'm currently trying to run through the code to see if I can make it work.)



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to