https://bz.apache.org/bugzilla/show_bug.cgi?id=59909master
@@ -40,6 +40,15 @@ Fixed bugs: | |||||
worked on OSes where sed is GNU sed. | worked on OSes where sed is GNU sed. | ||||
Bugzilla Report 59898 | Bugzilla Report 59898 | ||||
* <touch>'s default pattern as well as the default patterns used by | |||||
the <date> (resource) selectors depended on the JDK being used - or | |||||
rather the locale provider being used and the default locale | |||||
provider changed with Java 9. | |||||
They are now fixed and the documentation has been updated to | |||||
reflect the real patterns used rather than a non-formal description | |||||
of the expected format. | |||||
Bugzilla Report 59909 | |||||
Other changes: | Other changes: | ||||
-------------- | -------------- | ||||
@@ -74,8 +74,10 @@ resource collections (which also includes directories). Prior to Apache Ant | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">pattern</td> | <td valign="top">pattern</td> | ||||
<td valign="top">SimpleDateFormat-compatible pattern string. | |||||
Defaults to MM/DD/YYYY HH:MM AM_or_PM or MM/DD/YYYY HH:MM:SS AM_or_PM. | |||||
<td valign="top">SimpleDateFormat-compatible pattern string using | |||||
the current locale. | |||||
Defaults to "MM/dd/YYYY hh:mm a" or "MM/dd/yyyy hh:mm:ss a" | |||||
using the US locale. | |||||
<b>Since Ant 1.6.3</b></td> | <b>Since Ant 1.6.3</b></td> | ||||
<td valign="top" align="center">No</td> | <td valign="top" align="center">No</td> | ||||
</tr> | </tr> | ||||
@@ -641,9 +641,10 @@ platforms. | |||||
<tr> | <tr> | ||||
<td valign="top">pattern</td> | <td valign="top">pattern</td> | ||||
<td valign="top">SimpleDateFormat-compatible pattern | <td valign="top">SimpleDateFormat-compatible pattern | ||||
for use with the <code>datetime</code> attribute</td> | |||||
for use with the <code>datetime</code> attribute using the | |||||
current locale</td> | |||||
<td align="center" valign="top"> | <td align="center" valign="top"> | ||||
No, default is "MM/DD/YYYY HH:MM AM_or_PM"</td> | |||||
No, default is "MM/dd/yyyy hh:mm a" using the US locale</td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">granularity</td> | <td valign="top">granularity</td> | ||||
@@ -173,9 +173,9 @@ | |||||
<tr> | <tr> | ||||
<td valign="top">datetime</td> | <td valign="top">datetime</td> | ||||
<td valign="top">Specifies the date and time to test for. | <td valign="top">Specifies the date and time to test for. | ||||
Should be in the format MM/DD/YYYY HH:MM AM_or_PM, or | |||||
an alternative pattern specified via the <i>pattern</i> | |||||
attribute. | |||||
Should be in the format "MM/dd/yyyy hh:mm a" using the US | |||||
locale, or an alternative pattern specified via | |||||
the <i>pattern</i> attribute. | |||||
</td> | </td> | ||||
<td valign="top" align="center" rowspan="2">At least one of the two.</td> | <td valign="top" align="center" rowspan="2">At least one of the two.</td> | ||||
</tr> | </tr> | ||||
@@ -212,7 +212,8 @@ | |||||
<tr> | <tr> | ||||
<td valign="top">pattern</td> | <td valign="top">pattern</td> | ||||
<td valign="top">The <CODE>SimpleDateFormat</CODE>-compatible pattern | <td valign="top">The <CODE>SimpleDateFormat</CODE>-compatible pattern | ||||
to use when interpreting the <i>datetime</i> attribute. | |||||
to use when interpreting the <i>datetime</i> attribute using | |||||
the current locale. | |||||
<i>Since Ant 1.6.2</i> | <i>Since Ant 1.6.2</i> | ||||
</td> | </td> | ||||
<td valign="top" align="center">No</td> | <td valign="top" align="center">No</td> | ||||
@@ -61,23 +61,29 @@ public class Touch extends Task { | |||||
public static final DateFormatFactory DEFAULT_DF_FACTORY | public static final DateFormatFactory DEFAULT_DF_FACTORY | ||||
= new DateFormatFactory() { | = new DateFormatFactory() { | ||||
/* | |||||
* The initial version used DateFormat.SHORT for the | |||||
* time format, which ignores seconds. If we want | |||||
* seconds as well, we need DateFormat.MEDIUM, which | |||||
* in turn would break all old build files. | |||||
* | |||||
* First try to parse with DateFormat.SHORT and if | |||||
* that fails with MEDIUM - throw an exception if both | |||||
* fail. | |||||
*/ | |||||
private ThreadLocal<DateFormat> primary = | |||||
new ThreadLocal<DateFormat>() { | |||||
@Override | |||||
protected DateFormat initialValue() { | |||||
return new SimpleDateFormat("MM/dd/yyyy hh:mm a", | |||||
Locale.US); | |||||
} | |||||
}; | |||||
private ThreadLocal<DateFormat> fallback = | |||||
new ThreadLocal<DateFormat>() { | |||||
@Override | |||||
protected DateFormat initialValue() { | |||||
return new SimpleDateFormat("MM/dd/yyyy hh:mm:ss a", | |||||
Locale.US); | |||||
} | |||||
}; | |||||
public DateFormat getPrimaryFormat() { | public DateFormat getPrimaryFormat() { | ||||
return DateFormat.getDateTimeInstance(DateFormat.SHORT, | |||||
DateFormat.SHORT, Locale.US); | |||||
return primary.get(); | |||||
} | } | ||||
public DateFormat getFallbackFormat() { | public DateFormat getFallbackFormat() { | ||||
return DateFormat.getDateTimeInstance(DateFormat.SHORT, | |||||
DateFormat.MEDIUM, Locale.US); | |||||
return fallback.get(); | |||||
} | } | ||||
}; | }; | ||||
private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); | private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); | ||||
@@ -137,10 +137,10 @@ public class Date implements ResourceSelector { | |||||
throw new BuildException(MILLIS_OR_DATETIME); | throw new BuildException(MILLIS_OR_DATETIME); | ||||
} | } | ||||
if (millis == null) { | if (millis == null) { | ||||
DateFormat df = ((pattern == null) | |||||
? DateFormat.getDateTimeInstance( | |||||
DateFormat.SHORT, DateFormat.SHORT, Locale.US) | |||||
: new SimpleDateFormat(pattern)); | |||||
String p = pattern == null ? "MM/dd/yyyy hh:mm a" : pattern; | |||||
DateFormat df = pattern == null | |||||
? new SimpleDateFormat(p, Locale.US) | |||||
: new SimpleDateFormat(p); | |||||
try { | try { | ||||
long m = df.parse(dateTime).getTime(); | long m = df.parse(dateTime).getTime(); | ||||
if (m < 0) { | if (m < 0) { | ||||
@@ -151,9 +151,8 @@ public class Date implements ResourceSelector { | |||||
setMillis(m); | setMillis(m); | ||||
} catch (ParseException pe) { | } catch (ParseException pe) { | ||||
throw new BuildException("Date of " + dateTime | throw new BuildException("Date of " + dateTime | ||||
+ " Cannot be parsed correctly. It should be in" | |||||
+ (pattern == null | |||||
? " MM/DD/YYYY HH:MM AM_PM" : pattern) + " format."); | |||||
+ " Cannot be parsed correctly. It should be in '" | |||||
+ p + "' format."); | |||||
} | } | ||||
} | } | ||||
return when.evaluate(r.getLastModified(), millis.longValue(), granularity); | return when.evaluate(r.getLastModified(), millis.longValue(), granularity); | ||||
@@ -34,7 +34,7 @@ import org.apache.tools.ant.types.DataType; | |||||
public abstract class BaseSelector extends DataType implements FileSelector { | public abstract class BaseSelector extends DataType implements FileSelector { | ||||
private String errmsg = null; | private String errmsg = null; | ||||
private Throwable cause; | |||||
/** | /** | ||||
* Do nothing constructor. | * Do nothing constructor. | ||||
@@ -54,6 +54,19 @@ public abstract class BaseSelector extends DataType implements FileSelector { | |||||
} | } | ||||
} | } | ||||
/** | |||||
* Allows all selectors to indicate a setup error. Note that only | |||||
* the first error message is recorded. | |||||
* | |||||
* @param msg The error message any BuildException should throw. | |||||
*/ | |||||
public void setError(String msg, Throwable cause) { | |||||
if (errmsg == null) { | |||||
errmsg = msg; | |||||
this.cause = cause; | |||||
} | |||||
} | |||||
/** | /** | ||||
* Returns any error messages that have been set. | * Returns any error messages that have been set. | ||||
* | * | ||||
@@ -87,7 +100,7 @@ public abstract class BaseSelector extends DataType implements FileSelector { | |||||
verifySettings(); | verifySettings(); | ||||
} | } | ||||
if (getError() != null) { | if (getError() != null) { | ||||
throw new BuildException(errmsg); | |||||
throw new BuildException(errmsg, cause); | |||||
} | } | ||||
if (!isReference()) { | if (!isReference()) { | ||||
dieOnCircularReference(); | dieOnCircularReference(); | ||||
@@ -209,11 +209,10 @@ public class DateSelector extends BaseExtendSelector { | |||||
setError("You must provide a datetime or the number of " | setError("You must provide a datetime or the number of " | ||||
+ "milliseconds."); | + "milliseconds."); | ||||
} else if (millis < 0 && dateTime != null) { | } else if (millis < 0 && dateTime != null) { | ||||
// check millis and only set it once. | |||||
DateFormat df = ((pattern == null) | |||||
? DateFormat.getDateTimeInstance( | |||||
DateFormat.SHORT, DateFormat.SHORT, Locale.US) | |||||
: new SimpleDateFormat(pattern)); | |||||
String p = pattern == null ? "MM/dd/yyyy hh:mm a" : pattern; | |||||
DateFormat df = pattern == null | |||||
? new SimpleDateFormat(p, Locale.US) | |||||
: new SimpleDateFormat(p); | |||||
try { | try { | ||||
setMillis(df.parse(dateTime).getTime()); | setMillis(df.parse(dateTime).getTime()); | ||||
@@ -224,9 +223,8 @@ public class DateSelector extends BaseExtendSelector { | |||||
} | } | ||||
} catch (ParseException pe) { | } catch (ParseException pe) { | ||||
setError("Date of " + dateTime | setError("Date of " + dateTime | ||||
+ " Cannot be parsed correctly. It should be in" | |||||
+ ((pattern == null) | |||||
? " MM/DD/YYYY HH:MM AM_PM" : pattern) + " format."); | |||||
+ " Cannot be parsed correctly. It should be in '" | |||||
+ p + "' format.", pe); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -73,7 +73,7 @@ public class DateSelectorTest { | |||||
} catch (BuildException be3) { | } catch (BuildException be3) { | ||||
assertEquals("Date of this is not a date" | assertEquals("Date of this is not a date" | ||||
+ " Cannot be parsed correctly. It should be in" | + " Cannot be parsed correctly. It should be in" | ||||
+ " MM/DD/YYYY HH:MM AM_PM format.", be3.getMessage()); | |||||
+ " 'MM/dd/yyyy hh:mm a' format.", be3.getMessage()); | |||||
} | } | ||||
s = new DateSelector(); | s = new DateSelector(); | ||||