LANG-916: DateFormatUtils.format does not correctly change Calendar TimeZone in certain situations

LANG-1123: Unit test FastDatePrinterTimeZonesTest needs a timezone set
This commit is contained in:
Chas Honton 2015-04-30 19:21:26 -07:00
parent bea1ae92aa
commit 775203dd2c
8 changed files with 143 additions and 113 deletions

View File

@ -22,6 +22,8 @@
<body>
<release version="3.5" date="tba" description="tba">
<action issue="LANG-1123" type="fix" dev="chas" due-to="Christian P. Momon">Unit test FastDatePrinterTimeZonesTest needs a timezone set</action>
<action issue="LANG-916" type="fix" dev="chas" due-to="Christian P. Momon">DateUtilsTest.testLang530 fails for some timezones</action>
<action issue="LANG-1116" type="fix" dev="chas" due-to="Aaron Sheldon">DateUtilsTest.testLang530 fails for some timezones</action>
<action issue="LANG-1114" type="fix" dev="britter" due-to="Andy Coates">TypeUtils.ParameterizedType#equals doesn't work with wildcard types</action>
<action issue="LANG-1119" type="add" dev="britter" due-to="Loic Guibert">Add rotate(string, int) method to StringUtils</action>

View File

@ -43,6 +43,7 @@ public class DateFormatUtils {
/**
* ISO 8601 formatter for date-time without time zone.
* The format used is {@code yyyy-MM-dd'T'HH:mm:ss}.
* This format uses the default TimeZone in effect at the time of loading DateFormatUtils class.
*/
public static final FastDateFormat ISO_DATETIME_FORMAT
= FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
@ -50,6 +51,7 @@ public class DateFormatUtils {
/**
* ISO 8601 formatter for date-time with time zone.
* The format used is {@code yyyy-MM-dd'T'HH:mm:ssZZ}.
* This format uses the default TimeZone in effect at the time of loading DateFormatUtils class.
*/
public static final FastDateFormat ISO_DATETIME_TIME_ZONE_FORMAT
= FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssZZ");
@ -57,6 +59,7 @@ public class DateFormatUtils {
/**
* ISO 8601 formatter for date without time zone.
* The format used is {@code yyyy-MM-dd}.
* This format uses the default TimeZone in effect at the time of loading DateFormatUtils class.
*/
public static final FastDateFormat ISO_DATE_FORMAT
= FastDateFormat.getInstance("yyyy-MM-dd");
@ -66,6 +69,7 @@ public class DateFormatUtils {
* The format used is {@code yyyy-MM-ddZZ}.
* This pattern does not comply with the formal ISO 8601 specification
* as the standard does not allow a time zone without a time.
* This format uses the default TimeZone in effect at the time of loading DateFormatUtils class.
*/
public static final FastDateFormat ISO_DATE_TIME_ZONE_FORMAT
= FastDateFormat.getInstance("yyyy-MM-ddZZ");
@ -73,6 +77,7 @@ public class DateFormatUtils {
/**
* ISO 8601 formatter for time without time zone.
* The format used is {@code 'T'HH:mm:ss}.
* This format uses the default TimeZone in effect at the time of loading DateFormatUtils class.
*/
public static final FastDateFormat ISO_TIME_FORMAT
= FastDateFormat.getInstance("'T'HH:mm:ss");
@ -80,6 +85,7 @@ public class DateFormatUtils {
/**
* ISO 8601 formatter for time with time zone.
* The format used is {@code 'T'HH:mm:ssZZ}.
* This format uses the default TimeZone in effect at the time of loading DateFormatUtils class.
*/
public static final FastDateFormat ISO_TIME_TIME_ZONE_FORMAT
= FastDateFormat.getInstance("'T'HH:mm:ssZZ");
@ -89,6 +95,7 @@ public class DateFormatUtils {
* The format used is {@code HH:mm:ss}.
* This pattern does not comply with the formal ISO 8601 specification
* as the standard requires the 'T' prefix for times.
* This format uses the default TimeZone in effect at the time of loading DateFormatUtils class.
*/
public static final FastDateFormat ISO_TIME_NO_T_FORMAT
= FastDateFormat.getInstance("HH:mm:ss");
@ -98,6 +105,7 @@ public class DateFormatUtils {
* The format used is {@code HH:mm:ssZZ}.
* This pattern does not comply with the formal ISO 8601 specification
* as the standard requires the 'T' prefix for times.
* This format uses the default TimeZone in effect at the time of loading DateFormatUtils class.
*/
public static final FastDateFormat ISO_TIME_NO_T_TIME_ZONE_FORMAT
= FastDateFormat.getInstance("HH:mm:ssZZ");
@ -105,6 +113,7 @@ public class DateFormatUtils {
/**
* SMTP (and probably other) date headers.
* The format used is {@code EEE, dd MMM yyyy HH:mm:ss Z} in US locale.
* This format uses the default TimeZone in effect at the time of loading DateFormatUtils class.
*/
public static final FastDateFormat SMTP_DATETIME_FORMAT
= FastDateFormat.getInstance("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);

View File

@ -49,8 +49,11 @@ public interface DatePrinter {
/**
* <p>Formats a {@code Calendar} object.</p>
* The TimeZone set on the Calendar is only used to adjust the time offset.
* The TimeZone specified during the construction of the Parser will determine the TimeZone
* used in the formatted string.
*
* @param calendar the calendar to format
* @param calendar the calendar to format.
* @return the formatted string
*/
String format(Calendar calendar);
@ -76,8 +79,10 @@ public interface DatePrinter {
StringBuffer format(Date date, StringBuffer buf);
/**
* <p>Formats a {@code Calendar} object into the
* supplied {@code StringBuffer}.</p>
* <p>Formats a {@code Calendar} object into the supplied {@code StringBuffer}.</p>
* The TimeZone set on the Calendar is only used to adjust the time offset.
* The TimeZone specified during the construction of the Parser will determine the TimeZone
* used in the formatted string.
*
* @param calendar the calendar to format
* @param buf the buffer to format into

View File

@ -477,7 +477,8 @@ public class FastDatePrinter implements DatePrinter, Serializable {
*/
@Override
public StringBuffer format(final Calendar calendar, final StringBuffer buf) {
return applyRules(calendar, buf);
// do not pass in calendar directly, this will cause TimeZone of FastDatePrinter to be ignored
return format(calendar.getTime(), buf);
}
/**

View File

@ -108,132 +108,75 @@ public class DateFormatUtilsTest {
assertEquals ("2005-01-01T12:00:00", DateFormatUtils.formatUTC(c.getTime().getTime(), DateFormatUtils.ISO_DATETIME_FORMAT.getPattern(), Locale.US));
}
private void assertFormats(String expectedValue, String pattern, TimeZone timeZone, Calendar cal) {
assertEquals(expectedValue, DateFormatUtils.format(cal.getTime(), pattern, timeZone));
assertEquals(expectedValue, DateFormatUtils.format(cal.getTime().getTime(), pattern, timeZone));
assertEquals(expectedValue, DateFormatUtils.format(cal, pattern, timeZone));
}
private Calendar createFebruaryTestDate(final TimeZone timeZone) {
final Calendar cal = Calendar.getInstance(timeZone);
cal.set(2002, Calendar.FEBRUARY, 23, 9, 11, 12);
return cal;
}
private Calendar createJuneTestDate(final TimeZone timeZone) {
final Calendar cal = Calendar.getInstance(timeZone);
cal.set(2003, Calendar.JUNE, 8, 10, 11, 12);
return cal;
}
private void testGmtMinus3(String expectedValue, String pattern) {
final TimeZone timeZone = TimeZone.getTimeZone("GMT-3");
assertFormats(expectedValue, pattern, timeZone, createFebruaryTestDate(timeZone));
}
private void testUTC(String expectedValue, String pattern) {
final TimeZone timeZone = TimeZone.getTimeZone("UTC");
assertFormats(expectedValue, pattern, timeZone, createFebruaryTestDate(timeZone));
}
@Test
public void testDateTimeISO() throws Exception {
final TimeZone timeZone = TimeZone.getTimeZone("GMT-3");
final Calendar cal = Calendar.getInstance(timeZone);
cal.set(2002, Calendar.FEBRUARY, 23, 9, 11, 12);
String text = DateFormatUtils.format(cal.getTime(),
DateFormatUtils.ISO_DATETIME_FORMAT.getPattern(), timeZone);
assertEquals("2002-02-23T09:11:12", text);
text = DateFormatUtils.format(cal.getTime().getTime(),
DateFormatUtils.ISO_DATETIME_FORMAT.getPattern(), timeZone);
assertEquals("2002-02-23T09:11:12", text);
text = DateFormatUtils.ISO_DATETIME_FORMAT.format(cal);
assertEquals("2002-02-23T09:11:12", text);
text = DateFormatUtils.format(cal.getTime(),
DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), timeZone);
assertEquals("2002-02-23T09:11:12-03:00", text);
text = DateFormatUtils.format(cal.getTime().getTime(),
DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), timeZone);
assertEquals("2002-02-23T09:11:12-03:00", text);
text = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(cal);
assertEquals("2002-02-23T09:11:12-03:00", text);
Calendar utcCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
utcCal.set(2002, Calendar.FEBRUARY, 23, 9, 11, 12);
utcCal.set(Calendar.MILLISECOND, 0);
text = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(utcCal);
assertEquals("2002-02-23T09:11:12Z", text);
Date date = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.parse(text);
assertEquals(utcCal.getTime(), date);
testGmtMinus3("2002-02-23T09:11:12", DateFormatUtils.ISO_DATETIME_FORMAT.getPattern());
testGmtMinus3("2002-02-23T09:11:12-03:00", DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern());
testUTC("2002-02-23T09:11:12Z", DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern());
}
@Test
public void testDateISO(){
final TimeZone timeZone = TimeZone.getTimeZone("GMT-3");
final Calendar cal = Calendar.getInstance(timeZone);
cal.set(2002, Calendar.FEBRUARY, 23, 10, 11, 12);
String text = DateFormatUtils.format(cal.getTime(),
DateFormatUtils.ISO_DATE_FORMAT.getPattern(), timeZone);
assertEquals("2002-02-23", text);
text = DateFormatUtils.format(cal.getTime().getTime(),
DateFormatUtils.ISO_DATE_FORMAT.getPattern(), timeZone);
assertEquals("2002-02-23", text);
text = DateFormatUtils.ISO_DATE_FORMAT.format(cal);
assertEquals("2002-02-23", text);
text = DateFormatUtils.format(cal.getTime(),
DateFormatUtils.ISO_DATE_TIME_ZONE_FORMAT.getPattern(), timeZone);
assertEquals("2002-02-23-03:00", text);
text = DateFormatUtils.format(cal.getTime().getTime(),
DateFormatUtils.ISO_DATE_TIME_ZONE_FORMAT.getPattern(), timeZone);
assertEquals("2002-02-23-03:00", text);
text = DateFormatUtils.ISO_DATE_TIME_ZONE_FORMAT.format(cal);
assertEquals("2002-02-23-03:00", text);
testGmtMinus3("2002-02-23", DateFormatUtils.ISO_DATE_FORMAT.getPattern());
testGmtMinus3("2002-02-23-03:00", DateFormatUtils.ISO_DATE_TIME_ZONE_FORMAT.getPattern());
testUTC("2002-02-23Z", DateFormatUtils.ISO_DATE_TIME_ZONE_FORMAT.getPattern());
}
@Test
public void testTimeISO(){
final TimeZone timeZone = TimeZone.getTimeZone("GMT-3");
final Calendar cal = Calendar.getInstance(timeZone);
cal.set(2002, Calendar.FEBRUARY, 23, 10, 11, 12);
String text = DateFormatUtils.format(cal.getTime(),
DateFormatUtils.ISO_TIME_FORMAT.getPattern(), timeZone);
assertEquals("T10:11:12", text);
text = DateFormatUtils.format(cal.getTime().getTime(),
DateFormatUtils.ISO_TIME_FORMAT.getPattern(), timeZone);
assertEquals("T10:11:12", text);
text = DateFormatUtils.ISO_TIME_FORMAT.format(cal);
assertEquals("T10:11:12", text);
text = DateFormatUtils.format(cal.getTime(),
DateFormatUtils.ISO_TIME_TIME_ZONE_FORMAT.getPattern(), timeZone);
assertEquals("T10:11:12-03:00", text);
text = DateFormatUtils.format(cal.getTime().getTime(),
DateFormatUtils.ISO_TIME_TIME_ZONE_FORMAT.getPattern(), timeZone);
assertEquals("T10:11:12-03:00", text);
text = DateFormatUtils.ISO_TIME_TIME_ZONE_FORMAT.format(cal);
assertEquals("T10:11:12-03:00", text);
testGmtMinus3("T09:11:12", DateFormatUtils.ISO_TIME_FORMAT.getPattern());
testGmtMinus3("T09:11:12-03:00", DateFormatUtils.ISO_TIME_TIME_ZONE_FORMAT.getPattern());
testUTC("T09:11:12Z", DateFormatUtils.ISO_TIME_TIME_ZONE_FORMAT.getPattern());
}
@Test
public void testTimeNoTISO(){
final TimeZone timeZone = TimeZone.getTimeZone("GMT-3");
final Calendar cal = Calendar.getInstance(timeZone);
cal.set(2002, Calendar.FEBRUARY, 23, 10, 11, 12);
String text = DateFormatUtils.format(cal.getTime(),
DateFormatUtils.ISO_TIME_NO_T_FORMAT.getPattern(), timeZone);
assertEquals("10:11:12", text);
text = DateFormatUtils.format(cal.getTime().getTime(),
DateFormatUtils.ISO_TIME_NO_T_FORMAT.getPattern(), timeZone);
assertEquals("10:11:12", text);
text = DateFormatUtils.ISO_TIME_NO_T_FORMAT.format(cal);
assertEquals("10:11:12", text);
text = DateFormatUtils.format(cal.getTime(),
DateFormatUtils.ISO_TIME_NO_T_TIME_ZONE_FORMAT.getPattern(), timeZone);
assertEquals("10:11:12-03:00", text);
text = DateFormatUtils.format(cal.getTime().getTime(),
DateFormatUtils.ISO_TIME_NO_T_TIME_ZONE_FORMAT.getPattern(), timeZone);
assertEquals("10:11:12-03:00", text);
text = DateFormatUtils.ISO_TIME_NO_T_TIME_ZONE_FORMAT.format(cal);
assertEquals("10:11:12-03:00", text);
testGmtMinus3("09:11:12", DateFormatUtils.ISO_TIME_NO_T_FORMAT.getPattern());
testGmtMinus3("09:11:12-03:00", DateFormatUtils.ISO_TIME_NO_T_TIME_ZONE_FORMAT.getPattern());
testUTC("09:11:12Z", DateFormatUtils.ISO_TIME_NO_T_TIME_ZONE_FORMAT.getPattern());
}
@Test
public void testSMTP(){
final TimeZone timeZone = TimeZone.getTimeZone("GMT-3");
final Calendar cal = Calendar.getInstance(timeZone);
cal.set(2003, Calendar.JUNE, 8, 10, 11, 12);
String text = DateFormatUtils.format(cal.getTime(),
DateFormatUtils.SMTP_DATETIME_FORMAT.getPattern(), timeZone,
DateFormatUtils.SMTP_DATETIME_FORMAT.getLocale());
assertEquals("Sun, 08 Jun 2003 10:11:12 -0300", text);
text = DateFormatUtils.format(cal.getTime().getTime(),
DateFormatUtils.SMTP_DATETIME_FORMAT.getPattern(), timeZone,
DateFormatUtils.SMTP_DATETIME_FORMAT.getLocale());
assertEquals("Sun, 08 Jun 2003 10:11:12 -0300", text);
text = DateFormatUtils.SMTP_DATETIME_FORMAT.format(cal);
assertEquals("Sun, 08 Jun 2003 10:11:12 -0300", text);
TimeZone timeZone = TimeZone.getTimeZone("GMT-3");
Calendar june = createJuneTestDate(timeZone);
// format UTC
text = DateFormatUtils.formatUTC(cal.getTime().getTime(),
DateFormatUtils.SMTP_DATETIME_FORMAT.getPattern(),
DateFormatUtils.SMTP_DATETIME_FORMAT.getLocale());
assertEquals("Sun, 08 Jun 2003 13:11:12 +0000", text);
assertFormats("Sun, 08 Jun 2003 10:11:12 -0300", DateFormatUtils.SMTP_DATETIME_FORMAT.getPattern(),
timeZone, june);
timeZone = TimeZone.getTimeZone("UTC");
june = createJuneTestDate(timeZone);
assertFormats("Sun, 08 Jun 2003 10:11:12 +0000", DateFormatUtils.SMTP_DATETIME_FORMAT.getPattern(),
timeZone, june);
}
/*
@ -285,4 +228,46 @@ public class DateFormatUtilsTest {
TimeZone.setDefault(save);
}
}
/**
* According to LANG-916 (https://issues.apache.org/jira/browse/LANG-916),
* the format method did contain a bug: it did not use the TimeZone data.
*
* This method test that the bug is fixed.
*/
@Test
public void testLang916() throws Exception {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Paris"));
cal.clear();
cal.set(2009, 9, 16, 8, 42, 16);
// Long.
{
String value = DateFormatUtils.format(cal.getTimeInMillis(), DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZone.getTimeZone("Europe/Paris"));
assertEquals("long", "2009-10-16T08:42:16+02:00", value);
}
{
String value = DateFormatUtils.format(cal.getTimeInMillis(), DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZone.getTimeZone("Asia/Kolkata"));
assertEquals("long", "2009-10-16T12:12:16+05:30", value);
}
{
String value = DateFormatUtils.format(cal.getTimeInMillis(), DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZone.getTimeZone("Europe/London"));
assertEquals("long", "2009-10-16T07:42:16+01:00", value);
}
// Calendar.
{
String value = DateFormatUtils.format(cal, DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZone.getTimeZone("Europe/Paris"));
assertEquals("calendar", "2009-10-16T08:42:16+02:00", value);
}
{
String value = DateFormatUtils.format(cal, DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZone.getTimeZone("Asia/Kolkata"));
assertEquals("calendar", "2009-10-16T12:12:16+05:30", value);
}
{
String value = DateFormatUtils.format(cal, DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZone.getTimeZone("Europe/London"));
assertEquals("calendar", "2009-10-16T07:42:16+01:00", value);
}
}
}

View File

@ -266,7 +266,7 @@ public class DurationFormatUtilsTest {
cal.set(Calendar.MILLISECOND, 1);
String text;
// repeat a test from testDateTimeISO to compare extended and not extended.
text = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(cal);
text = DateFormatUtils.format(cal, DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), timeZone);
assertEquals("2002-02-23T09:11:12-03:00", text);
// test fixture is the same as above, but now with extended format.
text = DurationFormatUtils.formatPeriod(base.getTime().getTime(), cal.getTime().getTime(),

View File

@ -214,7 +214,7 @@ public class FastDatePrinterTest {
final DatePrinter format = getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone("GMT"));
assertEquals("dateTime", "2009-10-16T16:42:16.000Z", format.format(cal.getTime()));
assertEquals("dateTime", "2009-10-16T08:42:16.000Z", format.format(cal));
assertEquals("dateTime", "2009-10-16T16:42:16.000Z", format.format(cal));
}
@Test
@ -339,4 +339,32 @@ public class FastDatePrinterTest {
assertEquals("0002", getInstance("dddd", SWEDEN).format(cal));
assertEquals("00002", getInstance("ddddd", SWEDEN).format(cal));
}
/**
* According to LANG-916 (https://issues.apache.org/jira/browse/LANG-916),
* the format method did contain a bug: it did not use the TimeZone data.
*
* This method test that the bug is fixed.
*/
@Test
public void testLang916() throws Exception {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Paris"));
cal.clear();
cal.set(2009, 9, 16, 8, 42, 16);
// calendar fast.
{
String value = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss Z", TimeZone.getTimeZone("Europe/Paris")).format(cal);
assertEquals("calendar", "2009-10-16T08:42:16 +0200", value);
}
{
String value = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss Z", TimeZone.getTimeZone("Asia/Kolkata")).format(cal);
assertEquals("calendar", "2009-10-16T12:12:16 +0530", value);
}
{
String value = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss Z", TimeZone.getTimeZone("Europe/London")).format(cal);
assertEquals("calendar", "2009-10-16T07:42:16 +0100", value);
}
}
}

View File

@ -57,7 +57,7 @@ public class FastDatePrinterTimeZonesTest {
final SimpleDateFormat sdf = new SimpleDateFormat(PATTERN);
sdf.setTimeZone(timeZone);
final String expectedValue = sdf.format(cal.getTime());
final String actualValue = FastDateFormat.getInstance(PATTERN).format(cal);
final String actualValue = FastDateFormat.getInstance(PATTERN, this.timeZone).format(cal);
assertEquals(expectedValue, actualValue);
}