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 e547d704b21a1b10a2dae0158f7288d7afa4d064 Author: Gary Gregory <[email protected]> AuthorDate: Tue Nov 3 08:33:07 2026 -0500 Sort members. --- .../apache/commons/lang3/time/FastDateParser.java | 14 +- .../org/apache/commons/lang3/LocaleUtilsTest.java | 30 ++--- .../lang3/concurrent/TimedSemaphoreTest.java | 50 ++++---- .../lang3/exception/ExceptionUtilsTest.java | 52 ++++---- .../commons/lang3/reflect/TypeUtilsTest.java | 44 +++---- .../lang3/text/translate/UnicodeEscaperTest.java | 20 +-- .../apache/commons/lang3/time/DateUtilsTest.java | 142 ++++++++++----------- .../commons/lang3/time/FastDateParserTest.java | 44 +++---- 8 files changed, 198 insertions(+), 198 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/time/FastDateParser.java b/src/main/java/org/apache/commons/lang3/time/FastDateParser.java index 7eb294528..087165eaf 100644 --- a/src/main/java/org/apache/commons/lang3/time/FastDateParser.java +++ b/src/main/java/org/apache/commons/lang3/time/FastDateParser.java @@ -547,6 +547,13 @@ public String toString() { */ private static final ConcurrentMap<Locale, Strategy> ISO_EXTENDED_ZONE_CACHE = new ConcurrentHashMap<>(3); + private static final String GMT_OPTION = TimeZones.GMT_ID + "[+-]\\d{1,2}:\\d{2}"; + + /** + * Index of zone id from {@link DateFormatSymbols#getZoneStrings()}. + */ + private static final int ID = 0; + static void clearIsoExtendedCache() { ISO_EXTENDED_ZONE_CACHE.clear(); } @@ -561,13 +568,6 @@ static Strategy getIsoExtendedStrategy(final Locale locale) { return ISO_EXTENDED_ZONE_CACHE.computeIfAbsent(LocaleUtils.toLocale(locale), k -> new TimeZoneStrategy(k, true)); } - private static final String GMT_OPTION = TimeZones.GMT_ID + "[+-]\\d{1,2}:\\d{2}"; - - /** - * Index of zone id from {@link DateFormatSymbols#getZoneStrings()}. - */ - private static final int ID = 0; - /** * Tests whether to skip the given time zone, true if TimeZone.getTimeZone(). * <p> diff --git a/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java b/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java index 71d3d21c1..7b5f98c8b 100644 --- a/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java @@ -540,21 +540,6 @@ void testToLocale_2Part() { assertIllegalArgumentException(() -> LocaleUtils.toLocale("us_E3"), "Should fail second part not uppercase"); } - /** - * Tests that language, country and area codes are restricted to ASCII, as {@link Locale} defines them. - */ - @ParameterizedTest - @ValueSource(strings = { - // FULLWIDTH LATIN SMALL LETTER E and N - "en", "éé", "ééé", "eé", "éé_GB", "éé-GB", "éé_GB_POSIX", - // FULLWIDTH LATIN CAPITAL LETTER G and B - "en_GB", "en_ÉÉ", "en-ÉÉ", "en_ÉÉ_POSIX", "_ÉÉ", "_ÉÉ_POSIX", "-ÉÉ", "_GÉ", "_ÉB", - // ARABIC-INDIC DIGIT ONE, TWO and THREE - "en_١٢٣", "en-١٢٣", "en_1٢3", "en_١٢٣_POSIX" }) - void testToLocaleNonAsciiCodes(final String localeString) { - assertIllegalArgumentException(() -> LocaleUtils.toLocale(localeString), "Should fail code not ASCII: " + localeString); - } - /** * Test toLocale() method. */ @@ -619,6 +604,21 @@ void testToLocaleGetIso3LanguageKown(final String country) { assertEquals(LocaleUtils.ofCountry(country).getDisplayCountry(), LocaleUtils.toLocale(country).getDisplayCountry()); } + /** + * Tests that language, country and area codes are restricted to ASCII, as {@link Locale} defines them. + */ + @ParameterizedTest + @ValueSource(strings = { + // FULLWIDTH LATIN SMALL LETTER E and N + "en", "éé", "ééé", "eé", "éé_GB", "éé-GB", "éé_GB_POSIX", + // FULLWIDTH LATIN CAPITAL LETTER G and B + "en_GB", "en_ÉÉ", "en-ÉÉ", "en_ÉÉ_POSIX", "_ÉÉ", "_ÉÉ_POSIX", "-ÉÉ", "_GÉ", "_ÉB", + // ARABIC-INDIC DIGIT ONE, TWO and THREE + "en_١٢٣", "en-١٢٣", "en_1٢3", "en_١٢٣_POSIX" }) + void testToLocaleNonAsciiCodes(final String localeString) { + assertIllegalArgumentException(() -> LocaleUtils.toLocale(localeString), "Should fail code not ASCII: " + localeString); + } + /** * Test toLocale(Locale) method. */ diff --git a/src/test/java/org/apache/commons/lang3/concurrent/TimedSemaphoreTest.java b/src/test/java/org/apache/commons/lang3/concurrent/TimedSemaphoreTest.java index 88516ab91..520d737c2 100644 --- a/src/test/java/org/apache/commons/lang3/concurrent/TimedSemaphoreTest.java +++ b/src/test/java/org/apache/commons/lang3/concurrent/TimedSemaphoreTest.java @@ -46,6 +46,22 @@ */ class TimedSemaphoreTest extends AbstractLangTest { + private static final class EqualFairAcquirer extends FairAcquirer { + EqualFairAcquirer(final TimedSemaphore semaphore, final BlockingQueue<Integer> order, final int id) { + super(semaphore, order, id); + } + + @Override + public boolean equals(final Object other) { + return other instanceof EqualFairAcquirer; + } + + @Override + public int hashCode() { + return 1; + } + } + private static class FairAcquirer extends Thread { private final TimedSemaphore semaphore; private final BlockingQueue<Integer> order; @@ -70,22 +86,6 @@ public void run() { } } - private static final class EqualFairAcquirer extends FairAcquirer { - EqualFairAcquirer(final TimedSemaphore semaphore, final BlockingQueue<Integer> order, final int id) { - super(semaphore, order, id); - } - - @Override - public boolean equals(final Object other) { - return other instanceof EqualFairAcquirer; - } - - @Override - public int hashCode() { - return 1; - } - } - /** * A test thread class that will be used by tests for triggering the * semaphore. The thread calls the semaphore a configurable number of times. @@ -289,6 +289,15 @@ private static TimedSemaphore newFairSemaphore(final int limit) { return TimedSemaphore.builder().setPeriod(1).setTimeUnit(TimeUnit.DAYS).setLimit(limit).setFair(true).get(); } + private static void stopAcquirers(final TimedSemaphore semaphore, final FairAcquirer... threads) throws InterruptedException { + semaphore.shutdown(); + for (final FairAcquirer thread : threads) { + thread.interrupt(); + thread.join(5000); + assertFalse(thread.isAlive(), "Acquirer did not terminate"); + } + } + /** * Prepares an executor service mock to expect the start of the timer. * @@ -301,15 +310,6 @@ private void prepareStartTimer(final ScheduledExecutorService service, EasyMock.expectLastCall().andReturn(future); } - private static void stopAcquirers(final TimedSemaphore semaphore, final FairAcquirer... threads) throws InterruptedException { - semaphore.shutdown(); - for (final FairAcquirer thread : threads) { - thread.interrupt(); - thread.join(5000); - assertFalse(thread.isAlive(), "Acquirer did not terminate"); - } - } - /** * Tests the acquire() method if a limit is set. * diff --git a/src/test/java/org/apache/commons/lang3/exception/ExceptionUtilsTest.java b/src/test/java/org/apache/commons/lang3/exception/ExceptionUtilsTest.java index 4a210a708..a93e22f68 100644 --- a/src/test/java/org/apache/commons/lang3/exception/ExceptionUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/exception/ExceptionUtilsTest.java @@ -412,6 +412,32 @@ void testGetRootCauseStackTrace_Throwable() { assertFalse(match); } + @Test + void testGetRootCauseStackTraceList_Throwable() { + assertEquals(0, ExceptionUtils.getRootCauseStackTraceList(null).size()); + + final Throwable cause = createExceptionWithCause(); + List<String> stackTrace = ExceptionUtils.getRootCauseStackTraceList(cause); + boolean match = false; + for (final String element : stackTrace) { + if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) { + match = true; + break; + } + } + assertTrue(match); + + stackTrace = ExceptionUtils.getRootCauseStackTraceList(withoutCause); + match = false; + for (final String element : stackTrace) { + if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) { + match = true; + break; + } + } + assertFalse(match); + } + @Test void testGetRootCauseStackTraceListComparesElements() { final StackTraceElement causeFrame = new StackTraceElement("Type", "call", "Type.java", -1); @@ -475,32 +501,6 @@ void testGetRootCauseStackTraceListSharedSuffix() { assertEquals(3, wrapper.getStackTrace().length); } - @Test - void testGetRootCauseStackTraceList_Throwable() { - assertEquals(0, ExceptionUtils.getRootCauseStackTraceList(null).size()); - - final Throwable cause = createExceptionWithCause(); - List<String> stackTrace = ExceptionUtils.getRootCauseStackTraceList(cause); - boolean match = false; - for (final String element : stackTrace) { - if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) { - match = true; - break; - } - } - assertTrue(match); - - stackTrace = ExceptionUtils.getRootCauseStackTraceList(withoutCause); - match = false; - for (final String element : stackTrace) { - if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) { - match = true; - break; - } - } - assertFalse(match); - } - /** * Tests that ordinary message lines whose first non-whitespace characters happen to be "at" are no longer mistaken for stack * frames, so a multi-line untrusted message can neither inject a non-frame-shaped line into the frame list nor suppress the diff --git a/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java b/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java index 0acb81b58..f98012483 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java @@ -1221,6 +1221,28 @@ void testGetTypeArgumentsInheritedWildcardBounds() { assertArrayEquals(new Type[] { variable }, inheritedLower.getLowerBounds()); } + @ParameterizedTest + @MethodSource + void testHashCodeMatchesJdk(final String fieldName, final Type built) throws NoSuchFieldException { + final Type jdk = HashCodeTypes.class.getDeclaredField(fieldName).getGenericType(); + assertEquals(jdk, built); + assertEquals(built, jdk); + + final Map<Type, String> jdkMap = new HashMap<>(); + jdkMap.put(jdk, "value"); + final Map<Type, String> builtMap = new HashMap<>(); + builtMap.put(built, "value"); + final Set<Type> jdkSet = new HashSet<>(); + jdkSet.add(jdk); + final Set<Type> builtSet = new HashSet<>(); + builtSet.add(built); + assertEquals(jdk.hashCode(), built.hashCode()); + assertEquals("value", jdkMap.get(built)); + assertEquals("value", builtMap.get(jdk)); + assertTrue(jdkSet.contains(built)); + assertTrue(builtSet.contains(jdk)); + } + @Test void testInterfaceBoundPreserved() { assertEquals("T extends java.util.List<java.lang.String>", @@ -1262,28 +1284,6 @@ void testIsArrayGenericTypes() throws NoSuchFieldException { assertTrue(TypeUtils.isArrayType(superStringListTypeArray)); } - @ParameterizedTest - @MethodSource - void testHashCodeMatchesJdk(final String fieldName, final Type built) throws NoSuchFieldException { - final Type jdk = HashCodeTypes.class.getDeclaredField(fieldName).getGenericType(); - assertEquals(jdk, built); - assertEquals(built, jdk); - - final Map<Type, String> jdkMap = new HashMap<>(); - jdkMap.put(jdk, "value"); - final Map<Type, String> builtMap = new HashMap<>(); - builtMap.put(built, "value"); - final Set<Type> jdkSet = new HashSet<>(); - jdkSet.add(jdk); - final Set<Type> builtSet = new HashSet<>(); - builtSet.add(built); - assertEquals(jdk.hashCode(), built.hashCode()); - assertEquals("value", jdkMap.get(built)); - assertEquals("value", builtMap.get(jdk)); - assertTrue(jdkSet.contains(built)); - assertTrue(builtSet.contains(jdk)); - } - @Test void testIsArrayTypeClasses() { assertTrue(TypeUtils.isArrayType(boolean[].class)); diff --git a/src/test/java/org/apache/commons/lang3/text/translate/UnicodeEscaperTest.java b/src/test/java/org/apache/commons/lang3/text/translate/UnicodeEscaperTest.java index 523addb2d..9a417f5e9 100644 --- a/src/test/java/org/apache/commons/lang3/text/translate/UnicodeEscaperTest.java +++ b/src/test/java/org/apache/commons/lang3/text/translate/UnicodeEscaperTest.java @@ -52,6 +52,16 @@ void testBetween() { assertEquals("AD\\u0046\\u0047Z", result, "Failed to escape Unicode characters via the between method"); } + @Test + void testSupplementary() { + final UnicodeEscaper ue = UnicodeEscaper.above(0x7f); + // U+10437 (DESERET SMALL LETTER YEE) encodes to surrogate pair U+D801 U+DC37. + final String input = new String(Character.toChars(0x10437)); + final String result = ue.translate(input); + assertEquals("\\uD801\\uDC37", result, "Supplementary code point must escape to a surrogate pair"); + assertEquals(input, new UnicodeUnescaper().translate(result), "Escaped supplementary code point must round-trip"); + } + @Test void testToUtf16EscapeBmp() { final UnicodeEscaper ue = new UnicodeEscaper(); @@ -64,14 +74,4 @@ void testToUtf16EscapeBmp() { assertEquals(ue.translate("A"), ue.toUtf16Escape('A'), "toUtf16Escape must match the translate output"); assertEquals("A", new UnicodeUnescaper().translate(ue.toUtf16Escape('A')), "Escaped BMP code point must round-trip"); } - - @Test - void testSupplementary() { - final UnicodeEscaper ue = UnicodeEscaper.above(0x7f); - // U+10437 (DESERET SMALL LETTER YEE) encodes to surrogate pair U+D801 U+DC37. - final String input = new String(Character.toChars(0x10437)); - final String result = ue.translate(input); - assertEquals("\\uD801\\uDC37", result, "Supplementary code point must escape to a surrogate pair"); - assertEquals(input, new UnicodeUnescaper().translate(result), "Escaped supplementary code point must round-trip"); - } } diff --git a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java index 5cc4434fc..53feea519 100644 --- a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java @@ -767,6 +767,77 @@ void testCeiling_MET() throws Exception { "ceiling MET date across DST change-over"); } + /** + * Tests that clearing sub-hour fields does not spuriously select the later occurrence of an overlapping local time. + * + * @param milliseconds the millisecond value below, at, or above the rounding threshold. + */ + @DefaultTimeZone("Europe/London") + @ParameterizedTest + @ValueSource(ints = { 245, 499, 500, 999 }) + void testCeilingAndRoundDstOverlap(final int milliseconds) { + // 2024-10-27 01:00-02:00 BST repeats as 01:00-02:00 GMT; ZonedDateTime.of() picks the earlier offset, BST. + final ZonedDateTime bst = ZonedDateTime.of(2024, 10, 27, 1, 8, 58, milliseconds * 1_000_000, ZoneId.of("Europe/London")); + assertEquals(ZoneOffset.ofHours(1), bst.getOffset()); + final ZonedDateTime gmt = bst.withLaterOffsetAtOverlap(); + assertEquals(ZoneOffset.UTC, gmt.getOffset()); + for (final ZonedDateTime zdt : new ZonedDateTime[] { bst, gmt }) { + final Date date = Date.from(zdt.toInstant()); + final ZonedDateTime second = zdt.truncatedTo(ChronoUnit.SECONDS); + final ZonedDateTime minute = zdt.truncatedTo(ChronoUnit.MINUTES); + final ZonedDateTime hour = zdt.truncatedTo(ChronoUnit.HOURS); + assertEquals(Date.from(zdt.truncatedTo(ChronoUnit.SECONDS).plusSeconds(1).toInstant()), DateUtils.ceiling(date, Calendar.SECOND), "Ceiling Calendar.SECOND " + zdt); + assertEquals(Date.from(second.plusSeconds(milliseconds >= 500 ? 1 : 0).toInstant()), DateUtils.round(date, Calendar.SECOND), + "Round Calendar.SECOND " + zdt); + assertEquals(Date.from(minute.plusMinutes(1).toInstant()), DateUtils.ceiling(date, Calendar.MINUTE), "Ceiling Calendar.MINUTE " + zdt); + assertEquals(Date.from(minute.plusMinutes(1).toInstant()), DateUtils.round(date, Calendar.MINUTE), "Round Calendar.MINUTE " + zdt); + assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.ceiling(date, Calendar.HOUR_OF_DAY), "Ceiling Calendar.HOUR_OF_DAY " + zdt); + assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.ceiling(date, Calendar.HOUR), "Ceiling Calendar.HOUR " + zdt); + assertEquals(Date.from(hour.toInstant()), DateUtils.round(date, Calendar.HOUR_OF_DAY), "Round Calendar.HOUR_OF_DAY " + zdt); + // 38 minutes past rounds up to the next hour + final Date late = Date.from(zdt.withMinute(38).toInstant()); + assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.round(late, Calendar.HOUR_OF_DAY), "Round up Calendar.HOUR_OF_DAY " + zdt); + assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.round(late, Calendar.HOUR), "Round up Calendar.HOUR " + zdt); + } + } + + /** + * Tests that clearing sub-hour fields does not spuriously select the later occurrence of an overlapping local time. + * + * @param milliseconds the millisecond value below, at, or above the rounding threshold. + */ + @DefaultTimeZone("UTC") + @ParameterizedTest + @ValueSource(ints = { 245, 499, 500, 999 }) + void testCeilingAndRoundDstOverlapCalendar(final int milliseconds) { + // 2024-10-27 01:00-02:00 BST repeats as 01:00-02:00 GMT; ZonedDateTime.of() picks the earlier offset, BST. + final ZonedDateTime bst = ZonedDateTime.of(2024, 10, 27, 1, 8, 58, milliseconds * 1_000_000, ZoneId.of("Europe/London")); + assertEquals(ZoneOffset.ofHours(1), bst.getOffset()); + final ZonedDateTime gmt = bst.withLaterOffsetAtOverlap(); + assertEquals(ZoneOffset.UTC, gmt.getOffset()); + for (final ZonedDateTime zdt : new ZonedDateTime[] { bst, gmt }) { + final Calendar date = GregorianCalendar.from(zdt); + final Calendar original = (Calendar) date.clone(); + final ZonedDateTime second = zdt.truncatedTo(ChronoUnit.SECONDS); + final ZonedDateTime minute = zdt.truncatedTo(ChronoUnit.MINUTES); + final ZonedDateTime hour = zdt.truncatedTo(ChronoUnit.HOURS); + assertEquals(Date.from(zdt.truncatedTo(ChronoUnit.SECONDS).plusSeconds(1).toInstant()), DateUtils.ceiling(date, Calendar.SECOND).getTime(), "Ceiling Calendar.SECOND " + zdt); + assertEquals(Date.from(second.plusSeconds(milliseconds >= 500 ? 1 : 0).toInstant()), DateUtils.round(date, Calendar.SECOND).getTime(), + "Round Calendar.SECOND " + zdt); + assertEquals(Date.from(minute.plusMinutes(1).toInstant()), DateUtils.ceiling(date, Calendar.MINUTE).getTime(), "Ceiling Calendar.MINUTE " + zdt); + assertEquals(Date.from(minute.plusMinutes(1).toInstant()), DateUtils.round(date, Calendar.MINUTE).getTime(), "Round Calendar.MINUTE " + zdt); + assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.ceiling(date, Calendar.HOUR_OF_DAY).getTime(), "Ceiling Calendar.HOUR_OF_DAY " + zdt); + assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.ceiling(date, Calendar.HOUR).getTime(), "Ceiling Calendar.HOUR " + zdt); + assertEquals(Date.from(hour.toInstant()), DateUtils.round(date, Calendar.HOUR_OF_DAY).getTime(), "Round Calendar.HOUR_OF_DAY " + zdt); + // 38 minutes past rounds up to the next hour + final Calendar late = GregorianCalendar.from(zdt.withMinute(38)); + assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.round(late, Calendar.HOUR_OF_DAY).getTime(), "Round up Calendar.HOUR_OF_DAY " + zdt); + assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.round(late, Calendar.HOUR).getTime(), "Round up Calendar.HOUR " + zdt); + assertEquals(zdt.getZone().getId(), DateUtils.ceiling(date, Calendar.HOUR_OF_DAY).getTimeZone().getID()); + assertEquals(original, date, "Input calendar must remain unchanged"); + } + } + @Test void testConstructor() { assertNotNull(new DateUtils()); @@ -1808,77 +1879,6 @@ void testTruncateLang59() { assertEquals(oct31MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.DATE), "Round Calendar.DATE"); } - /** - * Tests that clearing sub-hour fields does not spuriously select the later occurrence of an overlapping local time. - * - * @param milliseconds the millisecond value below, at, or above the rounding threshold. - */ - @DefaultTimeZone("Europe/London") - @ParameterizedTest - @ValueSource(ints = { 245, 499, 500, 999 }) - void testCeilingAndRoundDstOverlap(final int milliseconds) { - // 2024-10-27 01:00-02:00 BST repeats as 01:00-02:00 GMT; ZonedDateTime.of() picks the earlier offset, BST. - final ZonedDateTime bst = ZonedDateTime.of(2024, 10, 27, 1, 8, 58, milliseconds * 1_000_000, ZoneId.of("Europe/London")); - assertEquals(ZoneOffset.ofHours(1), bst.getOffset()); - final ZonedDateTime gmt = bst.withLaterOffsetAtOverlap(); - assertEquals(ZoneOffset.UTC, gmt.getOffset()); - for (final ZonedDateTime zdt : new ZonedDateTime[] { bst, gmt }) { - final Date date = Date.from(zdt.toInstant()); - final ZonedDateTime second = zdt.truncatedTo(ChronoUnit.SECONDS); - final ZonedDateTime minute = zdt.truncatedTo(ChronoUnit.MINUTES); - final ZonedDateTime hour = zdt.truncatedTo(ChronoUnit.HOURS); - assertEquals(Date.from(zdt.truncatedTo(ChronoUnit.SECONDS).plusSeconds(1).toInstant()), DateUtils.ceiling(date, Calendar.SECOND), "Ceiling Calendar.SECOND " + zdt); - assertEquals(Date.from(second.plusSeconds(milliseconds >= 500 ? 1 : 0).toInstant()), DateUtils.round(date, Calendar.SECOND), - "Round Calendar.SECOND " + zdt); - assertEquals(Date.from(minute.plusMinutes(1).toInstant()), DateUtils.ceiling(date, Calendar.MINUTE), "Ceiling Calendar.MINUTE " + zdt); - assertEquals(Date.from(minute.plusMinutes(1).toInstant()), DateUtils.round(date, Calendar.MINUTE), "Round Calendar.MINUTE " + zdt); - assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.ceiling(date, Calendar.HOUR_OF_DAY), "Ceiling Calendar.HOUR_OF_DAY " + zdt); - assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.ceiling(date, Calendar.HOUR), "Ceiling Calendar.HOUR " + zdt); - assertEquals(Date.from(hour.toInstant()), DateUtils.round(date, Calendar.HOUR_OF_DAY), "Round Calendar.HOUR_OF_DAY " + zdt); - // 38 minutes past rounds up to the next hour - final Date late = Date.from(zdt.withMinute(38).toInstant()); - assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.round(late, Calendar.HOUR_OF_DAY), "Round up Calendar.HOUR_OF_DAY " + zdt); - assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.round(late, Calendar.HOUR), "Round up Calendar.HOUR " + zdt); - } - } - - /** - * Tests that clearing sub-hour fields does not spuriously select the later occurrence of an overlapping local time. - * - * @param milliseconds the millisecond value below, at, or above the rounding threshold. - */ - @DefaultTimeZone("UTC") - @ParameterizedTest - @ValueSource(ints = { 245, 499, 500, 999 }) - void testCeilingAndRoundDstOverlapCalendar(final int milliseconds) { - // 2024-10-27 01:00-02:00 BST repeats as 01:00-02:00 GMT; ZonedDateTime.of() picks the earlier offset, BST. - final ZonedDateTime bst = ZonedDateTime.of(2024, 10, 27, 1, 8, 58, milliseconds * 1_000_000, ZoneId.of("Europe/London")); - assertEquals(ZoneOffset.ofHours(1), bst.getOffset()); - final ZonedDateTime gmt = bst.withLaterOffsetAtOverlap(); - assertEquals(ZoneOffset.UTC, gmt.getOffset()); - for (final ZonedDateTime zdt : new ZonedDateTime[] { bst, gmt }) { - final Calendar date = GregorianCalendar.from(zdt); - final Calendar original = (Calendar) date.clone(); - final ZonedDateTime second = zdt.truncatedTo(ChronoUnit.SECONDS); - final ZonedDateTime minute = zdt.truncatedTo(ChronoUnit.MINUTES); - final ZonedDateTime hour = zdt.truncatedTo(ChronoUnit.HOURS); - assertEquals(Date.from(zdt.truncatedTo(ChronoUnit.SECONDS).plusSeconds(1).toInstant()), DateUtils.ceiling(date, Calendar.SECOND).getTime(), "Ceiling Calendar.SECOND " + zdt); - assertEquals(Date.from(second.plusSeconds(milliseconds >= 500 ? 1 : 0).toInstant()), DateUtils.round(date, Calendar.SECOND).getTime(), - "Round Calendar.SECOND " + zdt); - assertEquals(Date.from(minute.plusMinutes(1).toInstant()), DateUtils.ceiling(date, Calendar.MINUTE).getTime(), "Ceiling Calendar.MINUTE " + zdt); - assertEquals(Date.from(minute.plusMinutes(1).toInstant()), DateUtils.round(date, Calendar.MINUTE).getTime(), "Round Calendar.MINUTE " + zdt); - assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.ceiling(date, Calendar.HOUR_OF_DAY).getTime(), "Ceiling Calendar.HOUR_OF_DAY " + zdt); - assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.ceiling(date, Calendar.HOUR).getTime(), "Ceiling Calendar.HOUR " + zdt); - assertEquals(Date.from(hour.toInstant()), DateUtils.round(date, Calendar.HOUR_OF_DAY).getTime(), "Round Calendar.HOUR_OF_DAY " + zdt); - // 38 minutes past rounds up to the next hour - final Calendar late = GregorianCalendar.from(zdt.withMinute(38)); - assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.round(late, Calendar.HOUR_OF_DAY).getTime(), "Round up Calendar.HOUR_OF_DAY " + zdt); - assertEquals(Date.from(hour.plusHours(1).toInstant()), DateUtils.round(late, Calendar.HOUR).getTime(), "Round up Calendar.HOUR " + zdt); - assertEquals(zdt.getZone().getId(), DateUtils.ceiling(date, Calendar.HOUR_OF_DAY).getTimeZone().getID()); - assertEquals(original, date, "Input calendar must remain unchanged"); - } - } - /** * Tests the calendar iterator for week ranges */ diff --git a/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java b/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java index 87b1aaaec..d27a53a43 100644 --- a/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java +++ b/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java @@ -167,6 +167,28 @@ static Stream<Arguments> dateParserParameters() { ); } + private static Calendar initializeCalendar(final TimeZone timeZone) { + final Calendar cal = Calendar.getInstance(timeZone); + cal.set(Calendar.YEAR, 2001); + cal.set(Calendar.MONTH, 1); // not daylight savings + cal.set(Calendar.DAY_OF_MONTH, 4); + cal.set(Calendar.HOUR_OF_DAY, 12); + cal.set(Calendar.MINUTE, 8); + cal.set(Calendar.SECOND, 56); + cal.set(Calendar.MILLISECOND, 235); + return cal; + } + + static ArgumentSets testParsesFactory() { + // @formatter:off + return ArgumentSets + .argumentsForFirstParameter(LONG_FORMAT, SHORT_FORMAT) + .argumentsForNextParameter(LocaleUtils.availableLocaleList()) + .argumentsForNextParameter(NEW_YORK, REYKJAVIK, TimeZones.GMT) + .argumentsForNextParameter(2003, 1940, 1868, 1867, 1, -1, -1940); + // @formatter:on + } + static Stream<Arguments> weekYearParsingOutOfRangeDayOfWeekParameters() { final String[][] cases = { { "YYYY-'W'ww-u", "2025-W01-8" }, @@ -190,28 +212,6 @@ static Stream<Arguments> weekYearParsingStrictDayOfWeekParameters() { .flatMap(locale -> Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 123, Integer.MAX_VALUE).map(dayOfWeek -> Arguments.of(dayOfWeek, locale))); } - private static Calendar initializeCalendar(final TimeZone timeZone) { - final Calendar cal = Calendar.getInstance(timeZone); - cal.set(Calendar.YEAR, 2001); - cal.set(Calendar.MONTH, 1); // not daylight savings - cal.set(Calendar.DAY_OF_MONTH, 4); - cal.set(Calendar.HOUR_OF_DAY, 12); - cal.set(Calendar.MINUTE, 8); - cal.set(Calendar.SECOND, 56); - cal.set(Calendar.MILLISECOND, 235); - return cal; - } - - static ArgumentSets testParsesFactory() { - // @formatter:off - return ArgumentSets - .argumentsForFirstParameter(LONG_FORMAT, SHORT_FORMAT) - .argumentsForNextParameter(LocaleUtils.availableLocaleList()) - .argumentsForNextParameter(NEW_YORK, REYKJAVIK, TimeZones.GMT) - .argumentsForNextParameter(2003, 1940, 1868, 1867, 1, -1, -1940); - // @formatter:on - } - private final TriFunction<String, TimeZone, Locale, DateParser> dateParserProvider = (format, timeZone, locale) -> new FastDateParser(format, timeZone, locale, null);
