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)
*/