From 6d2cef028a0b5147ac9535e19a5b2cde8fbfa327 Mon Sep 17 00:00:00 2001 From: "petar.tahchiev" Date: Wed, 14 Jun 2023 12:42:19 +0300 Subject: [PATCH] Add a new method --- .../org/threeten/extra/AmountFormats.java | 87 ++++++++++++++++--- .../org/threeten/extra/TestAmountFormats.java | 28 ++++++ 2 files changed, 101 insertions(+), 14 deletions(-) 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() {