This is an automated email from the ASF dual-hosted git repository.

sebbASF pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-validator.git


The following commit(s) were added to refs/heads/master by this push:
     new 7f1bae0a compare week-year not calendar year in compareWeeks (#408)
7f1bae0a is described below

commit 7f1bae0a98d00152ca1dc07ec1b9f9292da30a3c
Author: sahvx655-wq <[email protected]>
AuthorDate: Sat Jun 27 16:15:45 2026 +0530

    compare week-year not calendar year in compareWeeks (#408)
---
 .../routines/AbstractCalendarValidator.java        | 43 +++++++++++++++-----
 .../validator/routines/CalendarValidatorTest.java  | 46 ++++++++++++++++++++++
 2 files changed, 79 insertions(+), 10 deletions(-)

diff --git 
a/src/main/java/org/apache/commons/validator/routines/AbstractCalendarValidator.java
 
b/src/main/java/org/apache/commons/validator/routines/AbstractCalendarValidator.java
index a2b2e9a2..be88d07b 100644
--- 
a/src/main/java/org/apache/commons/validator/routines/AbstractCalendarValidator.java
+++ 
b/src/main/java/org/apache/commons/validator/routines/AbstractCalendarValidator.java
@@ -23,6 +23,7 @@ import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Locale;
 import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.commons.validator.GenericValidator;
 
@@ -38,6 +39,9 @@ public abstract class AbstractCalendarValidator extends 
AbstractFormatValidator
 
     private static final long serialVersionUID = -1410008585975827379L;
 
+    /** Number of milliseconds in a week, beyond which two instants cannot 
share a week. */
+    private static final long MILLIS_PER_WEEK = TimeUnit.DAYS.toMillis(7);
+
     /**
      * The date style to use for Locale validation.
      */
@@ -117,17 +121,20 @@ public abstract class AbstractCalendarValidator extends 
AbstractFormatValidator
 
         int result;
 
+        // Week of Year and Week of Month numbers repeat across the boundaries 
they reset on, and a
+        // week can belong to a different calendar year or month than its 
number suggests (for
+        // example 31 December may fall in week 1 of the following year), so 
the week is compared by
+        // day distance and week number rather than by comparing the calendar 
year first.
+        if (field == Calendar.WEEK_OF_YEAR || field == Calendar.WEEK_OF_MONTH) 
{
+            return compareWeek(value, compare, field);
+        }
+
         // Compare Year
         result = calculateCompareResult(value, compare, Calendar.YEAR);
         if (result != 0 || field == Calendar.YEAR) {
             return result;
         }
 
-        // Compare Week of Year
-        if (field == Calendar.WEEK_OF_YEAR) {
-            return calculateCompareResult(value, compare, 
Calendar.WEEK_OF_YEAR);
-        }
-
         // Compare Day of the Year
         if (field == Calendar.DAY_OF_YEAR) {
             return calculateCompareResult(value, compare, 
Calendar.DAY_OF_YEAR);
@@ -139,11 +146,6 @@ public abstract class AbstractCalendarValidator extends 
AbstractFormatValidator
             return result;
         }
 
-        // Compare Week of Month
-        if (field == Calendar.WEEK_OF_MONTH) {
-            return calculateCompareResult(value, compare, 
Calendar.WEEK_OF_MONTH);
-        }
-
         // Compare Date
         result = calculateCompareResult(value, compare, Calendar.DATE);
         if (result != 0 || field == Calendar.DATE ||
@@ -416,4 +418,25 @@ public abstract class AbstractCalendarValidator extends 
AbstractFormatValidator
      */
     @Override
     protected abstract Object processParsedValue(Object value, Format 
formatter);
+
+    /**
+     * Compares the week two calendars fall in, ordering by the actual week 
rather than by the
+     * {@code WEEK_OF_YEAR} or {@code WEEK_OF_MONTH} number alone. Those 
numbers repeat across the
+     * boundaries they reset on (for example 31 December may be week 1 of the 
following year, and
+     * the first week of a month can hold days carried over from the previous 
month), so the gap
+     * between the two instants is checked first: dates a week or more apart 
are always in different
+     * weeks, and nearer dates share a week only when the week number also 
matches.
+     *
+     * @param value The Calendar value.
+     * @param compare The {@link Calendar} to check the value against.
+     * @param field {@code Calendar.WEEK_OF_YEAR} or {@code 
Calendar.WEEK_OF_MONTH}.
+     * @return Zero if both calendars are in the same week, -1 or +1 otherwise.
+     */
+    private int compareWeek(final Calendar value, final Calendar compare, 
final int field) {
+        final long millis = value.getTimeInMillis() - 
compare.getTimeInMillis();
+        if (Math.abs(millis) >= MILLIS_PER_WEEK || 
calculateCompareResult(value, compare, field) != 0) {
+            return Long.signum(millis);
+        }
+        return 0;
+    }
 }
diff --git 
a/src/test/java/org/apache/commons/validator/routines/CalendarValidatorTest.java
 
b/src/test/java/org/apache/commons/validator/routines/CalendarValidatorTest.java
index 2d835aff..a28105ac 100644
--- 
a/src/test/java/org/apache/commons/validator/routines/CalendarValidatorTest.java
+++ 
b/src/test/java/org/apache/commons/validator/routines/CalendarValidatorTest.java
@@ -232,6 +232,52 @@ class CalendarValidatorTest extends 
AbstractCalendarValidatorTest {
         assertEquals("Invalid field: -1", e.getMessage(), "check message");
     }
 
+    /**
+     * Test compareWeeks() across the year boundary, where WEEK_OF_YEAR 
repeats: 31 December can be
+     * week 1 of the next year while 1 January of the same calendar year is 
also week 1.
+     */
+    @Test
+    @DefaultLocale(country = "US", language = "en")
+    void testCompareWeeksAcrossYearBoundary() {
+        final int noon = 120000;
+        // US locale: week starts Sunday, so Sun 30 Dec 2018 to Sat 5 Jan 2019 
is a single week
+        final Calendar dec30y2018 = createCalendar(TimeZones.GMT, 20181230, 
noon);
+        final Calendar dec31y2018 = createCalendar(TimeZones.GMT, 20181231, 
noon);
+        final Calendar jan01y2018 = createCalendar(TimeZones.GMT, 20180101, 
noon);
+        final Calendar jan01y2019 = createCalendar(TimeZones.GMT, 20190101, 
noon);
+        final Calendar jan05y2019 = createCalendar(TimeZones.GMT, 20190105, 
noon);
+
+        // both are calendar year 2018 and WEEK_OF_YEAR 1, but lie ~52 weeks 
apart
+        assertEquals(1, calValidator.compareWeeks(dec31y2018, jan01y2018), 
"Dec 31 2018 is weeks after Jan 1 2018");
+        assertEquals(-1, calValidator.compareWeeks(jan01y2018, dec31y2018), 
"Jan 1 2018 is weeks before Dec 31 2018");
+
+        // different calendar years but the same week
+        assertEquals(0, calValidator.compareWeeks(dec31y2018, jan01y2019), 
"Dec 31 2018 is the same week as Jan 1 2019");
+
+        // the whole Sun 30 Dec 2018 to Sat 5 Jan 2019 span is one week
+        assertEquals(0, calValidator.compareWeeks(dec30y2018, jan05y2019), 
"Dec 30 2018 is the same week as Jan 5 2019");
+    }
+
+    /**
+     * Test WEEK_OF_MONTH comparison across a month/year boundary, where the 
week number resets and
+     * the first week of a month can hold days carried over from the previous 
month.
+     */
+    @Test
+    @DefaultLocale(country = "US", language = "en")
+    void testCompareWeekOfMonthAcrossBoundary() {
+        final int noon = 120000;
+        final Calendar dec30y2018 = createCalendar(TimeZones.GMT, 20181230, 
noon);
+        final Calendar dec31y2018 = createCalendar(TimeZones.GMT, 20181231, 
noon);
+        final Calendar jan01y2019 = createCalendar(TimeZones.GMT, 20190101, 
noon);
+
+        // 30 and 31 Dec 2018 fall in the same week of December
+        assertEquals(0, calValidator.compare(dec30y2018, dec31y2018, 
Calendar.WEEK_OF_MONTH), "Dec 30 and 31 2018 are the same week of month");
+
+        // 31 Dec 2018 and 1 Jan 2019 are one day apart but lie in different 
weeks of their months
+        assertEquals(-1, calValidator.compare(dec31y2018, jan01y2019, 
Calendar.WEEK_OF_MONTH), "Dec 31 2018 is before Jan 1 2019 by week of month");
+        assertEquals(1, calValidator.compare(jan01y2019, dec31y2018, 
Calendar.WEEK_OF_MONTH), "Jan 1 2019 is after Dec 31 2018 by week of month");
+    }
+
     /**
      * Test Date/Time style Validator (there isn't an implementation for this)
      */

Reply via email to