[
https://issues.apache.org/jira/browse/LANG-1791?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Igor Rodchenkov updated LANG-1791:
----------------------------------
Description:
Lately, we discovered a bug which is better demonstrate with this test case
(it's not perfect because always passes in GMT zone, but e.g. in Toronto it
passes when commons-lang3 v3.0.1 used and fails when v3.18.0 or 3.19.0 is used;
and yes, in this special case, we have somewhat legacy java code using Calendar
etc., but that was what broke in our app by this library upgrade, which
supoosed to be backward compatible):
{code:java}
/*
* org.apache.commons.lang3.time.FastDateFormat
* In dateFormatter.format(timeCal), when time was not set explicitly, would
make e.g.
* the following timestamps in Toronto at 10 am on 2025-09-17:
* "2025091714" - UTC, as expected, when using commons-lang3 v3.0.1
* "2025091710" - EDT, when using commons-lang3 v3.18.0 :(
*/
@Test
public void fastDateFormatFormatterUsingCalendarShouldMakeGmtTimestamp() {
FastDateFormat dateFormatter = FastDateFormat.getInstance("yyyyMMddHH");
TimeZone timeZone = TimeZone.getTimeZone("GMT");
Calendar timeCal = Calendar.getInstance(timeZone);
Date time1 = timeCal.getTime(); //in local time zone
ZoneId zone = ZoneId.systemDefault();
ZoneOffset off = zone.getRules().getOffset(time1.toInstant());
System.out.printf("Time: %s, zone: %s, offset(s): %s\n", time1, zone,
off.getTotalSeconds());
String timestamp1 = dateFormatter.format(timeCal); //makes local zone
timestamp when commons-lang3 v3.18.0 is used.
Date time = Date.from(Instant.now().minusSeconds(off.getTotalSeconds()));
String timestamp2 = dateFormatter.format(time); //GMT timestamp
assertEquals(timestamp2, timestamp1);
}{code}
I think the bug is in 3.18.0 the FastDatePrinter here -
{code:java}
@Override
public <B extends Appendable> B format(Calendar calendar, final B buf) {
// do not pass in calendar directly, this will cause TimeZone of
FastDatePrinter to be ignored
if (!calendar.getTimeZone().equals(timeZone)) {
calendar = (Calendar) calendar.clone();
calendar.setTimeZone(timeZone);
}
return applyRules(calendar, buf);
}{code}
- note misleading comment, - it's calendar's timeZone is in fact there ignored
instead of date printer's timeZone!
was:
Lately, we discovered a bug which is better demonstrate with this test case
(it's not perfect because always passes in GMT zone, but e.g. in Toronto it
passes when commons-lang3 v3.0.1 used and fails when v3.18.0 or 3.19.0 is used;
and yes, in this special case, we have somewhat legacy java code using Calendar
etc., but that was what broke in our app by this library upgrade, which
supoosed to be backward compatible):
{code:java}
/*
* org.apache.commons.lang3.time.FastDateFormat
* In dateFormatter.format(timeCal), when time was not set explicitly, would
make e.g.
* the following timestamps in Toronto at 10 am on 2025-09-17:
* "2025091714" - UTC, as expected, when using commons-lang3 v3.0.1
* "2025091710" - EDT, when using commons-lang3 v3.18.0 :(
*/
@Test
public void fastDateFormatFormatterUsingCalendarShouldMakeGmtTimestamp() {
FastDateFormat dateFormatter = FastDateFormat.getInstance("yyyyMMddHH");
TimeZone timeZone = TimeZone.getTimeZone("GMT");
Calendar timeCal = Calendar.getInstance(timeZone);
Date time1 = timeCal.getTime(); //in local time zone
ZoneId zone = ZoneId.systemDefault();
ZoneOffset off = zone.getRules().getOffset(time1.toInstant());
System.out.printf("Time: %s, zone: %s, offset(s): %s\n", time1, zone,
off.getTotalSeconds());
String timestamp1 = dateFormatter.format(timeCal); //makes local zone
timestamp when commons-lang3 v3.18.0 is used.
Date time = Date.from(Instant.now().minusSeconds(off.getTotalSeconds()));
String timestamp2 = dateFormatter.format(time); //GMT timestamp
assertEquals(timestamp2, timestamp1);
}{code}
> FastDateFormat behavior changed between 3.0.1 and e.g. 3.18.0 and later
> -----------------------------------------------------------------------
>
> Key: LANG-1791
> URL: https://issues.apache.org/jira/browse/LANG-1791
> Project: Commons Lang
> Issue Type: Bug
> Environment: Linux (Ubuntu 24.04), temurin-17.0.16 JDK, run in
> Toronto.
> Reporter: Igor Rodchenkov
> Priority: Major
>
> Lately, we discovered a bug which is better demonstrate with this test case
> (it's not perfect because always passes in GMT zone, but e.g. in Toronto it
> passes when commons-lang3 v3.0.1 used and fails when v3.18.0 or 3.19.0 is
> used; and yes, in this special case, we have somewhat legacy java code using
> Calendar etc., but that was what broke in our app by this library upgrade,
> which supoosed to be backward compatible):
>
>
> {code:java}
> /*
> * org.apache.commons.lang3.time.FastDateFormat
> * In dateFormatter.format(timeCal), when time was not set explicitly, would
> make e.g.
> * the following timestamps in Toronto at 10 am on 2025-09-17:
> * "2025091714" - UTC, as expected, when using commons-lang3 v3.0.1
> * "2025091710" - EDT, when using commons-lang3 v3.18.0 :(
> */
> @Test
> public void fastDateFormatFormatterUsingCalendarShouldMakeGmtTimestamp() {
> FastDateFormat dateFormatter = FastDateFormat.getInstance("yyyyMMddHH");
> TimeZone timeZone = TimeZone.getTimeZone("GMT");
> Calendar timeCal = Calendar.getInstance(timeZone);
> Date time1 = timeCal.getTime(); //in local time zone
> ZoneId zone = ZoneId.systemDefault();
> ZoneOffset off = zone.getRules().getOffset(time1.toInstant());
> System.out.printf("Time: %s, zone: %s, offset(s): %s\n", time1, zone,
> off.getTotalSeconds());
> String timestamp1 = dateFormatter.format(timeCal); //makes local zone
> timestamp when commons-lang3 v3.18.0 is used.
> Date time = Date.from(Instant.now().minusSeconds(off.getTotalSeconds()));
> String timestamp2 = dateFormatter.format(time); //GMT timestamp
> assertEquals(timestamp2, timestamp1);
> }{code}
>
> I think the bug is in 3.18.0 the FastDatePrinter here -
> {code:java}
> @Override
> public <B extends Appendable> B format(Calendar calendar, final B buf) {
> // do not pass in calendar directly, this will cause TimeZone of
> FastDatePrinter to be ignored
> if (!calendar.getTimeZone().equals(timeZone)) {
> calendar = (Calendar) calendar.clone();
> calendar.setTimeZone(timeZone);
> }
> return applyRules(calendar, buf);
> }{code}
> - note misleading comment, - it's calendar's timeZone is in fact there
> ignored instead of date printer's timeZone!
>
>
--
This message was sent by Atlassian Jira
(v8.20.10#820010)