This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git
The following commit(s) were added to refs/heads/master by this push:
new 117a9ea79 Clone the TimeZone in FastDatePrinter and FastDateParser
(#1795)
117a9ea79 is described below
commit 117a9ea791b87af1b90413fa8290891ca3106cfd
Author: alhuda <[email protected]>
AuthorDate: Sat Sep 19 20:01:47 2026 +0530
Clone the TimeZone in FastDatePrinter and FastDateParser (#1795)
TimeZone is mutable and FastDateFormat shares cached instances
process-wide, so mutating the zone passed to the factory, or the one returned
by getTimeZone(), changed the formatter for every other holder. Clone it in
both constructors and both getters.
---
.../apache/commons/lang3/time/FastDateFormat.java | 2 +-
.../apache/commons/lang3/time/FastDateParser.java | 5 ++--
.../apache/commons/lang3/time/FastDatePrinter.java | 5 ++--
.../commons/lang3/time/FastDateFormatTest.java | 28 ++++++++++++++++++++++
.../commons/lang3/time/FastDateParserTest.java | 14 +++++++++++
5 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java
b/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java
index e7c012938..b1bf48748 100644
--- a/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java
+++ b/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java
@@ -591,7 +591,7 @@ public String getPattern() {
* This zone is always used for {@link Date} formatting.
* </p>
*
- * @return The time zone.
+ * @return A copy of the time zone, changing it has no effect on this
formatter.
*/
@Override
public TimeZone getTimeZone() {
diff --git a/src/main/java/org/apache/commons/lang3/time/FastDateParser.java
b/src/main/java/org/apache/commons/lang3/time/FastDateParser.java
index 3f47d4cf9..7b632083e 100644
--- a/src/main/java/org/apache/commons/lang3/time/FastDateParser.java
+++ b/src/main/java/org/apache/commons/lang3/time/FastDateParser.java
@@ -940,7 +940,8 @@ protected FastDateParser(final String pattern, final
TimeZone timeZone, final Lo
*/
protected FastDateParser(final String pattern, final TimeZone timeZone,
final Locale locale, final Date centuryStart) {
this.pattern = Objects.requireNonNull(pattern, "pattern");
- this.timeZone = Objects.requireNonNull(timeZone, "timeZone");
+ // TimeZone is mutable and instances are shared through the
FastDateFormat cache.
+ this.timeZone = (TimeZone) Objects.requireNonNull(timeZone,
"timeZone").clone();
this.locale = LocaleUtils.toLocale(locale);
final Calendar definingCalendar = Calendar.getInstance(timeZone,
this.locale);
final int centuryStartYear;
@@ -1108,7 +1109,7 @@ private Strategy getStrategy(final char f, final int
width, final Calendar defin
*/
@Override
public TimeZone getTimeZone() {
- return timeZone;
+ return (TimeZone) timeZone.clone();
}
/**
diff --git a/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java
b/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java
index 0beed585e..e67971fa2 100644
--- a/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java
+++ b/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java
@@ -1067,7 +1067,8 @@ static String getTimeZoneDisplay(final TimeZone tz, final
boolean daylight, fina
*/
protected FastDatePrinter(final String pattern, final TimeZone timeZone,
final Locale locale) {
this.pattern = pattern;
- this.timeZone = timeZone;
+ // TimeZone is mutable and instances are shared through the
FastDateFormat cache.
+ this.timeZone = (TimeZone) timeZone.clone();
this.locale = LocaleUtils.toLocale(locale);
init();
}
@@ -1304,7 +1305,7 @@ public String getPattern() {
*/
@Override
public TimeZone getTimeZone() {
- return timeZone;
+ return (TimeZone) timeZone.clone();
}
/**
diff --git
a/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java
b/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java
index 30586ac0a..d86d7ec4b 100644
--- a/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java
+++ b/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java
@@ -478,4 +478,32 @@ void testTimeDefaults() {
assertEquals(FastDateFormat.getTimeInstance(FastDateFormat.LONG),
FastDateFormat.getTimeInstance(FastDateFormat.LONG,
TimeZone.getDefault(), Locale.getDefault()));
}
+
+ /**
+ * Mutating the TimeZone passed to the factory must not change the cached,
shared instance.
+ */
+ @Test
+ void testTimeZoneArgumentIsCopied() throws ParseException {
+ final TimeZone timeZone = TimeZones.getTimeZone("UTC");
+ final FastDateFormat printer = FastDateFormat.getInstance("yyyy-MM-dd
HH:mm Z", timeZone, Locale.US);
+ final FastDateFormat parser = FastDateFormat.getInstance("yyyy-MM-dd
HH:mm", timeZone, Locale.US);
+ timeZone.setRawOffset(5 * 3_600_000);
+ assertEquals(TimeZones.getTimeZone("UTC"), printer.getTimeZone());
+ assertEquals("1970-01-01 00:00 +0000", printer.format(new Date(0)));
+ assertEquals(new Date(0), parser.parse("1970-01-01 00:00"));
+ }
+
+ /**
+ * Mutating the TimeZone returned by the getter must not change the
cached, shared instance.
+ */
+ @Test
+ void testTimeZoneGetterReturnsCopy() throws ParseException {
+ final FastDateFormat printer = FastDateFormat.getInstance("yyyy-MM-dd
HH:mm:ss Z", TimeZones.getTimeZone("UTC"), Locale.US);
+ final FastDateFormat parser = FastDateFormat.getInstance("yyyy-MM-dd
HH:mm:ss", TimeZones.getTimeZone("UTC"), Locale.US);
+ printer.getTimeZone().setRawOffset(5 * 3_600_000);
+ parser.getTimeZone().setRawOffset(5 * 3_600_000);
+ assertEquals(TimeZones.getTimeZone("UTC"), printer.getTimeZone());
+ assertEquals("1970-01-01 00:00:00 +0000", printer.format(new Date(0)));
+ assertEquals(new Date(0), parser.parse("1970-01-01 00:00:00"));
+ }
}
diff --git
a/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java
b/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java
index 22b6357ee..0fddcbafc 100644
--- a/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java
+++ b/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java
@@ -863,6 +863,20 @@ void testSpecialCharacters(final TriFunction<String,
TimeZone, Locale, DateParse
testSdfAndFdp(dpProvider, "yyyy-MM-dd 'QED'", "2003-02-10 qed", true);
}
+ /**
+ * Mutating the TimeZone passed to the constructor or returned by the
getter must not change the parser.
+ */
+ @Test
+ void testTimeZoneIsCopied() throws ParseException {
+ final TimeZone timeZone = TimeZones.getTimeZone("UTC");
+ final FastDateParser parser = new FastDateParser("yyyy-MM-dd HH:mm",
timeZone, Locale.US);
+ timeZone.setRawOffset(5 * 3_600_000);
+ assertEquals(new Date(0), parser.parse("1970-01-01 00:00"));
+ parser.getTimeZone().setRawOffset(5 * 3_600_000);
+ assertEquals(TimeZones.getTimeZone("UTC"), parser.getTimeZone());
+ assertEquals(new Date(0), parser.parse("1970-01-01 00:00"));
+ }
+
@Test
@ReadsDefaultLocale
void testTimeZoneMatches() {