http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DummyPreciseClock.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DummyPreciseClock.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DummyPreciseClock.java
new file mode 100644
index 0000000..2c88640
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DummyPreciseClock.java
@@ -0,0 +1,49 @@
+/*
+ * 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.logging.log4j.core.util;
+
+/**
+ * Implementation of the {@code PreciseClock} interface that always returns a 
fixed value.
+ * @since 2.11
+ */
+public class DummyPreciseClock implements PreciseClock {
+    private final long currentTimeMillis;
+    private final int nanosOfMillisecond;
+
+    public DummyPreciseClock() {
+        this(0);
+    }
+
+    public DummyPreciseClock(final long currentTimeMillis) {
+        this(currentTimeMillis, 0);
+    }
+
+    public DummyPreciseClock(final long currentTimeMillis, final int 
nanosOfMillisecond) {
+        this.currentTimeMillis = currentTimeMillis;
+        this.nanosOfMillisecond = nanosOfMillisecond;
+    }
+
+    @Override
+    public void init(final MutableInstant instant) {
+        instant.initFromEpochMilli(currentTimeMillis, nanosOfMillisecond);
+    }
+
+    @Override
+    public long currentTimeMillis() {
+        return currentTimeMillis;
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Instant.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Instant.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Instant.java
new file mode 100644
index 0000000..a798021
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Instant.java
@@ -0,0 +1,72 @@
+/*
+ * 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.logging.log4j.core.util;
+
+/**
+ * Models a point in time, suitable for event timestamps.
+ * <p>
+ * Provides methods for obtaining high precision time information similar to 
the
+ * <a 
href="https://docs.oracle.com/javase/9/docs/api/java/time/Instant.html";>Instant</a>
 class introduced in Java 8,
+ * while also supporting the legacy millisecond precision API.
+ * </p><p>
+ * Depending on the platform, time sources ({@link Clock} implementations) may 
produce high precision or millisecond
+ * precision time values. At the same time, some time value consumers (for 
example timestamp formatters) may only be
+ * able to consume time values of millisecond precision, while some others may 
require a high precision time value.
+ * </p><p>
+ * This class bridges these two time APIs.
+ * </p>
+ * @since 2.11
+ */
+public interface Instant {
+    /**
+     * Gets the number of seconds from the Java epoch of 1970-01-01T00:00:00Z.
+     * <p>
+     * The epoch second count is a simple incrementing count of seconds where 
second 0 is 1970-01-01T00:00:00Z.
+     * The nanosecond part of the day is returned by {@link 
#getNanoOfSecond()}.
+     * </p>
+     * @return the seconds from the epoch of 1970-01-01T00:00:00Z
+     */
+    long getEpochSecond();
+
+    /**
+     * Gets the number of nanoseconds, later along the time-line, from the 
start of the second.
+     * <p>
+     * The nanosecond-of-second value measures the total number of nanoseconds 
from the second returned by {@link #getEpochSecond()}.
+     * </p>
+     * @return the nanoseconds within the second, always positive, never 
exceeds {@code 999,999,999}
+     */
+    int getNanoOfSecond();
+
+    /**
+     * Gets the number of milliseconds from the Java epoch of 
1970-01-01T00:00:00Z.
+     * <p>
+     * The epoch millisecond count is a simple incrementing count of 
milliseconds where millisecond 0 is 1970-01-01T00:00:00Z.
+     * The nanosecond part of the day is returned by {@link 
#getNanoOfMillisecond()}.
+     * </p>
+     * @return the milliseconds from the epoch of 1970-01-01T00:00:00Z
+     */
+    long getEpochMillisecond();
+
+    /**
+     * Gets the number of nanoseconds, later along the time-line, from the 
start of the millisecond.
+     * <p>
+     * The nanosecond-of-millisecond value measures the total number of 
nanoseconds from the millisecond returned by {@link #getEpochMillisecond()}.
+     * </p>
+     * @return the nanoseconds within the millisecond, always positive, never 
exceeds {@code 999,999}
+     */
+    int getNanoOfMillisecond();
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/util/MutableInstant.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/MutableInstant.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/MutableInstant.java
new file mode 100644
index 0000000..1c64525
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/MutableInstant.java
@@ -0,0 +1,152 @@
+/*
+ * 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.logging.log4j.core.util;
+
+import org.apache.logging.log4j.util.PerformanceSensitive;
+
+import java.io.Serializable;
+
+/**
+ * An instantaneous point on the time line, used for high-precision log event 
timestamps.
+ * Modelled on <a 
href="https://docs.oracle.com/javase/9/docs/api/index.html?java/time/class-use/Instant.html";>java.time.Instant</a>,
+ * except that this version is mutable to prevent allocating temporary objects 
that need to be garbage-collected later.
+ * <p>
+ * Instances of this class are <em>not</em> thread-safe and should not be 
shared between threads.
+ * </p>
+ *
+ * @since 2.11
+ */
+@PerformanceSensitive("allocation")
+public class MutableInstant implements Instant, Serializable {
+
+    private static final int MILLIS_PER_SECOND = 1000;
+    private static final int NANOS_PER_MILLI = 1000_000;
+    private static final int NANOS_PER_SECOND = MILLIS_PER_SECOND * 
NANOS_PER_MILLI;
+
+    private long epochSecond;
+    private int nanoOfSecond;
+
+    @Override
+    public long getEpochSecond() {
+        return epochSecond;
+    }
+
+    @Override
+    public int getNanoOfSecond() {
+        return nanoOfSecond;
+    }
+
+    @Override
+    public long getEpochMillisecond() {
+        final int millis = nanoOfSecond / NANOS_PER_MILLI;
+        long epochMillisecond = epochSecond * MILLIS_PER_SECOND + millis;
+        return epochMillisecond;
+    }
+
+    @Override
+    public int getNanoOfMillisecond() {
+        final int millis = nanoOfSecond / NANOS_PER_MILLI;
+        int nanoOfMillisecond = nanoOfSecond - (millis * NANOS_PER_MILLI); // 
cheaper than nanoOfSecond % NANOS_PER_MILLI
+        return nanoOfMillisecond;
+    }
+
+    public void initFrom(final Instant other) {
+        this.epochSecond = other.getEpochSecond();
+        this.nanoOfSecond = other.getNanoOfSecond();
+    }
+
+    /**
+     * Updates the fields of this {@code MutableInstant} from the specified 
epoch millis.
+     * @param epochMilli the number of milliseconds from the Java epoch of 
1970-01-01T00:00:00Z
+     * @param nanoOfMillisecond the number of nanoseconds, later along the 
time-line, from the start of the millisecond
+     */
+    public void initFromEpochMilli(final long epochMilli, final int 
nanoOfMillisecond) {
+        validateNanoOfMillisecond(nanoOfMillisecond);
+        this.epochSecond = epochMilli / MILLIS_PER_SECOND;
+        this.nanoOfSecond = (int) (epochMilli - (epochSecond * 
MILLIS_PER_SECOND)) * NANOS_PER_MILLI + nanoOfMillisecond;
+    }
+
+    private void validateNanoOfMillisecond(final int nanoOfMillisecond) {
+        if (nanoOfMillisecond < 0 || nanoOfMillisecond >= NANOS_PER_MILLI) {
+            throw new IllegalArgumentException("Invalid nanoOfMillisecond " + 
nanoOfMillisecond);
+        }
+    }
+
+    public void initFrom(final Clock clock) {
+        if (clock instanceof PreciseClock) {
+            ((PreciseClock) clock).init(this);
+        } else {
+            initFromEpochMilli(clock.currentTimeMillis(), 0);
+        }
+    }
+
+    /**
+     * Updates the fields of this {@code MutableInstant} from the specified 
instant components.
+     * @param epochSecond the number of seconds from the Java epoch of 
1970-01-01T00:00:00Z
+     * @param nano the number of nanoseconds, later along the time-line, from 
the start of the second
+     */
+    public void initFromEpochSecond(final long epochSecond, final int nano) {
+        validateNanoOfSecond(nano);
+        this.epochSecond = epochSecond;
+        this.nanoOfSecond = nano;
+    }
+
+    private void validateNanoOfSecond(final int nano) {
+        if (nano < 0 || nano >= NANOS_PER_SECOND) {
+            throw new IllegalArgumentException("Invalid nanoOfSecond " + nano);
+        }
+    }
+
+    /**
+     * Updates the elements of the specified {@code long[]} result array from 
the specified instant components.
+     * @param epochSecond (input) the number of seconds from the Java epoch of 
1970-01-01T00:00:00Z
+     * @param nano (input) the number of nanoseconds, later along the 
time-line, from the start of the second
+     * @param result (output) a two-element array to store the result: the 
first element is the number of milliseconds
+     *               from the Java epoch of 1970-01-01T00:00:00Z,
+     *               the second element is the number of nanoseconds, later 
along the time-line, from the start of the millisecond
+     */
+    public static void instantToMillisAndNanos(final long epochSecond, final 
int nano, final long[] result) {
+        int millis = nano / NANOS_PER_MILLI;
+        result[0] = epochSecond * MILLIS_PER_SECOND + millis;
+        result[1] = nano - (millis * NANOS_PER_MILLI); // cheaper than 
nanoOfSecond % NANOS_PER_MILLI
+    }
+
+    @Override
+    public boolean equals(final Object object) {
+        if (object == this) {
+            return true;
+        }
+        if (!(object instanceof MutableInstant)) {
+            return false;
+        }
+        MutableInstant other = (MutableInstant) object;
+        return epochSecond == other.epochSecond && nanoOfSecond == 
other.nanoOfSecond;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + (int) (epochSecond ^ (epochSecond >>> 32));
+        result = 31 * result + nanoOfSecond;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "MutableInstant[epochSecond=" + epochSecond + ", nano=" + 
nanoOfSecond + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/util/PreciseClock.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/PreciseClock.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/PreciseClock.java
new file mode 100644
index 0000000..366c1c4
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/PreciseClock.java
@@ -0,0 +1,33 @@
+/*
+ * 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.logging.log4j.core.util;
+
+/**
+ * Extension of the {@link Clock} interface that is able to provide more 
accurate time information than milliseconds
+ * since the epoch. {@code PreciseClock} implementations are free to return 
millisecond-precision time
+ * if that is the most accurate time information available on this platform.
+ * @since 2.11
+ */
+public interface PreciseClock extends Clock {
+
+    /**
+     * Initializes the specified instant with time information as accurate as 
available on this platform.
+     * @param mutableInstant the container to be initialized with the accurate 
time information
+     * @since 2.11
+     */
+    void init(final MutableInstant mutableInstant);
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/util/datetime/FixedDateFormat.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/datetime/FixedDateFormat.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/datetime/FixedDateFormat.java
index 671725d..b9b6af3 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/datetime/FixedDateFormat.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/datetime/FixedDateFormat.java
@@ -17,6 +17,8 @@
 
 package org.apache.logging.log4j.core.util.datetime;
 
+import org.apache.logging.log4j.core.util.Instant;
+
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Objects;
@@ -31,6 +33,7 @@ import java.util.concurrent.TimeUnit;
  * 
/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadsafeDateFormatBenchmark.java
  */
 public class FixedDateFormat {
+
     /**
      * Enumeration over the supported date/time format patterns.
      * <p>
@@ -41,6 +44,14 @@ public class FixedDateFormat {
          * ABSOLUTE time format: {@code "HH:mm:ss,SSS"}.
          */
         ABSOLUTE("HH:mm:ss,SSS", null, 0, ':', 1, ',', 1),
+        /**
+         * ABSOLUTE time format with microsecond precision: {@code 
"HH:mm:ss,nnnnnn"}.
+         */
+        ABSOLUTE_MICROS("HH:mm:ss,nnnnnn", null, 0, ':', 1, ',', 1),
+        /**
+         * ABSOLUTE time format with nanosecond precision: {@code 
"HH:mm:ss,nnnnnnnnn"}.
+         */
+        ABSOLUTE_NANOS("HH:mm:ss,nnnnnnnnn", null, 0, ':', 1, ',', 1),
 
         /**
          * ABSOLUTE time format variation with period separator: {@code 
"HH:mm:ss.SSS"}.
@@ -66,6 +77,14 @@ public class FixedDateFormat {
          * DEFAULT time format: {@code "yyyy-MM-dd HH:mm:ss,SSS"}.
          */
         DEFAULT("yyyy-MM-dd HH:mm:ss,SSS", "yyyy-MM-dd ", 0, ':', 1, ',', 1),
+        /**
+         * DEFAULT time format with microsecond precision: {@code "yyyy-MM-dd 
HH:mm:ss,nnnnnn"}.
+         */
+        DEFAULT_MICROS("yyyy-MM-dd HH:mm:ss,nnnnnn", "yyyy-MM-dd ", 0, ':', 1, 
',', 1),
+        /**
+         * DEFAULT time format with nanosecond precision: {@code "yyyy-MM-dd 
HH:mm:ss,nnnnnnnnn"}.
+         */
+        DEFAULT_NANOS("yyyy-MM-dd HH:mm:ss,nnnnnnnnn", "yyyy-MM-dd ", 0, ':', 
1, ',', 1),
 
         /**
          * DEFAULT time format variation with period separator: {@code 
"yyyy-MM-dd HH:mm:ss.SSS"}.
@@ -92,6 +111,10 @@ public class FixedDateFormat {
          */
         ISO8601_PERIOD("yyyy-MM-dd'T'HH:mm:ss.SSS", "yyyy-MM-dd'T'", 2, ':', 
1, '.', 1);
 
+        private static final String DEFAULT_SECOND_FRACTION_PATTERN = "SSS";
+        private static final int MILLI_FRACTION_DIGITS = 
DEFAULT_SECOND_FRACTION_PATTERN.length();
+        private static final char SECOND_FRACTION_PATTERN = 'n';
+
         private final String pattern;
         private final String datePattern;
         private final int escapeCount;
@@ -144,6 +167,31 @@ public class FixedDateFormat {
             return null;
         }
 
+        static FixedFormat lookupIgnoringNanos(final String pattern) {
+            final int nanoStart = nanoStart(pattern);
+            if (nanoStart > 0) {
+                final String subPattern = pattern.substring(0, nanoStart) + 
DEFAULT_SECOND_FRACTION_PATTERN;
+                for (final FixedFormat type : FixedFormat.values()) {
+                    if (type.getPattern().equals(subPattern)) {
+                        return type;
+                    }
+                }
+            }
+            return null;
+        }
+
+        private static int nanoStart(final String pattern) {
+            final int index = pattern.indexOf(SECOND_FRACTION_PATTERN);
+            if (index >= 0) {
+                for (int i = index + 1; i < pattern.length(); i++) {
+                    if (pattern.charAt(i) != SECOND_FRACTION_PATTERN) {
+                        return -1;
+                    }
+                }
+            }
+            return index;
+        }
+
         /**
          * Returns the length of the resulting formatted date and time strings.
          *
@@ -187,6 +235,7 @@ public class FixedDateFormat {
     private final FixedFormat fixedFormat;
     private final TimeZone timeZone;
     private final int length;
+    private final int secondFractionDigits;
     private final FastDateFormat fastDateFormat; // may be null
     private final char timeSeparatorChar;
     private final char millisSeparatorChar;
@@ -212,8 +261,22 @@ public class FixedDateFormat {
      * Package protected for unit tests.
      *
      * @param fixedFormat the fixed format
+     * @param tz time zone
      */
     FixedDateFormat(final FixedFormat fixedFormat, final TimeZone tz) {
+        this(fixedFormat, tz, 
FixedFormat.DEFAULT_SECOND_FRACTION_PATTERN.length());
+    }
+
+    /**
+     * Constructs a FixedDateFormat for the specified fixed format.
+     * <p>
+     * Package protected for unit tests.
+     *
+     * @param fixedFormat the fixed format
+     * @param tz time zone
+     * @param secondFractionDigits the number of digits specifying the 
fraction of the second to show
+     */
+    FixedDateFormat(final FixedFormat fixedFormat, final TimeZone tz, final 
int secondFractionDigits) {
         this.fixedFormat = Objects.requireNonNull(fixedFormat);
         this.timeZone = Objects.requireNonNull(tz);
         this.timeSeparatorChar = fixedFormat.timeSeparatorChar;
@@ -221,6 +284,7 @@ public class FixedDateFormat {
         this.millisSeparatorChar = fixedFormat.millisSeparatorChar;
         this.millisSeparatorLength = fixedFormat.millisSeparatorLength;
         this.length = fixedFormat.getLength();
+        this.secondFractionDigits = Math.max(1, Math.min(9, 
secondFractionDigits));
         this.fastDateFormat = fixedFormat.getFastDateFormat(tz);
     }
 
@@ -230,17 +294,20 @@ public class FixedDateFormat {
         }
         final TimeZone tz;
         if (options.length > 1) {
-            if (options[1] != null){
+            if (options[1] != null) {
                 tz = TimeZone.getTimeZone(options[1]);
             } else {
                 tz = TimeZone.getDefault();
             }
-        } else if (options.length > 2) {
-            return null;
         } else {
             tz = TimeZone.getDefault();
         }
 
+        final FixedFormat withNanos = 
FixedFormat.lookupIgnoringNanos(options[0]);
+        if (withNanos != null) {
+            final int secondFractionDigits = options[0].length() - 
FixedFormat.nanoStart(options[0]);
+            return new FixedDateFormat(withNanos, tz, secondFractionDigits);
+        }
         final FixedFormat type = FixedFormat.lookup(options[0]);
         return type == null ? null : new FixedDateFormat(type, tz);
     }
@@ -351,23 +418,44 @@ public class FixedDateFormat {
         }
     }
 
+    public String formatInstant(final Instant instant) {
+        final char[] result = new char[length << 1]; // double size for 
locales with lengthy DateFormatSymbols
+        final int written = formatInstant(instant, result, 0);
+        return new String(result, 0, written);
+    }
+
+    public int formatInstant(final Instant instant, final char[] buffer, final 
int startPos) {
+        int result = format(instant.getEpochMillisecond(), buffer, startPos);
+        result -= digitsLessThanThree();
+        formatNanoOfMillisecond(instant.getNanoOfMillisecond(), buffer, 
startPos + result);
+        return result + digitsMorePreciseThanMillis();
+    }
+
+    private int digitsLessThanThree() { // in case user specified only 1 or 2 
'n' format characters
+        return Math.max(0, FixedFormat.MILLI_FRACTION_DIGITS - 
secondFractionDigits);
+    }
+
+    private int digitsMorePreciseThanMillis() {
+        return Math.max(0, secondFractionDigits - 
FixedFormat.MILLI_FRACTION_DIGITS);
+    }
+
     // Profiling showed this method is important to log4j performance. Modify 
with care!
     // 28 bytes (allows immediate JVM inlining: <= -XX:MaxInlineSize=35 bytes)
-    public String format(final long time) {
+    public String format(final long epochMillis) {
         final char[] result = new char[length << 1]; // double size for 
locales with lengthy DateFormatSymbols
-        final int written = format(time, result, 0);
+        final int written = format(epochMillis, result, 0);
         return new String(result, 0, written);
     }
 
     // Profiling showed this method is important to log4j performance. Modify 
with care!
     // 31 bytes (allows immediate JVM inlining: <= -XX:MaxInlineSize=35 bytes)
-    public int format(final long time, final char[] buffer, final int 
startPos) {
+    public int format(final long epochMillis, final char[] buffer, final int 
startPos) {
         // Calculate values by getting the ms values first and do then
         // calculate the hour minute and second values divisions.
 
         // Get daytime in ms: this does fit into an int
         // int ms = (int) (time % 86400000);
-        final int ms = (int) (millisSinceMidnight(time));
+        final int ms = (int) (millisSinceMidnight(epochMillis));
         writeDate(buffer, startPos);
         return writeTime(ms, buffer, startPos + dateLength) - startPos;
     }
@@ -431,6 +519,26 @@ public class FixedDateFormat {
         return pos;
     }
 
+    static int[] TABLE = {
+            100000, // 0
+            10000, // 1
+            1000, // 2
+            100, // 3
+            10, // 4
+            1, // 5
+    };
+
+    private void formatNanoOfMillisecond(int nanoOfMillisecond, final char[] 
buffer, int pos) {
+        int temp;
+        int remain = nanoOfMillisecond;
+        for (int i = 0; i < secondFractionDigits - 
FixedFormat.MILLI_FRACTION_DIGITS; i++) {
+            int divisor = TABLE[i];
+            temp = remain / divisor;
+            buffer[pos++] = ((char) (temp + '0'));
+            remain -= divisor * temp; // equivalent of remain % 10
+        }
+    }
+
     private int daylightSavingTime(final int hourOfDay) {
         return hourOfDay > 23 ? dstOffsets[23] : dstOffsets[hourOfDay];
     }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/JsonCompleteFileAppenderTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/JsonCompleteFileAppenderTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/JsonCompleteFileAppenderTest.java
index e6dbdb6..323ca13 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/JsonCompleteFileAppenderTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/JsonCompleteFileAppenderTest.java
@@ -19,13 +19,21 @@ package org.apache.logging.log4j.core.appender;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.util.List;
 
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.categories.Layouts;
+import org.apache.logging.log4j.core.filter.TimeFilterTest;
+import org.apache.logging.log4j.core.impl.Log4jLogEventTest;
 import org.apache.logging.log4j.core.selector.ContextSelector;
 import org.apache.logging.log4j.core.selector.CoreContextSelectors;
+import org.apache.logging.log4j.core.util.ClockFactory;
 import org.apache.logging.log4j.junit.CleanFiles;
 import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
@@ -49,6 +57,16 @@ public class JsonCompleteFileAppenderTest {
         this.ruleChain = 
RuleChain.outerRule(cleanFiles).around(loggerContextRule);
     }
 
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty(ClockFactory.PROPERTY_NAME, 
Log4jLogEventTest.FixedTimeClock.class.getName());
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        System.clearProperty(ClockFactory.PROPERTY_NAME);
+    }
+
     @Parameters(name = "{0}")
     public static Class<?>[] getParameters() {
         return CoreContextSelectors.CLASSES;
@@ -68,45 +86,28 @@ public class JsonCompleteFileAppenderTest {
         logger.info(logMsg);
         logger.error(logMsg, new IllegalArgumentException("badarg"));
         this.loggerContextRule.getLoggerContext().stop(); // stops async thread
-        String line1;
-        String line2;
-        String line3;
-        String line4;
-        String line5;
-        String line6;
-        try (final BufferedReader reader = new BufferedReader(new 
FileReader(this.logFile))) {
-            line1 = reader.readLine();
-            line2 = reader.readLine();
-            line3 = reader.readLine();
-            line4 = reader.readLine();
-            line5 = reader.readLine();
-            line6 = reader.readLine();
-        }
-        assertNotNull("line1", line1);
-        final String msg1 = "[";
-        assertTrue("line1 incorrect: [" + line1 + "], does not contain: [" + 
msg1 + ']', line1.equals(msg1));
 
-        assertNotNull("line2", line2);
-        final String msg2 = "{";
-        assertTrue("line2 incorrect: [" + line2 + "], does not contain: [" + 
msg2 + ']', line2.equals(msg2));
-
-        assertNotNull("line3", line3);
-        final String msg3 = "  \"timeMillis\" : ";
-        assertTrue("line3 incorrect: [" + line3 + "], does not contain: [" + 
msg3 + ']', line3.contains(msg3));
-
-        assertNotNull("line4", line4);
-        final String msg4 = "  \"thread\" : \"main\",";
-        assertTrue("line4 incorrect: [" + line4 + "], does not contain: [" + 
msg4 + ']', line4.contains(msg4));
-
-        assertNotNull("line5", line5);
-        final String msg5 = "  \"level\" : \"INFO\",";
-        assertTrue("line5 incorrect: [" + line5 + "], does not contain: [" + 
msg5 + ']', line5.contains(msg5));
-
-        assertNotNull("line6", line6);
-        final String msg6 = "  \"loggerName\" : \"com.foo.Bar\",";
-        assertTrue("line5 incorrect: [" + line6 + "], does not contain: [" + 
msg6 + ']', line6.contains(msg6));
+        List<String> lines = Files.readAllLines(logFile.toPath(), 
Charset.forName("UTF8"));
 
+        String[] expected = {
+                "[", // equals
+                "{", // equals
+                "  \"thread\" : \"main\",", //
+                "  \"level\" : \"INFO\",", //
+                "  \"loggerName\" : \"com.foo.Bar\",", //
+                "  \"message\" : \"Message flushed with immediate 
flush=true\",", //
+                "  \"endOfBatch\" : false,", //
+                "  \"loggerFqcn\" : 
\"org.apache.logging.log4j.spi.AbstractLogger\",", //
+                "  \"instant\" : {", //
+                "    \"epochSecond\" : 1234567,", //
+                "    \"nanoOfSecond\" : 890000000", //
+                "  },", //
+        };
+        for (int i = 0; i < expected.length; i++) {
+            String line = lines.get(i);
+            assertTrue("line " + i + " incorrect: [" + line + "], does not 
contain: [" + expected[i] + ']', line.contains(expected[i]));
+        }
         final String location = "testFlushAtEndOfBatch";
-        assertTrue("no location", !line1.contains(location));
+        assertTrue("no location", !lines.get(0).contains(location));
     }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompleteFileAppenderTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompleteFileAppenderTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompleteFileAppenderTest.java
index 35e01a1..c77b464 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompleteFileAppenderTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompleteFileAppenderTest.java
@@ -16,13 +16,13 @@
  */
 package org.apache.logging.log4j.core.appender;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
 
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.categories.Layouts;
@@ -39,6 +39,8 @@ import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
+import static org.junit.Assert.*;
+
 /**
  * Tests a "complete" XML file a.k.a. a well-formed XML file.
  */
@@ -75,12 +77,14 @@ public class XmlCompleteFileAppenderTest {
         String line2;
         String line3;
         String line4;
+        String line5;
         try (final BufferedReader reader = new BufferedReader(new 
FileReader(logFile))) {
             line1 = reader.readLine();
             line2 = reader.readLine();
             reader.readLine(); // ignore the empty line after the <Events> root
             line3 = reader.readLine();
             line4 = reader.readLine();
+            line5 = reader.readLine();
         } finally {
             logFile.delete();
         }
@@ -97,9 +101,13 @@ public class XmlCompleteFileAppenderTest {
         assertTrue("line3 incorrect: [" + line3 + "], does not contain: [" + 
msg3 + ']', line3.contains(msg3));
 
         assertNotNull("line4", line4);
-        final String msg4 = logMsg;
+        final String msg4 = "<Instant epochSecond=";
         assertTrue("line4 incorrect: [" + line4 + "], does not contain: [" + 
msg4 + ']', line4.contains(msg4));
 
+        assertNotNull("line5", line5);
+        final String msg5 = logMsg;
+        assertTrue("line5 incorrect: [" + line5 + "], does not contain: [" + 
msg5 + ']', line5.contains(msg5));
+
         final String location = "testFlushAtEndOfBatch";
         assertTrue("no location", !line1.contains(location));
     }
@@ -108,17 +116,19 @@ public class XmlCompleteFileAppenderTest {
      * Test the indentation of the Events XML.
      * <p>Expected Events XML is as below.</p>
      * <pre>
-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-&lt;Events xmlns=&quot;http://logging.apache.org/log4j/2.0/events&quot;&gt;
-
-  &lt;Event xmlns=&quot;http://logging.apache.org/log4j/2.0/events&quot; 
timeMillis=&quot;1460974522088&quot; thread=&quot;main&quot; 
level=&quot;INFO&quot; loggerName=&quot;com.foo.Bar&quot; 
endOfBatch=&quot;false&quot; 
loggerFqcn=&quot;org.apache.logging.log4j.spi.AbstractLogger&quot; 
threadId=&quot;11&quot; threadPriority=&quot;5&quot;&gt;
-    &lt;Message&gt;First Msg tag must be in level 2 after correct 
indentation&lt;/Message&gt;
-  &lt;/Event&gt;
-
-  &lt;Event xmlns=&quot;http://logging.apache.org/log4j/2.0/events&quot; 
timeMillis=&quot;1460974522089&quot; thread=&quot;main&quot; 
level=&quot;INFO&quot; loggerName=&quot;com.foo.Bar&quot; 
endOfBatch=&quot;true&quot; 
loggerFqcn=&quot;org.apache.logging.log4j.spi.AbstractLogger&quot; 
threadId=&quot;11&quot; threadPriority=&quot;5&quot;&gt;
-    &lt;Message&gt;Second Msg tag must also be in level 2 after correct 
indentation&lt;/Message&gt;
-  &lt;/Event&gt;
-&lt;/Events&gt;
+&lt;?xml version="1.0" encoding="UTF-8"?>
+&lt;Events xmlns="http://logging.apache.org/log4j/2.0/events";>
+
+  &lt;Event xmlns="http://logging.apache.org/log4j/2.0/events"; thread="main" 
level="INFO" loggerName="com.foo.Bar" endOfBatch="true" 
loggerFqcn="org.apache.logging.log4j.spi.AbstractLogger" threadId="12" 
threadPriority="5">
+    &lt;Instant epochSecond="1515889414" nanoOfSecond="144000000" 
epochMillisecond="1515889414144" nanoOfMillisecond="0"/>
+    &lt;Message>First Msg tag must be in level 2 after correct 
indentation&lt;/Message>
+  &lt;/Event>
+
+  &lt;Event xmlns="http://logging.apache.org/log4j/2.0/events"; thread="main" 
level="INFO" loggerName="com.foo.Bar" endOfBatch="true" 
loggerFqcn="org.apache.logging.log4j.spi.AbstractLogger" threadId="12" 
threadPriority="5">
+    &lt;Instant epochSecond="1515889414" nanoOfSecond="144000000" 
epochMillisecond="1515889414144" nanoOfMillisecond="0"/>
+    &lt;Message>Second Msg tag must also be in level 2 after correct 
indentation&lt;/Message>
+  &lt;/Event>
+&lt;/Events>
      * </pre>
      * @throws Exception
      */
@@ -131,55 +141,32 @@ public class XmlCompleteFileAppenderTest {
         logger.info(secondLogMsg);
         CoreLoggerContexts.stopLoggerContext(false, logFile); // stop async 
thread
 
-        final String[] lines = new String[9];
-
-        try (final BufferedReader reader = new BufferedReader(new 
FileReader(logFile))) {
-
-            int usefulLinesIndex = 0;
-            String readLine;
-            while((readLine = reader.readLine()) != null) {
-
-                if (!"".equals(readLine.trim())) {
-                    lines[usefulLinesIndex] = readLine;
-                    usefulLinesIndex++;
-                }
+        int[] indentations = {
+                0, //"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                0, //"<Events 
xmlns=\"http://logging.apache.org/log4j/2.0/events\";>\n"
+                -1, // empty
+                2, //"  <Event 
xmlns=\"http://logging.apache.org/log4j/2.0/events\"; thread=\"main\" 
level=\"INFO\" loggerName=\"com.foo.Bar\" endOfBatch=\"true\" 
loggerFqcn=\"org.apache.logging.log4j.spi.AbstractLogger\" threadId=\"12\" 
threadPriority=\"5\">\n"
+                4, //"    <Instant epochSecond=\"1515889414\" 
nanoOfSecond=\"144000000\" epochMillisecond=\"1515889414144\" 
nanoOfMillisecond=\"0\"/>\n"
+                4, //"    <Message>First Msg tag must be in level 2 after 
correct indentation</Message>\n" +
+                2, //"  </Event>\n"
+                -1, // empty
+                2, //"  <Event 
xmlns=\"http://logging.apache.org/log4j/2.0/events\"; thread=\"main\" 
level=\"INFO\" loggerName=\"com.foo.Bar\" endOfBatch=\"true\" 
loggerFqcn=\"org.apache.logging.log4j.spi.AbstractLogger\" threadId=\"12\" 
threadPriority=\"5\">\n" +
+                4, //"    <Instant epochSecond=\"1515889414\" 
nanoOfSecond=\"144000000\" epochMillisecond=\"1515889414144\" 
nanoOfMillisecond=\"0\"/>\n" +
+                4, //"    <Message>Second Msg tag must also be in level 2 
after correct indentation</Message>\n" +
+                2, //"  </Event>\n" +
+                0, //"</Events>\n";
+        };
+        List<String> lines1 = Files.readAllLines(logFile.toPath(), 
Charset.forName("UTF-8"));
+
+        assertEquals("number of lines", indentations.length, lines1.size());
+        for (int i = 0; i < indentations.length; i++) {
+            String line = lines1.get(i);
+            if (line.trim().isEmpty()) {
+                assertEquals(-1, indentations[i]);
+            } else {
+                String padding = "        ".substring(0, indentations[i]);
+                assertTrue("Expected " + indentations[i] + " leading spaces 
but got: " + line, line.startsWith(padding));
             }
-        } finally {
-            logFile.delete();
         }
-
-        String currentLine = lines[0];
-        assertFalse("line1 incorrect: [" + currentLine + "], must have no 
indentation", currentLine.startsWith(" "));
-        // <EVENTS
-        currentLine = lines[1];
-        assertFalse("line2 incorrect: [" + currentLine + "], must have no 
indentation", currentLine.startsWith(" "));
-        // <EVENT
-        currentLine = lines[2];
-        assertTrue("line3 incorrect: [" + currentLine + "], must have 
two-space indentation", currentLine.startsWith("  "));
-        assertFalse("line3 incorrect: [" + currentLine + "], must not have 
more than two-space indentation", currentLine.startsWith("   "));
-        // <MSG
-        currentLine = lines[3];
-        assertTrue("line4 incorrect: [" + currentLine + "], must have 
four-space indentation", currentLine.startsWith("    "));
-        assertFalse("line4 incorrect: [" + currentLine + "], must not have 
more than four-space indentation", currentLine.startsWith("     "));
-        // </EVENT
-        currentLine = lines[4];
-        assertTrue("line5 incorrect: [" + currentLine + "], must have 
two-space indentation", currentLine.startsWith("  "));
-        assertFalse("line5 incorrect: [" + currentLine + "], must not have 
more than two-space indentation", currentLine.startsWith("   "));
-
-        // <EVENT
-        currentLine = lines[5];
-        assertTrue("line6 incorrect: [" + currentLine + "], must have 
two-space indentation", currentLine.startsWith("  "));
-        assertFalse("line6 incorrect: [" + currentLine + "], must not have 
more than two-space indentation", currentLine.startsWith("   "));
-        // <MSG
-        currentLine = lines[6];
-        assertTrue("line7 incorrect: [" + currentLine + "], must have 
four-space indentation", currentLine.startsWith("    "));
-        assertFalse("line7 incorrect: [" + currentLine + "], must not have 
more than four-space indentation", currentLine.startsWith("     "));
-        // </EVENT
-        currentLine = lines[7];
-        assertTrue("line8 incorrect: [" + currentLine + "], must have 
two-space indentation", currentLine.startsWith("  "));
-        assertFalse("line8 incorrect: [" + currentLine + "], must not have 
more than two-space indentation", currentLine.startsWith("   "));
-        // </EVENTS
-        currentLine = lines[8];
-        assertFalse("line9 incorrect: [" + currentLine + "], must have no 
indentation", currentLine.startsWith(" "));
     }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/XmlFileAppenderTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/XmlFileAppenderTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/XmlFileAppenderTest.java
index 98a9fab..f9cdff3 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/XmlFileAppenderTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/XmlFileAppenderTest.java
@@ -19,6 +19,9 @@ package org.apache.logging.log4j.core.appender;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.util.List;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -53,32 +56,23 @@ public class XmlFileAppenderTest {
         log.info(logMsg);
         CoreLoggerContexts.stopLoggerContext(false, file); // stop async thread
 
-        String line1;
-        String line2;
-        String line3;
-        try (final BufferedReader reader = new BufferedReader(new 
FileReader(file))) {
-            reader.readLine(); // first line is empty, so ignore it
-            line1 = reader.readLine();
-            line2 = reader.readLine();
-            line3 = reader.readLine();
-        } finally {
-            file.delete();
-        }
-        assertNotNull("line1", line1);
-
-        assertNotNull("line1", line1);
-        final String msg1 = "<Event ";
-        assertTrue("line1 incorrect: [" + line1 + "], does not contain: [" + 
msg1 + ']', line1.contains(msg1));
+        List<String> lines = Files.readAllLines(file.toPath(), 
Charset.forName("UTF8"));
+        file.delete();
 
-        assertNotNull("line2", line2);
-        final String msg2 = logMsg;
-        assertTrue("line2 incorrect: [" + line2 + "], does not contain: [" + 
msg2 + ']', line2.contains(msg2));
+        String[] expect = {
+                "", // ? unsure why initial empty line...
+            "<Event ", //
+            "<Instant epochSecond=", //
+            logMsg, //
+            "</Event>", //
+        };
 
-        assertNotNull("line3", line3);
-        final String msg3 = "</Event>";
-        assertTrue("line3 incorrect: [" + line3 + "], does not contain: [" + 
msg3 + ']', line3.contains(msg3));
+        for (int i = 0; i < expect.length; i++) {
+            assertTrue("Expected line " + i + " to contain " + expect[i] + " 
but got: " + lines.get(i),
+                    lines.get(i).contains(expect[i]));
+        }
 
         final String location = "testFlushAtEndOfBatch";
-        assertTrue("no location", !line1.contains(location));
+        assertTrue("no location", !lines.get(0).contains(location));
     }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/InstantAttributeConverterTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/InstantAttributeConverterTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/InstantAttributeConverterTest.java
new file mode 100644
index 0000000..ab2324f
--- /dev/null
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/InstantAttributeConverterTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.logging.log4j.core.appender.db.jpa.converter;
+
+import org.apache.logging.log4j.categories.Appenders;
+import org.apache.logging.log4j.core.util.Instant;
+import org.apache.logging.log4j.core.util.MutableInstant;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import static org.junit.Assert.*;
+
+@Category(Appenders.Jpa.class)
+public class InstantAttributeConverterTest {
+    private static final StatusLogger LOGGER = StatusLogger.getLogger();
+
+    private InstantAttributeConverter converter;
+
+    @Before
+    public void setUp() {
+        this.converter = new InstantAttributeConverter();
+    }
+
+    @Test
+    public void testConvert01() {
+        final MutableInstant instant = new MutableInstant();
+        instant.initFromEpochSecond(1234567, 89012);
+
+        final String converted = 
this.converter.convertToDatabaseColumn(instant);
+
+        assertNotNull("The converted value should not be null.", converted);
+        assertEquals("The converted value is not correct.", "1234567,89012", 
converted);
+
+        final Instant reversed = 
this.converter.convertToEntityAttribute(converted);
+
+        assertNotNull("The reversed value should not be null.", reversed);
+        assertEquals("epoch sec", 1234567, reversed.getEpochSecond());
+        assertEquals("nanoOfSecond", 89012, reversed.getNanoOfSecond());
+    }
+
+    @Test
+    public void testConvertNullToDatabaseColumn() {
+        assertNull("The converted value should be null.", 
this.converter.convertToDatabaseColumn(null));
+    }
+
+    @Test
+    public void testConvertNullOrBlankToEntityAttribute() {
+        assertNull("The converted attribute should be null (1).", 
this.converter.convertToEntityAttribute(null));
+        assertNull("The converted attribute should be null (2).", 
this.converter.convertToEntityAttribute(""));
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTimestampMessageTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTimestampMessageTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTimestampMessageTest.java
index c177b8a..f797daf 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTimestampMessageTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTimestampMessageTest.java
@@ -74,7 +74,7 @@ public class AsyncLoggerTimestampMessageTest {
         final Logger log = LogManager.getLogger("com.foo.Bar");
         assertFalse(PoisonClock.called);
         log.info((Message) new TimeMsg("Async logger msg with embedded 
timestamp", 123456789000L));
-        assertTrue(PoisonClock.called);
+        assertFalse(PoisonClock.called);
         CoreLoggerContexts.stopLoggerContext(false, file); // stop async thread
 
         final BufferedReader reader = new BufferedReader(new FileReader(file));

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
index 355e668..4dea6a5 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
@@ -30,6 +30,8 @@ import org.apache.logging.log4j.MarkerManager;
 import org.apache.logging.log4j.ThreadContext.ContextStack;
 import org.apache.logging.log4j.categories.AsyncLoggers;
 import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.util.DummyNanoClock;
+import org.apache.logging.log4j.core.util.DummyPreciseClock;
 import org.apache.logging.log4j.util.FilteredObjectInputStream;
 import org.apache.logging.log4j.util.StringMap;
 import org.apache.logging.log4j.core.impl.ThrowableProxy;
@@ -66,10 +68,8 @@ public class RingBufferLogEventTest {
         final ContextStack contextStack = null;
         final String threadName = null;
         final StackTraceElement location = null;
-        final long currentTimeMillis = 0;
-        final long nanoTime = 1;
         evt.setValues(null, loggerName, marker, fqcn, level, data, t, 
(StringMap) evt.getContextData(),
-                contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
+                contextStack, -1, threadName, -1, location, new 
DummyPreciseClock(), new DummyNanoClock(1));
         assertEquals(Level.OFF, evt.getLevel());
     }
 
@@ -85,10 +85,8 @@ public class RingBufferLogEventTest {
         final ContextStack contextStack = null;
         final String threadName = null;
         final StackTraceElement location = null;
-        final long currentTimeMillis = 0;
-        final long nanoTime = 1;
         evt.setValues(null, loggerName, marker, fqcn, level, data, t, 
(StringMap) evt.getContextData(),
-                contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
+                contextStack, -1, threadName, -1, location, new 
DummyPreciseClock(), new DummyNanoClock(1));
         assertNotNull(evt.getMessage());
     }
 
@@ -104,11 +102,10 @@ public class RingBufferLogEventTest {
         final ContextStack contextStack = null;
         final String threadName = null;
         final StackTraceElement location = null;
-        final long currentTimeMillis = 123;
-        final long nanoTime = 1;
         evt.setValues(null, loggerName, marker, fqcn, level, data, t, 
(StringMap) evt.getContextData(),
-                contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
+                contextStack, -1, threadName, -1, location, new 
DummyPreciseClock(123, 456), new DummyNanoClock(1));
         assertEquals(123, evt.getTimeMillis());
+        assertEquals(456, evt.getInstant().getNanoOfMillisecond());
     }
 
     @Test
@@ -123,10 +120,9 @@ public class RingBufferLogEventTest {
         final ContextStack contextStack = null;
         final String threadName = "main";
         final StackTraceElement location = null;
-        final long currentTimeMillis = 12345;
-        final long nanoTime = 1;
         evt.setValues(null, loggerName, marker, fqcn, level, data, t, 
(StringMap) evt.getContextData(),
-                contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
+                contextStack, -1, threadName, -1, location,
+                new DummyPreciseClock(12345, 678), new DummyNanoClock(1));
         ((StringMap) evt.getContextData()).putValue("key", "value");
 
         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -146,7 +142,8 @@ public class RingBufferLogEventTest {
         assertEquals(contextStack, other.getContextStack());
         assertEquals(threadName, other.getThreadName());
         assertEquals(location, other.getSource());
-        assertEquals(currentTimeMillis, other.getTimeMillis());
+        assertEquals(12345, other.getTimeMillis());
+        assertEquals(678, other.getInstant().getNanoOfMillisecond());
     }
 
     @SuppressWarnings("deprecation")
@@ -162,10 +159,8 @@ public class RingBufferLogEventTest {
         final ContextStack contextStack = new 
MutableThreadContextStack(Arrays.asList("a", "b"));
         final String threadName = "main";
         final StackTraceElement location = null;
-        final long currentTimeMillis = 12345;
-        final long nanoTime = 1;
         evt.setValues(null, loggerName, marker, fqcn, level, data, t, 
(StringMap) evt.getContextData(),
-                contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
+                contextStack, -1, threadName, -1, location, new 
DummyPreciseClock(12345, 678), new DummyNanoClock(1));
         ((StringMap) evt.getContextData()).putValue("key", "value");
 
         final LogEvent actual = evt.createMemento();
@@ -180,6 +175,7 @@ public class RingBufferLogEventTest {
         assertEquals(evt.getContextStack(), actual.getContextStack());
         assertEquals(evt.getThreadName(), actual.getThreadName());
         assertEquals(evt.getTimeMillis(), actual.getTimeMillis());
+        assertEquals(evt.getInstant().getNanoOfMillisecond(), 
actual.getInstant().getNanoOfMillisecond());
         assertEquals(evt.getSource(), actual.getSource());
         assertEquals(evt.getThrownProxy(), actual.getThrownProxy());
     }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/ConcurrentLoggingWithJsonLayoutTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/ConcurrentLoggingWithJsonLayoutTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/ConcurrentLoggingWithJsonLayoutTest.java
index 3c1fa64..0e4ad52 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/ConcurrentLoggingWithJsonLayoutTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/ConcurrentLoggingWithJsonLayoutTest.java
@@ -85,7 +85,7 @@ public class ConcurrentLoggingWithJsonLayoutTest {
         if (new File(PATH).exists()) {
             final List<String> lines = Files.readAllLines(new 
File(PATH).toPath(), Charset.defaultCharset());
             for (final String line : lines) {
-                assertThat(line, startsWith("{\"timeMillis\":"));
+                assertThat(line, startsWith("{\"thread\":"));
                 assertThat(line, endsWith("\"threadPriority\":5}"));
             }
         }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/JsonLayoutTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/JsonLayoutTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/JsonLayoutTest.java
index 2a5eafd..dba5cf3 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/JsonLayoutTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/JsonLayoutTest.java
@@ -154,7 +154,7 @@ public class JsonLayoutTest {
         //
         assertNull(actual.getThrown());
         // make sure the names we want are used
-        this.checkPropertyName("timeMillis", compact, str);
+        this.checkPropertyName("instant", compact, str);
         this.checkPropertyName("thread", compact, str); // and not threadName
         this.checkPropertyName("level", compact, str);
         this.checkPropertyName("loggerName", compact, str);

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/XmlLayoutTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/XmlLayoutTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/XmlLayoutTest.java
index 95c9f6d..4ee8f1e 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/XmlLayoutTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/XmlLayoutTest.java
@@ -165,7 +165,10 @@ public class XmlLayoutTest {
         }
         //
         // make sure the names we want are used
-        this.checkAttributeName("timeMillis", compact, str);
+        //this.checkAttributeName("timeMillis", compact, str);
+        this.checkElementName("Instant", compact, str, true, false);
+        this.checkAttributeName("epochSecond", compact, str);
+        this.checkAttributeName("nanoOfSecond", compact, str);
         this.checkAttributeName("thread", compact, str); // and not threadName
         this.checkAttributeName("level", compact, str);
         this.checkAttributeName("loggerName", compact, str);

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/YamlLayoutTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/YamlLayoutTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/YamlLayoutTest.java
index de19606..2fef8de 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/YamlLayoutTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/YamlLayoutTest.java
@@ -138,7 +138,7 @@ public class YamlLayoutTest {
         //
         assertNull(actual.getThrown());
         // make sure the names we want are used
-        this.checkPropertyName("timeMillis", compact, str, true);
+        this.checkPropertyName("instant", compact, str, false);
         this.checkPropertyName("thread", compact, str, true); // and not 
threadName
         this.checkPropertyName("level", compact, str, true);
         this.checkPropertyName("loggerName", compact, str, true);

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/JsonLogEventParserTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/JsonLogEventParserTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/JsonLogEventParserTest.java
index 0dd0182..9683d63 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/JsonLogEventParserTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/JsonLogEventParserTest.java
@@ -28,6 +28,7 @@ public class JsonLogEventParserTest extends 
LogEventParserTest {
 
     private static final String JSON = "{\n" +
             "  \"timeMillis\" : 1493121664118,\n" +
+            "  
\"instant\":{\"epochSecond\":1493121664,\"nanoOfSecond\":118000000},\n" +
             "  \"thread\" : \"main\",\n" +
             "  \"threadId\" : 1,\n" +
             "  \"threadPriority\" : 5,\n" +
@@ -105,7 +106,7 @@ public class JsonLogEventParserTest extends 
LogEventParserTest {
 
     @Test(expected = ParseException.class)
     public void testStringWrongPropertyType() throws ParseException {
-        parser.parseFrom("{\"timeMillis\":\"foobar\"}");
+        parser.parseFrom("{\"threadId\":\"foobar\"}");
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/LogEventParserTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/LogEventParserTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/LogEventParserTest.java
index cd6475b..cdb3bef 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/LogEventParserTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/LogEventParserTest.java
@@ -30,7 +30,7 @@ import static org.junit.Assert.assertThat;
 public abstract class LogEventParserTest {
     protected void assertLogEvent(final LogEvent logEvent) {
         assertThat(logEvent, is(notNullValue()));
-        assertThat(logEvent.getTimeMillis(), equalTo(1493121664118L));
+        assertThat(logEvent.getInstant().getEpochMillisecond(), 
equalTo(1493121664118L));
         assertThat(logEvent.getThreadName(), equalTo("main"));
         assertThat(logEvent.getThreadId(), equalTo(1L));
         assertThat(logEvent.getThreadPriority(), equalTo(5));

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/XmlLogEventParserTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/XmlLogEventParserTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/XmlLogEventParserTest.java
index b460075..72da8ff 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/XmlLogEventParserTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/XmlLogEventParserTest.java
@@ -35,6 +35,7 @@ public class XmlLogEventParserTest extends LogEventParserTest 
{
             "       
loggerFqcn=\"org.apache.logging.log4j.spi.AbstractLogger\"\n" +
             "       threadId=\"1\"\n" +
             "       threadPriority=\"5\">\n" +
+            "  <Instant epochSecond=\"1493121664\" 
nanoOfSecond=\"118000000\"/>\n" +
             "  <Marker name=\"child\">\n" +
             "    <Parents>\n" +
             "      <Marker name=\"parent\">\n" +
@@ -100,6 +101,11 @@ public class XmlLogEventParserTest extends 
LogEventParserTest {
 
     @Test(expected = ParseException.class)
     public void testStringWrongPropertyType() throws ParseException {
+        parser.parseFrom("<Event><Instant 
epochSecond=\"bar\">foobar</Instant></Event>");
+    }
+
+    @Test
+    public void testTimeMillisIgnored() throws ParseException {
         parser.parseFrom("<Event><timeMillis>foobar</timeMillis></Event>");
     }
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/YamlLogEventParserTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/YamlLogEventParserTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/YamlLogEventParserTest.java
index b48989c..16c77e7 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/YamlLogEventParserTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/parser/YamlLogEventParserTest.java
@@ -28,6 +28,9 @@ public class YamlLogEventParserTest extends 
LogEventParserTest {
 
     private static final String YAML = "---\n" +
             "timeMillis: 1493121664118\n" +
+            "instant:\n" +
+            " epochSecond: 1493121664\n" +
+            " nanoOfSecond: 118000000\n" +
             "thread: \"main\"\n" +
             "level: \"INFO\"\n" +
             "loggerName: \"HelloWorld\"\n" +
@@ -92,9 +95,14 @@ public class YamlLogEventParserTest extends 
LogEventParserTest {
         parser.parseFrom("---\n");
     }
 
+    @Test
+    public void testTimeMillisIgnored() throws ParseException {
+        parser.parseFrom("---\ntimeMillis: \"foobar\"\n");
+    }
+
     @Test(expected = ParseException.class)
     public void testStringWrongPropertyType() throws ParseException {
-        parser.parseFrom("---\ntimeMillis: \"foobar\"\n");
+        parser.parseFrom("---\nthreadId: \"foobar\"\n");
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java
index 9f55165..e6be56a 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java
@@ -28,6 +28,8 @@ import java.util.TimeZone;
 import org.apache.logging.log4j.core.AbstractLogEvent;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.util.Constants;
+import org.apache.logging.log4j.core.util.Instant;
+import org.apache.logging.log4j.core.util.MutableInstant;
 import org.apache.logging.log4j.core.util.datetime.FixedDateFormat;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -148,6 +150,71 @@ public class DatePatternConverterTest {
         }
     }
 
+    private String precisePattern(final String pattern, int precision) {
+        String seconds = pattern.substring(0, pattern.indexOf("SSS"));
+        return seconds + "nnnnnnnnn".substring(0, precision);
+    }
+
+    // test with all formats from one 'n' (100s of millis) to 'nnnnnnnnn' 
(nanosecond precision)
+    @Test
+    public void testPredefinedFormatWithAnyValidNanoPrecision() {
+        final StringBuilder precise = new StringBuilder();
+        final StringBuilder milli = new StringBuilder();
+        final LogEvent event = new MyLogEvent();
+
+        for (String timeZone : new String[]{"PDT", null}) { // Pacific 
Daylight Time=UTC-8:00
+            for (final FixedDateFormat.FixedFormat format : 
FixedDateFormat.FixedFormat.values()) {
+                for (int i = 1; i <= 9; i++) {
+                    if (format.getPattern().endsWith("n")) {
+                        continue; // ignore patterns that already have precise 
time formats
+                    }
+                    precise.setLength(0);
+                    milli.setLength(0);
+
+                    final String[] preciseOptions = 
{precisePattern(format.getPattern(), i), timeZone};
+                    final DatePatternConverter preciseConverter = 
DatePatternConverter.newInstance(preciseOptions);
+                    preciseConverter.format(event, precise);
+
+                    final String[] milliOptions = {format.getPattern(), 
timeZone};
+                    
DatePatternConverter.newInstance(milliOptions).format(event, milli);
+                    milli.setLength(milli.length() - 3); // truncate millis
+                    String expected = milli.append("987123456".substring(0, 
i)).toString();
+
+                    assertEquals(expected, precise.toString());
+                    //System.out.println(preciseOptions[0] + ": " + precise);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testInvalidLongPatternIgnoresExcessiveDigits() {
+        final StringBuilder precise = new StringBuilder();
+        final StringBuilder milli = new StringBuilder();
+        final LogEvent event = new MyLogEvent();
+
+            for (final FixedDateFormat.FixedFormat format : 
FixedDateFormat.FixedFormat.values()) {
+                if (format.getPattern().endsWith("n")) {
+                    continue; // ignore patterns that already have precise 
time formats
+                }
+                precise.setLength(0);
+                milli.setLength(0);
+
+                final String pattern = format.getPattern().substring(0, 
format.getPattern().indexOf("SSS"));
+                final String[] preciseOptions = {pattern + "nnnnnnnnn" + "n"}; 
// too long
+                final DatePatternConverter preciseConverter = 
DatePatternConverter.newInstance(preciseOptions);
+                preciseConverter.format(event, precise);
+
+                final String[] milliOptions = {format.getPattern()};
+                DatePatternConverter.newInstance(milliOptions).format(event, 
milli);
+                milli.setLength(milli.length() - 3); // truncate millis
+                String expected = milli.append("987123456").toString();
+
+                assertEquals(expected, precise.toString());
+                //System.out.println(preciseOptions[0] + ": " + precise);
+            }
+    }
+
     private class MyLogEvent extends AbstractLogEvent {
         private static final long serialVersionUID = 0;
 
@@ -158,6 +225,13 @@ public class DatePatternConverterTest {
             cal.set(Calendar.MILLISECOND, 987);
             return cal.getTimeInMillis();
         }
+
+        @Override
+        public Instant getInstant() {
+            MutableInstant result = new MutableInstant();
+            result.initFromEpochMilli(getTimeMillis(), 123456);
+            return result;
+        }
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/util/MutableInstantTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/MutableInstantTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/MutableInstantTest.java
new file mode 100644
index 0000000..1715fc6
--- /dev/null
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/MutableInstantTest.java
@@ -0,0 +1,230 @@
+/*
+ * 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.logging.log4j.core.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class MutableInstantTest {
+
+    @Test
+    public void testGetEpochSecond() {
+        MutableInstant instant = new MutableInstant();
+        assertEquals("initial", 0, instant.getEpochSecond());
+
+        instant.initFromEpochSecond(123, 456);
+        assertEquals("returns directly set value", 123, 
instant.getEpochSecond());
+
+        instant.initFromEpochMilli(123456, 789012);
+        assertEquals("returns converted value when initialized from milllis", 
123, instant.getEpochSecond());
+
+        MutableInstant other = new MutableInstant();
+        other.initFromEpochSecond(788, 456);
+        instant.initFrom(other);
+
+        assertEquals("returns ref value when initialized from instant", 788, 
instant.getEpochSecond());
+    }
+
+    @Test
+    public void testGetNanoOfSecond() {
+        MutableInstant instant = new MutableInstant();
+        assertEquals("initial", 0, instant.getNanoOfSecond());
+
+        instant.initFromEpochSecond(123, 456);
+        assertEquals("returns directly set value", 456, 
instant.getNanoOfSecond());
+
+        instant.initFromEpochMilli(123456, 789012);
+        assertEquals("returns converted value when initialized from milllis", 
456789012, instant.getNanoOfSecond());
+
+        MutableInstant other = new MutableInstant();
+        other.initFromEpochSecond(788, 456);
+        instant.initFrom(other);
+
+        assertEquals("returns ref value when initialized from instant", 456, 
instant.getNanoOfSecond());
+    }
+
+    @Test
+    public void testGetEpochMillisecond() {
+        MutableInstant instant = new MutableInstant();
+        assertEquals("initial", 0, instant.getEpochMillisecond());
+
+        instant.initFromEpochMilli(123, 456);
+        assertEquals("returns directly set value", 123, 
instant.getEpochMillisecond());
+
+        instant.initFromEpochSecond(123, 456789012);
+        assertEquals("returns converted value when initialized from seconds", 
123456, instant.getEpochMillisecond());
+
+        MutableInstant other = new MutableInstant();
+        other.initFromEpochMilli(788, 456);
+        instant.initFrom(other);
+
+        assertEquals("returns ref value when initialized from instant", 788, 
instant.getEpochMillisecond());
+    }
+
+    @Test
+    public void getGetNanoOfMillisecond() {
+        MutableInstant instant = new MutableInstant();
+        assertEquals("initial", 0, instant.getNanoOfMillisecond());
+
+        instant.initFromEpochMilli(123, 456);
+        assertEquals("returns directly set value", 456, 
instant.getNanoOfMillisecond());
+
+        instant.initFromEpochSecond(123, 456789012);
+        assertEquals("returns converted value when initialized from milllis", 
789012, instant.getNanoOfMillisecond());
+
+        MutableInstant other = new MutableInstant();
+        other.initFromEpochMilli(788, 456);
+        instant.initFrom(other);
+
+        assertEquals("returns ref value when initialized from instant", 456, 
instant.getNanoOfMillisecond());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testInitFromInstantRejectsNull() {
+        new MutableInstant().initFrom((Instant) null);
+    }
+
+    @Test
+    public void testInitFromInstantCopiesValues() {
+        MutableInstant other = new MutableInstant();
+        other.initFromEpochSecond(788, 456);
+        assertEquals("epochSec", 788, other.getEpochSecond());
+        assertEquals("NanosOfSec", 456, other.getNanoOfSecond());
+
+        MutableInstant instant = new MutableInstant();
+        instant.initFrom(other);
+
+        assertEquals("epochSec", 788, instant.getEpochSecond());
+        assertEquals("NanoOfSec", 456, instant.getNanoOfSecond());
+    }
+
+    @Test
+    public void testInitFromEpochMillis() {
+        MutableInstant instant = new MutableInstant();
+        instant.initFromEpochMilli(123456, 789012);
+        assertEquals("epochSec", 123, instant.getEpochSecond());
+        assertEquals("NanoOfSec", 456789012, instant.getNanoOfSecond());
+        assertEquals("epochMilli", 123456, instant.getEpochMillisecond());
+        assertEquals("NanoOfMilli", 789012, instant.getNanoOfMillisecond());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInitFromEpochMillisRejectsNegativeNanoOfMilli() {
+        MutableInstant instant = new MutableInstant();
+        instant.initFromEpochMilli(123456, -1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInitFromEpochMillisRejectsTooLargeNanoOfMilli() {
+        MutableInstant instant = new MutableInstant();
+        instant.initFromEpochMilli(123456, 1000_000);
+    }
+
+    @Test
+    public void testInitFromEpochMillisAcceptsTooMaxNanoOfMilli() {
+        MutableInstant instant = new MutableInstant();
+        instant.initFromEpochMilli(123456, 999_999);
+        assertEquals("NanoOfMilli", 999_999, instant.getNanoOfMillisecond());
+    }
+
+    @Test
+    public void testInitFromEpochSecond() {
+        MutableInstant instant = new MutableInstant();
+        instant.initFromEpochSecond(123, 456789012);
+        assertEquals("epochSec", 123, instant.getEpochSecond());
+        assertEquals("NanoOfSec", 456789012, instant.getNanoOfSecond());
+        assertEquals("epochMilli", 123456, instant.getEpochMillisecond());
+        assertEquals("NanoOfMilli", 789012, instant.getNanoOfMillisecond());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInitFromEpochSecondRejectsNegativeNanoOfMilli() {
+        MutableInstant instant = new MutableInstant();
+        instant.initFromEpochSecond(123456, -1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInitFromEpochSecondRejectsTooLargeNanoOfMilli() {
+        MutableInstant instant = new MutableInstant();
+        instant.initFromEpochSecond(123456, 1000_000_000);
+    }
+
+    @Test
+    public void testInitFromEpochSecondAcceptsTooMaxNanoOfMilli() {
+        MutableInstant instant = new MutableInstant();
+        instant.initFromEpochSecond(123456, 999_999_999);
+        assertEquals("NanoOfSec", 999_999_999, instant.getNanoOfSecond());
+    }
+
+    @Test
+    public void testInstantToMillisAndNanos() {
+        long[] values = new long[2];
+        MutableInstant.instantToMillisAndNanos(123456, 999_999_999, values);
+        assertEquals(123456_999, values[0]);
+        assertEquals(999_999, values[1]);
+    }
+
+    @Test
+    public void testInitFromClock() {
+        MutableInstant instant = new MutableInstant();
+
+        PreciseClock clock = new DummyPreciseClock(123456, 789012);
+        instant.initFrom(clock);
+
+        assertEquals(123456, instant.getEpochMillisecond());
+        assertEquals(789012, instant.getNanoOfMillisecond());
+        assertEquals(123, instant.getEpochSecond());
+        assertEquals(456789012, instant.getNanoOfSecond());
+    }
+
+    @Test
+    public void testEquals() {
+        MutableInstant instant = new MutableInstant();
+        instant.initFromEpochSecond(123, 456789012);
+
+        MutableInstant instant2 = new MutableInstant();
+        instant2.initFromEpochMilli(123456, 789012);
+
+        assertEquals(instant, instant2);
+    }
+
+    @Test
+    public void testHashCode() {
+        MutableInstant instant = new MutableInstant();
+        instant.initFromEpochSecond(123, 456789012);
+
+        MutableInstant instant2 = new MutableInstant();
+        instant2.initFromEpochMilli(123456, 789012);
+
+        assertEquals(instant.hashCode(), instant2.hashCode());
+
+
+        instant2.initFromEpochMilli(123456, 789013);
+        assertNotEquals(instant.hashCode(), instant2.hashCode());
+    }
+
+    @Test
+    public void testToString() {
+        MutableInstant instant = new MutableInstant();
+        instant.initFromEpochSecond(123, 456789012);
+        assertEquals("MutableInstant[epochSecond=123, nano=456789012]", 
instant.toString());
+
+        instant.initFromEpochMilli(123456, 789012);
+        assertEquals("MutableInstant[epochSecond=123, nano=456789012]", 
instant.toString());
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/test/java/org/apache/logging/log4j/core/util/datetime/FixedDateFormatTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/datetime/FixedDateFormatTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/datetime/FixedDateFormatTest.java
index 55e5d59..e41c4e1 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/datetime/FixedDateFormatTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/datetime/FixedDateFormatTest.java
@@ -156,6 +156,9 @@ public class FixedDateFormatTest {
         final long start = now - TimeUnit.HOURS.toMillis(25);
         final long end = now + TimeUnit.HOURS.toMillis(25);
         for (final FixedFormat format : FixedFormat.values()) {
+            if (format.getPattern().endsWith("n")) {
+                continue; // cannot compile precise timestamp formats with 
SimpleDateFormat
+            }
             final SimpleDateFormat simpleDF = new 
SimpleDateFormat(format.getPattern(), Locale.getDefault());
             final FixedDateFormat customTF = new FixedDateFormat(format, 
TimeZone.getDefault());
             for (long time = start; time < end; time += 12345) {
@@ -172,6 +175,9 @@ public class FixedDateFormatTest {
         final long start = now - TimeUnit.HOURS.toMillis(25);
         final long end = now + TimeUnit.HOURS.toMillis(25);
         for (final FixedFormat format : FixedFormat.values()) {
+            if (format.getPattern().endsWith("n")) {
+                continue; // cannot compile precise timestamp formats with 
SimpleDateFormat
+            }
             final SimpleDateFormat simpleDF = new 
SimpleDateFormat(format.getPattern(), Locale.getDefault());
             final FixedDateFormat customTF = new FixedDateFormat(format, 
TimeZone.getDefault());
             for (long time = end; time > start; time -= 12345) {
@@ -189,6 +195,9 @@ public class FixedDateFormatTest {
         final long end = now + TimeUnit.HOURS.toMillis(25);
         final char[] buffer = new char[128];
         for (final FixedFormat format : FixedFormat.values()) {
+            if (format.getPattern().endsWith("n")) {
+                continue; // cannot compile precise timestamp formats with 
SimpleDateFormat
+            }
             final SimpleDateFormat simpleDF = new 
SimpleDateFormat(format.getPattern(), Locale.getDefault());
             final FixedDateFormat customTF = new FixedDateFormat(format, 
TimeZone.getDefault());
             for (long time = start; time < end; time += 12345) {
@@ -207,6 +216,9 @@ public class FixedDateFormatTest {
         final long end = now + TimeUnit.HOURS.toMillis(25);
         final char[] buffer = new char[128];
         for (final FixedFormat format : FixedFormat.values()) {
+            if (format.getPattern().endsWith("n")) {
+                continue; // cannot compile precise timestamp formats with 
SimpleDateFormat
+            }
             final SimpleDateFormat simpleDF = new 
SimpleDateFormat(format.getPattern(), Locale.getDefault());
             final FixedDateFormat customTF = new FixedDateFormat(format, 
TimeZone.getDefault());
             for (long time = end; time > start; time -= 12345) {
@@ -362,6 +374,9 @@ public class FixedDateFormatTest {
         final long end = now + TimeUnit.HOURS.toMillis(1);
 
         for (final FixedFormat format : FixedFormat.values()) {
+            if (format.getPattern().endsWith("n")) {
+                continue; // cannot compile precise timestamp formats with 
SimpleDateFormat
+            }
             final SimpleDateFormat simpleDF = new 
SimpleDateFormat(format.getPattern(), Locale.getDefault());
             final FixedDateFormat customTF = new FixedDateFormat(format, 
TimeZone.getDefault());
             for (long time = end; time > start; time -= 12345) {

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java
----------------------------------------------------------------------
diff --git 
a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java
 
b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java
index 7a2b962..73cd43e 100644
--- 
a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java
+++ 
b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java
@@ -32,6 +32,7 @@ import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.core.impl.ThrowableProxy;
+import org.apache.logging.log4j.core.util.Instant;
 import org.apache.logging.log4j.core.util.Patterns;
 import org.apache.logging.log4j.core.util.UuidUtil;
 import org.apache.logging.log4j.message.MapMessage;
@@ -294,6 +295,15 @@ public class FlumeEvent extends SimpleEvent implements 
LogEvent {
     }
 
     /**
+     * {@inheritDoc}
+     * @since 2.11
+     */
+    @Override
+    public Instant getInstant() {
+        return event.getInstant();
+    }
+
+    /**
      * Returns the value of the running Java Virtual Machine's high-resolution 
time source when this event was created,
      * or a dummy value if it is known that this value will not be used 
downstream.
      * @return the event nanosecond timestamp.

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-jpa/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
----------------------------------------------------------------------
diff --git 
a/log4j-jpa/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
 
b/log4j-jpa/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
index 60ca692..6c736c6 100644
--- 
a/log4j-jpa/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
+++ 
b/log4j-jpa/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
@@ -26,6 +26,7 @@ import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.AbstractLogEvent;
+import org.apache.logging.log4j.core.util.Instant;
 import org.apache.logging.log4j.util.ReadOnlyStringMap;
 import org.apache.logging.log4j.core.LogEvent;
 import 
org.apache.logging.log4j.core.appender.db.jpa.converter.ContextDataAttributeConverter;
@@ -216,6 +217,26 @@ public abstract class AbstractLogEventWrapperEntity 
implements LogEvent {
     /**
      * A no-op mutator to satisfy JPA requirements, as this entity is 
write-only.
      *
+     * @param instant Ignored.
+     */
+    @SuppressWarnings("unused")
+    public void setInstant(final Instant instant) {
+        // this entity is write-only
+    }
+
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is 
write-only.
+     *
+     * @param nanoOfMillisecond Ignored.
+     */
+    @SuppressWarnings("unused")
+    public void setNanoOfMillisecond(final int nanoOfMillisecond) {
+        // this entity is write-only
+    }
+
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is 
write-only.
+     *
      * @param throwable Ignored.
      */
     @SuppressWarnings("unused")

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-jpa/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
----------------------------------------------------------------------
diff --git 
a/log4j-jpa/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
 
b/log4j-jpa/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
index 5c06316..492aa61 100644
--- 
a/log4j-jpa/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
+++ 
b/log4j-jpa/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
@@ -26,14 +26,9 @@ import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.LogEvent;
-import 
org.apache.logging.log4j.core.appender.db.jpa.converter.ContextMapAttributeConverter;
-import 
org.apache.logging.log4j.core.appender.db.jpa.converter.ContextStackAttributeConverter;
-import 
org.apache.logging.log4j.core.appender.db.jpa.converter.LevelAttributeConverter;
-import 
org.apache.logging.log4j.core.appender.db.jpa.converter.MarkerAttributeConverter;
-import 
org.apache.logging.log4j.core.appender.db.jpa.converter.MessageAttributeConverter;
-import 
org.apache.logging.log4j.core.appender.db.jpa.converter.StackTraceElementAttributeConverter;
-import 
org.apache.logging.log4j.core.appender.db.jpa.converter.ThrowableAttributeConverter;
+import org.apache.logging.log4j.core.appender.db.jpa.converter.*;
 import org.apache.logging.log4j.core.impl.ThrowableProxy;
+import org.apache.logging.log4j.core.util.Instant;
 import org.apache.logging.log4j.message.Message;
 
 /**
@@ -189,6 +184,12 @@ public abstract class BasicLogEventEntity extends 
AbstractLogEventWrapperEntity
         return this.getWrappedEvent().getTimeMillis();
     }
 
+    @Override
+    @Convert(converter = InstantAttributeConverter.class)
+    public Instant getInstant() {
+        return this.getWrappedEvent().getInstant();
+    }
+
     /**
      * Returns the value of the running Java Virtual Machine's high-resolution 
time source when this event was created,
      * or a dummy value if it is known that this value will not be used 
downstream.

Reply via email to