diff --git a/persistit/core/src/main/java/com/persistit/Key.java b/persistit/core/src/main/java/com/persistit/Key.java
index 219c03275..440f627ab 100644
--- a/persistit/core/src/main/java/com/persistit/Key.java
+++ b/persistit/core/src/main/java/com/persistit/Key.java
@@ -1,5 +1,6 @@
/**
* Copyright 2005-2012 Akiban Technologies, Inc.
+ * Portions Copyrighted 2026 3A Systems, LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -504,14 +505,19 @@ public enum Direction {
public final static EdgeValue AFTER = new EdgeValue(true);
/**
- * The java.text.SimpleDateFormat used in formatting
+ * A thread-local java.text.SimpleDateFormat used in formatting
* Date- valued keys in the {@link #decodeDisplayable} methods.
* This format also governs the conversion of dates for the
* toString method. This format provides millisecond resolution
* so that the precise stored representation of a date used as a key can be
* represented exactly, but readably, in the displayable version.
+ *
+ * SimpleDateFormat is not thread-safe, so the instance is held
+ * in a {@link ThreadLocal}; call {@link ThreadLocal#get()} to obtain the
+ * per-thread formatter, e.g. SDF.get().format(date).
*/
- public final static SimpleDateFormat SDF = new SimpleDateFormat("yyyyMMddHHmmss.SSSZ");
+ public final static ThreadLocal SDF = ThreadLocal
+ .withInitial(() -> new SimpleDateFormat("yyyyMMddHHmmss.SSSZ"));
/**
* Displayable prefix for boolean values (optional for input, implied and
@@ -3139,7 +3145,7 @@ else if (cl == Boolean.class) {
else if (cl == Date.class) {
Util.append(sb, PREFIX_DATE);
- Util.append(sb, SDF.format(decodeDate()));
+ Util.append(sb, SDF.get().format(decodeDate()));
}
else if (cl == byte[].class) {
diff --git a/persistit/core/src/main/java/com/persistit/KeyParser.java b/persistit/core/src/main/java/com/persistit/KeyParser.java
index 3fc768a06..13c6561be 100644
--- a/persistit/core/src/main/java/com/persistit/KeyParser.java
+++ b/persistit/core/src/main/java/com/persistit/KeyParser.java
@@ -1,5 +1,6 @@
/**
* Copyright 2005-2012 Akiban Technologies, Inc.
+ * Portions Copyrighted 2026 3A Systems, LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -293,7 +294,7 @@ else if (matchExactString("false"))
}
} else if (matchExactString(Key.PREFIX_DATE) || matchExactString(Key.PREFIX_DATE0)) {
if (matchNumber(true, true)) {
- key.append(Key.SDF.parse(_sb.toString()));
+ key.append(Key.SDF.get().parse(_sb.toString()));
result = true;
}
} else if (matchExactString(Key.PREFIX_BYTE_ARRAY)) {
diff --git a/persistit/core/src/main/java/com/persistit/Value.java b/persistit/core/src/main/java/com/persistit/Value.java
index 7e7cdb172..5a5d05d2a 100644
--- a/persistit/core/src/main/java/com/persistit/Value.java
+++ b/persistit/core/src/main/java/com/persistit/Value.java
@@ -1,5 +1,6 @@
/**
* Copyright 2005-2012 Akiban Technologies, Inc.
+ * Portions Copyrighted 2026 3A Systems, LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1566,7 +1567,7 @@ private void appendDisplayable(final StringBuilder sb, final Object value, final
sb.append("...");
} else if (cl == Date.class) {
appendParenthesizedFriendlyClassName(sb, cl);
- sb.append(Key.SDF.format((Date) value));
+ sb.append(Key.SDF.get().format((Date) value));
} else if (value instanceof Number) {
sb.append('(');
sb.append(className.startsWith("java.lang.") ? className.substring(10) : className);
diff --git a/persistit/ui/src/main/java/com/persistit/ui/ValueInspectorTreeNode.java b/persistit/ui/src/main/java/com/persistit/ui/ValueInspectorTreeNode.java
index 3eb835090..906f9d41a 100644
--- a/persistit/ui/src/main/java/com/persistit/ui/ValueInspectorTreeNode.java
+++ b/persistit/ui/src/main/java/com/persistit/ui/ValueInspectorTreeNode.java
@@ -1,5 +1,6 @@
/**
* Copyright 2005-2012 Akiban Technologies, Inc.
+ * Portions Copyrighted 2026 3A Systems, LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -156,7 +157,7 @@ String displayable(final String fieldName, final Class type, final Object object
sb.append('\"');
} else if (object instanceof Date) {
sb.append(" = ");
- sb.append(Key.SDF.format((Date) object));
+ sb.append(Key.SDF.get().format((Date) object));
} else if (type == Boolean.class || type == Byte.class || type == Short.class || type == Character.class
|| type == Integer.class || type == Long.class || type == Float.class || type == Double.class
|| type == BigInteger.class || type == BigDecimal.class) {