diff --git a/src/main/java/org/threeten/extra/AmountFormats.java b/src/main/java/org/threeten/extra/AmountFormats.java
index facbf480..5912701c 100644
--- a/src/main/java/org/threeten/extra/AmountFormats.java
+++ b/src/main/java/org/threeten/extra/AmountFormats.java
@@ -35,15 +35,11 @@
import java.time.Period;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAmount;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.ResourceBundle;
+import java.util.*;
import java.util.function.Function;
import java.util.function.IntPredicate;
import java.util.regex.Pattern;
+import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
@@ -96,35 +92,35 @@ public final class AmountFormats {
/**
* The property file key for the word "year".
*/
- private static final String WORDBASED_YEAR = "WordBased.year";
+ public static final String WORDBASED_YEAR = "WordBased.year";
/**
* The property file key for the word "month".
*/
- private static final String WORDBASED_MONTH = "WordBased.month";
+ public static final String WORDBASED_MONTH = "WordBased.month";
/**
* The property file key for the word "week".
*/
- private static final String WORDBASED_WEEK = "WordBased.week";
+ public static final String WORDBASED_WEEK = "WordBased.week";
/**
* The property file key for the word "day".
*/
- private static final String WORDBASED_DAY = "WordBased.day";
+ public static final String WORDBASED_DAY = "WordBased.day";
/**
* The property file key for the word "hour".
*/
- private static final String WORDBASED_HOUR = "WordBased.hour";
+ public static final String WORDBASED_HOUR = "WordBased.hour";
/**
* The property file key for the word "minute".
*/
- private static final String WORDBASED_MINUTE = "WordBased.minute";
+ public static final String WORDBASED_MINUTE = "WordBased.minute";
/**
* The property file key for the word "second".
*/
- private static final String WORDBASED_SECOND = "WordBased.second";
+ public static final String WORDBASED_SECOND = "WordBased.second";
/**
* The property file key for the word "millisecond".
*/
- private static final String WORDBASED_MILLISECOND = "WordBased.millisecond";
+ public static final String WORDBASED_MILLISECOND = "WordBased.millisecond";
/**
* The predicate that matches 1 or -1.
*/
@@ -303,6 +299,69 @@ public static String wordBased(Period period, Duration duration, Locale locale)
return wb.format(values);
}
+ /**
+ * Formats a period and duration to a string in a localized word-based format using the provided list of formats.
+ *
+ * This returns a word-based format for the period and duration and only includes the provided formats.
+ * The year and month are printed as supplied unless the signs differ, in which case they are normalized.
+ * The words are configured in a resource bundle text file -
+ * {@code org.threeten.extra.wordbased.properties} - with overrides per language.
+ *
+ * @param period the period to format
+ * @param duration the duration to format
+ * @param formats a list of formats to include in the result
+ * @param locale the locale to use
+ * @return the localized word-based format for the period and duration
+ */
+ public static String wordBasedWithFormats(Period period, Duration duration, List formats, Locale locale) {
+ Objects.requireNonNull(period, "period must not be null");
+ Objects.requireNonNull(duration, "duration must not be null");
+ Objects.requireNonNull(formats, "formats must not be null");
+ Objects.requireNonNull(locale, "locale must not be null");
+ ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME, locale);
+
+ WordBased wb = new WordBased(formats.stream().map(f -> UnitFormat.of(bundle, f)).toArray(UnitFormat[]::new), bundle.getString(WORDBASED_COMMASPACE), bundle.getString(WORDBASED_SPACEANDSPACE));
+
+ Period normPeriod = oppositeSigns(period.getMonths(), period.getYears()) ? period.normalized() : period;
+ int weeks = 0;
+ int days = 0;
+ if (normPeriod.getDays() % DAYS_PER_WEEK == 0) {
+ weeks = normPeriod.getDays() / DAYS_PER_WEEK;
+ } else {
+ days = normPeriod.getDays();
+ }
+ long totalHours = duration.toHours();
+ days += (int) (totalHours / HOURS_PER_DAY);
+
+ List values = new ArrayList<>();
+ if (formats.contains(WORDBASED_YEAR)) {
+ values.add(normPeriod.getYears());
+ }
+ if (formats.contains(WORDBASED_MONTH)) {
+ values.add(normPeriod.getMonths());
+ }
+ if (formats.contains(WORDBASED_WEEK)) {
+ values.add(weeks);
+ }
+ if (formats.contains(WORDBASED_DAY)) {
+ values.add(days);
+ }
+ if (formats.contains(WORDBASED_HOUR)) {
+ values.add((int) (totalHours % HOURS_PER_DAY));
+ }
+ if (formats.contains(WORDBASED_MINUTE)) {
+ values.add((int) (duration.toMinutes() % MINUTES_PER_HOUR));
+ }
+ if (formats.contains(WORDBASED_SECOND)) {
+ values.add((int) (duration.getSeconds() % SECONDS_PER_MINUTE));
+ }
+ if (formats.contains(WORDBASED_MILLISECOND)) {
+ values.add(duration.getNano() / NANOS_PER_MILLIS);
+ }
+
+ return wb.format(values.stream().mapToInt(Integer::intValue).toArray());
+ }
+
// are the signs opposite
private static boolean oppositeSigns(int a, int b) {
return a < 0 ? (b >= 0) : (b < 0);
diff --git a/src/test/java/org/threeten/extra/TestAmountFormats.java b/src/test/java/org/threeten/extra/TestAmountFormats.java
index a91e1e26..74e2135d 100644
--- a/src/test/java/org/threeten/extra/TestAmountFormats.java
+++ b/src/test/java/org/threeten/extra/TestAmountFormats.java
@@ -37,6 +37,8 @@
import java.time.Duration;
import java.time.Period;
import java.time.format.DateTimeParseException;
+import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
import org.junit.jupiter.api.Test;
@@ -157,12 +159,38 @@ public static Object[][] period_duration_wordBased() {
};
}
+ public static Object[][] period_duration_wordBasedWithFormats() {
+ return new Object[][] {
+ {Period.ofDays(1), Duration.ofMinutes(180 + 2), Arrays.asList(AmountFormats.WORDBASED_DAY, AmountFormats.WORDBASED_HOUR), Locale.ROOT, "1 day and 3 hours"},
+ {Period.ofDays(2), Duration.ofSeconds(180), Arrays.asList(AmountFormats.WORDBASED_DAY, AmountFormats.WORDBASED_MINUTE), Locale.ROOT, "2 days and 3 minutes"},
+ {Period.ofDays(7), Duration.ofMinutes(80), Arrays.asList(AmountFormats.WORDBASED_WEEK, AmountFormats.WORDBASED_MINUTE), Locale.ROOT, "1 week and 20 minutes"},
+ {Period.ZERO, Duration.ofMillis(1_000), Arrays.asList(AmountFormats.WORDBASED_SECOND), Locale.ROOT, "1 second"},
+
+ {Period.ofMonths(0), Duration.ofSeconds(0), Arrays.asList(AmountFormats.WORDBASED_MILLISECOND), Locale.ENGLISH, "0 milliseconds"},
+ {Period.ofMonths(0), Duration.ofHours(9), Arrays.asList(AmountFormats.WORDBASED_HOUR), Locale.ENGLISH, "9 hours"},
+ {Period.ofMonths(1), Duration.ZERO, Arrays.asList(AmountFormats.WORDBASED_MONTH), Locale.ENGLISH, "1 month"},
+ {Period.ofMonths(4), Duration.ZERO, Arrays.asList(AmountFormats.WORDBASED_MONTH), Locale.ENGLISH, "4 months"},
+ {Period.of(1, 2, 5), Duration.ofHours(4),Arrays.asList(AmountFormats.WORDBASED_YEAR, AmountFormats.WORDBASED_MONTH, AmountFormats.WORDBASED_DAY, AmountFormats.WORDBASED_HOUR), Locale.ENGLISH, "1 year, 2 months, 5 days and 4 hours"},
+ {Period.ofDays(5), Duration.ofDays(2).plusHours(6), Arrays.asList(AmountFormats.WORDBASED_DAY, AmountFormats.WORDBASED_HOUR), Locale.ENGLISH, "7 days and 6 hours"},
+ {Period.ofDays(5), Duration.ofDays(-2).plusHours(-6), Arrays.asList(AmountFormats.WORDBASED_DAY, AmountFormats.WORDBASED_HOUR), Locale.ENGLISH, "3 days and -6 hours"},
+
+ {Period.ofDays(1), Duration.ofHours(5).plusMinutes(6).plusSeconds(7).plusNanos(8_000_000L), Arrays.asList(AmountFormats.WORDBASED_DAY, AmountFormats.WORDBASED_HOUR, AmountFormats.WORDBASED_MINUTE, AmountFormats.WORDBASED_SECOND, AmountFormats.WORDBASED_MILLISECOND), PL,
+ "1 dzie\u0144, 5 godzin, 6 minut, 7 sekund i 8 milisekund"},
+ };
+ }
+
@ParameterizedTest
@MethodSource("period_duration_wordBased")
public void test_wordBased(Period period, Duration duration, Locale locale, String expected) {
assertEquals(expected, AmountFormats.wordBased(period, duration, locale));
}
+ @ParameterizedTest
+ @MethodSource("period_duration_wordBasedWithFormats")
+ public void test_wordBasedWithFormats(Period period, Duration duration, List formats, Locale locale, String expected) {
+ assertEquals(expected, AmountFormats.wordBasedWithFormats(period, duration, formats, locale));
+ }
+
//-----------------------------------------------------------------------
@Test
public void test_wordBased_pl_formatStandard() {