You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2026/05/27 20:03:59 UTC
(commons-lang) 01/02: Javadoc
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
commit 093cf417bfc2a295c62fd541d39a994fa1d37804
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed May 27 16:02:07 2026 -0400
Javadoc
---
.../commons/lang3/time/DurationFormatUtils.java | 253 +++++++++------------
1 file changed, 107 insertions(+), 146 deletions(-)
diff --git a/src/main/java/org/apache/commons/lang3/time/DurationFormatUtils.java b/src/main/java/org/apache/commons/lang3/time/DurationFormatUtils.java
index c89ecc786..9e88bf115 100644
--- a/src/main/java/org/apache/commons/lang3/time/DurationFormatUtils.java
+++ b/src/main/java/org/apache/commons/lang3/time/DurationFormatUtils.java
@@ -102,9 +102,9 @@ static final class Token {
/**
* Helper method to determine if a set of tokens contain a value
*
- * @param tokens set to look in
- * @param value to look for
- * @return boolean {@code true} if contained
+ * @param tokens set to search.
+ * @param value to look find.
+ * @return boolean {@code true} if contained.
*/
static boolean containsTokenWithValue(final Token[] tokens, final Object value) {
return Stream.of(tokens).anyMatch(token -> token.getValue() == value);
@@ -118,8 +118,8 @@ static boolean containsTokenWithValue(final Token[] tokens, final Object value)
* Wraps a token around a value. A value would be something like a 'Y'.
*
* @param value value to wrap, non-null.
- * @param optional whether the token is optional
- * @param optionalIndex the index of the optional token within the pattern
+ * @param optional whether the token is optional.
+ * @param optionalIndex the index of the optional token within the pattern.
*/
Token(final CharSequence value, final boolean optional, final int optionalIndex) {
this.value = Objects.requireNonNull(value, "value");
@@ -132,8 +132,8 @@ static boolean containsTokenWithValue(final Token[] tokens, final Object value)
/**
* Supports equality of this Token to another Token.
*
- * @param obj2 Object to consider equality of
- * @return boolean {@code true} if equal
+ * @param obj2 Object to consider equality of.
+ * @return boolean {@code true} if equal.
*/
@Override
public boolean equals(final Object obj2) {
@@ -157,9 +157,9 @@ public boolean equals(final Object obj2) {
}
/**
- * Gets the current number of values represented
+ * Gets the current number of values represented.
*
- * @return int number of values represented
+ * @return int number of values represented.
*/
int getCount() {
return count;
@@ -175,11 +175,9 @@ Object getValue() {
}
/**
- * Returns a hash code for the token equal to the
- * hash code for the token's value. Thus 'TT' and 'TTTT'
- * will have the same hash code.
+ * Returns a hash code for the token equal to the hash code for the token's value. Thus 'TT' and 'TTTT' will have the same hash code.
*
- * @return The hash code for the token
+ * @return The hash code for the token.
*/
@Override
public int hashCode() {
@@ -187,7 +185,7 @@ public int hashCode() {
}
/**
- * Adds another one of the value
+ * Adds another one of the value.
*/
void increment() {
count++;
@@ -196,7 +194,7 @@ void increment() {
/**
* Represents this token as a String.
*
- * @return String representation of the token
+ * @return String representation of the token.
*/
@Override
public String toString() {
@@ -211,8 +209,7 @@ public String toString() {
private static final int HOURS_PER_DAY = 24;
/**
- * Pattern used with {@link FastDateFormat} and {@link SimpleDateFormat}
- * for the ISO 8601 period format used in durations.
+ * Pattern used with {@link FastDateFormat} and {@link SimpleDateFormat} for the ISO 8601 period format used in durations.
*
* @see org.apache.commons.lang3.time.FastDateFormat
* @see java.text.SimpleDateFormat
@@ -236,16 +233,16 @@ public String toString() {
/**
* The internal method to do the formatting.
*
- * @param tokens the tokens
- * @param years the number of years
- * @param months the number of months
- * @param days the number of days
- * @param hours the number of hours
- * @param minutes the number of minutes
- * @param seconds the number of seconds
- * @param milliseconds the number of millis
- * @param padWithZeros whether to pad
- * @return the formatted string
+ * @param tokens the tokens.
+ * @param years the number of years.
+ * @param months the number of months.
+ * @param days the number of days.
+ * @param hours the number of hours.
+ * @param minutes the number of minutes.
+ * @param seconds the number of seconds.
+ * @param milliseconds the number of milliseconds.
+ * @param padWithZeros whether to pad.
+ * @return the formatted string.
*/
static String format(final Token[] tokens, final long years, final long months, final long days, final long hours, final long minutes,
final long seconds,
@@ -327,7 +324,7 @@ static String format(final Token[] tokens, final long years, final long months,
}
lastOutputSeconds = false;
}
- //as soon as we hit first nonliteral in optional, check for literal prefix
+ // as soon as we hit first nonliteral in optional, check for literal prefix
if (inOptional && !isLiteral && !firstOptionalNonLiteral) {
firstOptionalNonLiteral = true;
if (lastOutputZero) {
@@ -340,43 +337,39 @@ static String format(final Token[] tokens, final long years, final long months,
/**
* Formats the time gap as a string, using the specified format, and padding with zeros.
+ * <p>
+ * This method formats durations using the days and lower fields of the format pattern. Months and larger are not used.
+ * </p>
*
- * <p>This method formats durations using the days and lower fields of the
- * format pattern. Months and larger are not used.</p>
- *
- * @param durationMillis the duration to format
- * @param format the way in which to format the duration, not null
- * @return the formatted duration, not null
- * @throws IllegalArgumentException if durationMillis is negative
+ * @param durationMillis the duration to format.
+ * @param format the way in which to format the duration, not null.
+ * @return the formatted duration, not null.
+ * @throws IllegalArgumentException if durationMillis is negative.
*/
public static String formatDuration(final long durationMillis, final String format) {
return formatDuration(durationMillis, format, true);
}
/**
- * Formats the time gap as a string, using the specified format.
- * Padding the left-hand side side of numbers with zeroes is optional.
- *
- * <p>This method formats durations using the days and lower fields of the
- * format pattern. Months and larger are not used.</p>
+ * Formats the time gap as a string, using the specified format. Padding the left-hand side side of numbers with zeroes is optional.
+ * <p>
+ * This method formats durations using the days and lower fields of the format pattern. Months and larger are not used.
+ * </p>
*
- * @param durationMillis the duration to format
- * @param format the way in which to format the duration, not null
- * @param padWithZeros whether to pad the left-hand side side of numbers with 0's
- * @return the formatted duration, not null
- * @throws IllegalArgumentException if durationMillis is negative
+ * @param durationMillis the duration to format.
+ * @param format the way in which to format the duration, not null.
+ * @param padWithZeros whether to pad the left-hand side side of numbers with 0's.
+ * @return the formatted duration, not null.
+ * @throws IllegalArgumentException if durationMillis is negative.
*/
public static String formatDuration(final long durationMillis, final String format, final boolean padWithZeros) {
Validate.inclusiveBetween(0, Long.MAX_VALUE, durationMillis, "durationMillis must not be negative");
-
final Token[] tokens = lexx(format);
-
long days = 0;
long hours = 0;
long minutes = 0;
long seconds = 0;
long milliseconds = durationMillis;
-
if (Token.containsTokenWithValue(tokens, d)) {
days = milliseconds / DateUtils.MILLIS_PER_DAY;
milliseconds -= days * DateUtils.MILLIS_PER_DAY;
@@ -393,18 +386,18 @@ public static String formatDuration(final long durationMillis, final String form
seconds = milliseconds / DateUtils.MILLIS_PER_SECOND;
milliseconds -= seconds * DateUtils.MILLIS_PER_SECOND;
}
-
return format(tokens, 0, 0, days, hours, minutes, seconds, milliseconds, padWithZeros);
}
/**
* Formats the time gap as a string.
+ * <p>
+ * The format used is ISO 8601-like: {@code HH:mm:ss.SSS}.
+ * </p>
*
- * <p>The format used is ISO 8601-like: {@code HH:mm:ss.SSS}.</p>
- *
- * @param durationMillis the duration to format
- * @return the formatted duration, not null
- * @throws IllegalArgumentException if durationMillis is negative
+ * @param durationMillis the duration to format.
+ * @return the formatted duration, not null.
+ * @throws IllegalArgumentException if durationMillis is negative.
*/
public static String formatDurationHMS(final long durationMillis) {
return formatDuration(durationMillis, "HH:mm:ss.SSS");
@@ -412,15 +405,16 @@ public static String formatDurationHMS(final long durationMillis) {
/**
* Formats the time gap as a string.
+ * <p>
+ * The format used is the ISO 8601 period format.
+ * </p>
+ * <p>
+ * This method formats durations using the days and lower fields of the ISO format pattern, such as P7D6TH5M4.321S.
+ * </p>
*
- * <p>The format used is the ISO 8601 period format.</p>
- *
- * <p>This method formats durations using the days and lower fields of the
- * ISO format pattern, such as P7D6TH5M4.321S.</p>
- *
- * @param durationMillis the duration to format
- * @return the formatted duration, not null
- * @throws IllegalArgumentException if durationMillis is negative
+ * @param durationMillis the duration to format.
+ * @return the formatted duration, not null.
+ * @throws IllegalArgumentException if durationMillis is negative.
*/
public static String formatDurationISO(final long durationMillis) {
return formatDuration(durationMillis, ISO_EXTENDED_FORMAT_PATTERN, false);
@@ -428,21 +422,17 @@ public static String formatDurationISO(final long durationMillis) {
/**
* Formats an elapsed time into a pluralization correct string.
+ * <p>
+ * This method formats durations using the days and lower fields of the format pattern. Months and larger are not used.
+ * </p>
*
- * <p>This method formats durations using the days and lower fields of the
- * format pattern. Months and larger are not used.</p>
- *
- * @param durationMillis the elapsed time to report in milliseconds
- * @param suppressLeadingZeroElements suppresses leading 0 elements
- * @param suppressTrailingZeroElements suppresses trailing 0 elements
- * @return the formatted text in days/hours/minutes/seconds, not null
- * @throws IllegalArgumentException if durationMillis is negative
+ * @param durationMillis the elapsed time to report in milliseconds.
+ * @param suppressLeadingZeroElements suppresses leading 0 elements.
+ * @param suppressTrailingZeroElements suppresses trailing 0 elements.
+ * @return the formatted text in days/hours/minutes/seconds, not null.
+ * @throws IllegalArgumentException if durationMillis is negative.
*/
- public static String formatDurationWords(
- final long durationMillis,
- final boolean suppressLeadingZeroElements,
- final boolean suppressTrailingZeroElements) {
-
+ public static String formatDurationWords(final long durationMillis, final boolean suppressLeadingZeroElements, final boolean suppressTrailingZeroElements) {
// This method is generally replaceable by the format method, but
// there are a series of tweaks and special cases that require
// trickery to replicate.
@@ -500,62 +490,54 @@ public static String formatDurationWords(
}
/**
- * Formats the time gap as a string, using the specified format.
- * Padding the left-hand side side of numbers with zeroes is optional.
+ * Formats the time gap as a string, using the specified format. Padding the left-hand side side of numbers with zeroes is optional.
*
- * @param startMillis the start of the duration
- * @param endMillis the end of the duration
- * @param format the way in which to format the duration, not null
- * @return the formatted duration, not null
- * @throws IllegalArgumentException if startMillis is greater than endMillis
+ * @param startMillis the start of the duration.
+ * @param endMillis the end of the duration.
+ * @param format the way in which to format the duration, not null.
+ * @return the formatted duration, not null.
+ * @throws IllegalArgumentException if startMillis is greater than endMillis.
*/
public static String formatPeriod(final long startMillis, final long endMillis, final String format) {
return formatPeriod(startMillis, endMillis, format, true, TimeZone.getDefault());
}
/**
- * <p>Formats the time gap as a string, using the specified format.
- * Padding the left-hand side side of numbers with zeroes is optional and
- * the time zone may be specified.
- *
- * <p>When calculating the difference between months/days, it chooses to
- * calculate months first. So when working out the number of months and
- * days between January 15th and March 10th, it choose 1 month and
- * 23 days gained by choosing January->February = 1 month and then
- * calculating days forwards, and not the 1 month and 26 days gained by
- * choosing March -> February = 1 month and then calculating days
- * backwards.</p>
- *
- * <p>For more control, the <a href="https://www.joda.org/joda-time/">Joda-Time</a>
- * library is recommended.</p>
+ * <p>
+ * Formats the time gap as a string, using the specified format. Padding the left-hand side side of numbers with zeroes is optional and the time zone may be
+ * specified.
+ * <p>
+ * When calculating the difference between months/days, it chooses to calculate months first. So when working out the number of months and days between
+ * January 15th and March 10th, it choose 1 month and 23 days gained by choosing January->February = 1 month and then calculating days forwards, and not
+ * the 1 month and 26 days gained by choosing March -> February = 1 month and then calculating days backwards.
+ * </p>
+ * <p>
+ * For more control, the <a href="https://www.joda.org/joda-time/">Joda-Time</a> library is recommended.
+ * </p>
*
- * @param startMillis the start of the duration
- * @param endMillis the end of the duration
- * @param format the way in which to format the duration, not null
- * @param padWithZeros whether to pad the left-hand side side of numbers with 0's
- * @param timezone the millis are defined in
- * @return the formatted duration, not null
- * @throws IllegalArgumentException if startMillis is greater than endMillis
+ * @param startMillis the start of the duration.
+ * @param endMillis the end of the duration.
+ * @param format the way in which to format the duration, not null.
+ * @param padWithZeros whether to pad the left-hand side side of numbers with 0's.
+ * @param timezone the millis are defined in.
+ * @return the formatted duration, not null.
+ * @throws IllegalArgumentException if startMillis is greater than endMillis.
*/
public static String formatPeriod(final long startMillis, final long endMillis, final String format, final boolean padWithZeros,
final TimeZone timezone) {
Validate.isTrue(startMillis <= endMillis, "startMillis must not be greater than endMillis");
-
// Used to optimize for differences under 28 days and
// called formatDuration(millis, format); however this did not work
// over leap years.
// TODO: Compare performance to see if anything was lost by
// losing this optimization.
-
final Token[] tokens = lexx(format);
-
// time zones get funky around 0, so normalizing everything to GMT
// stops the hours being off
final Calendar start = Calendar.getInstance(timezone);
start.setTime(new Date(startMillis));
final Calendar end = Calendar.getInstance(timezone);
end.setTime(new Date(endMillis));
-
// initial estimates
long milliseconds = end.get(Calendar.MILLISECOND) - start.get(Calendar.MILLISECOND);
int seconds = end.get(Calendar.SECOND) - start.get(Calendar.SECOND);
@@ -564,7 +546,6 @@ public static String formatPeriod(final long startMillis, final long endMillis,
int days = end.get(Calendar.DAY_OF_MONTH) - start.get(Calendar.DAY_OF_MONTH);
int months = end.get(Calendar.MONTH) - start.get(Calendar.MONTH);
int years = end.get(Calendar.YEAR) - start.get(Calendar.YEAR);
-
// each initial estimate is adjusted in case it is under 0
while (milliseconds < 0) {
milliseconds += DateUtils.MILLIS_PER_SECOND;
@@ -582,19 +563,16 @@ public static String formatPeriod(final long startMillis, final long endMillis,
hours += HOURS_PER_DAY;
days -= 1;
}
-
if (Token.containsTokenWithValue(tokens, M)) {
while (days < 0) {
days += start.getActualMaximum(Calendar.DAY_OF_MONTH);
months -= 1;
start.add(Calendar.MONTH, 1);
}
-
while (months < 0) {
months += 12;
years -= 1;
}
-
if (!Token.containsTokenWithValue(tokens, y) && years != 0) {
while (years != 0) {
months += 12 * years;
@@ -603,51 +581,37 @@ public static String formatPeriod(final long startMillis, final long endMillis,
}
} else {
// there are no M's in the format string
-
if (!Token.containsTokenWithValue(tokens, y)) {
int target = end.get(Calendar.YEAR);
if (months < 0) {
// target is end-year -1
target -= 1;
}
-
while (start.get(Calendar.YEAR) != target) {
days += start.getActualMaximum(Calendar.DAY_OF_YEAR) - start.get(Calendar.DAY_OF_YEAR);
-
// Not sure I grok why this is needed, but the brutal tests show it is
- if (start instanceof GregorianCalendar &&
- start.get(Calendar.MONTH) == Calendar.FEBRUARY &&
- start.get(Calendar.DAY_OF_MONTH) == 29) {
+ if (start instanceof GregorianCalendar && start.get(Calendar.MONTH) == Calendar.FEBRUARY && start.get(Calendar.DAY_OF_MONTH) == 29) {
days += 1;
}
-
start.add(Calendar.YEAR, 1);
-
days += start.get(Calendar.DAY_OF_YEAR);
}
-
years = 0;
}
-
while (start.get(Calendar.MONTH) != end.get(Calendar.MONTH)) {
days += start.getActualMaximum(Calendar.DAY_OF_MONTH);
start.add(Calendar.MONTH, 1);
}
-
months = 0;
-
while (days < 0) {
days += start.getActualMaximum(Calendar.DAY_OF_MONTH);
months -= 1;
start.add(Calendar.MONTH, 1);
}
-
}
-
// The rest of this code adds in values that
// aren't requested. This allows the user to ask for the
// number of months and get the real count and not just 0->11.
-
if (!Token.containsTokenWithValue(tokens, d)) {
hours += HOURS_PER_DAY * days;
days = 0;
@@ -664,19 +628,19 @@ public static String formatPeriod(final long startMillis, final long endMillis,
milliseconds += DateUtils.MILLIS_PER_SECOND * seconds;
seconds = 0;
}
-
return format(tokens, years, months, days, hours, minutes, seconds, milliseconds, padWithZeros);
}
/**
* Formats the time gap as a string.
+ * <p>
+ * The format used is the ISO 8601 period format.
+ * </p>
*
- * <p>The format used is the ISO 8601 period format.</p>
- *
- * @param startMillis the start of the duration to format
- * @param endMillis the end of the duration to format
- * @return the formatted duration, not null
- * @throws IllegalArgumentException if startMillis is greater than endMillis
+ * @param startMillis the start of the duration to format.
+ * @param endMillis the end of the duration to format.
+ * @return the formatted duration, not null.
+ * @throws IllegalArgumentException if startMillis is greater than endMillis.
*/
public static String formatPeriodISO(final long startMillis, final long endMillis) {
return formatPeriod(startMillis, endMillis, ISO_EXTENDED_FORMAT_PATTERN, false, TimeZone.getDefault());
@@ -685,12 +649,11 @@ public static String formatPeriodISO(final long startMillis, final long endMilli
/**
* Parses a classic date format string into Tokens
*
- * @param format the format to parse, not null
- * @return array of Token[]
+ * @param format the format to parse, not null.
+ * @return array of Token[].
*/
static Token[] lexx(final String format) {
final ArrayList<Token> list = new ArrayList<>(format.length());
-
boolean inLiteral = false;
// Although the buffer is stored in a Token, the Tokens are only
// used internally, so cannot be accessed by other threads
@@ -758,7 +721,6 @@ static Token[] lexx(final String format) {
}
buffer.append(ch);
}
-
if (value != null) {
if (previous != null && previous.getValue().equals(value)) {
previous.increment();
@@ -780,13 +742,12 @@ static Token[] lexx(final String format) {
}
/**
- * Converts a {@code long} to a {@link String} with optional
- * zero padding.
+ * Converts a {@code long} to a {@link String} with optional zero padding.
*
- * @param value the value to convert
- * @param padWithZeros whether to pad with zeroes
- * @param count the size to pad to (ignored if {@code padWithZeros} is false)
- * @return the string result
+ * @param value the value to convert.
+ * @param padWithZeros whether to pad with zeroes.
+ * @param count the size to pad to (ignored if {@code padWithZeros} is false).
+ * @return the string result.
*/
private static String paddedValue(final long value, final boolean padWithZeros, final int count) {
final String longString = Long.toString(value);
@@ -795,9 +756,9 @@ private static String paddedValue(final long value, final boolean padWithZeros,
/**
* DurationFormatUtils instances should NOT be constructed in standard programming.
- *
- * <p>This constructor is public to permit tools that require a JavaBean instance
- * to operate.</p>
+ * <p>
+ * This constructor is public to permit tools that require a JavaBean instance to operate.
+ * </p>
*
* @deprecated TODO Make private in 4.0.
*/